1 // --------------------------------------------------------------------------
4 // Markus Wittmann, 2016-2017
5 // RRZE, University of Erlangen-Nuremberg, Germany
6 // markus.wittmann -at- fau.de or hpc -at- rrze.fau.de
9 // LSS, University of Erlangen-Nuremberg, Germany
11 // This file is part of the Lattice Boltzmann Benchmark Kernels (LbmBenchKernels).
13 // LbmBenchKernels is free software: you can redistribute it and/or modify
14 // it under the terms of the GNU General Public License as published by
15 // the Free Software Foundation, either version 3 of the License, or
16 // (at your option) any later version.
18 // LbmBenchKernels is distributed in the hope that it will be useful,
19 // but WITHOUT ANY WARRANTY; without even the implied warranty of
20 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 // GNU General Public License for more details.
23 // You should have received a copy of the GNU General Public License
24 // along with LbmBenchKernels. If not, see <http://www.gnu.org/licenses/>.
26 // --------------------------------------------------------------------------
40 // -----------------------------------------------------------------------
42 // Binds the calling thread to specified core.
44 // Return value: 0 = success, else error.
46 // -----------------------------------------------------------------------
48 int PinCurrentThreadToCore(int coreNumber)
54 CPU_SET(coreNumber, &cpu_set);
56 error = sched_setaffinity((pid_t)0, sizeof(cpu_set_t), &cpu_set);
59 Error("pinning thread to core %d failed (%d): %s\n",
60 coreNumber, error, strerror(error));
67 // -----------------------------------------------------------------------
69 // Binds the calling thread to specified core by a cpu list specified
70 // in the given environment variable.
72 // Return value: 0 = success, else error.
74 // -----------------------------------------------------------------------
76 int PinCurrentThreadByEnvVar(const char * envVarName,
77 int mpiRank, int nodeRank, int threadNumber)
79 const char * envVarValue;
82 envVarValue = getenv(envVarName);
84 if (envVarValue == NULL) {
86 Print("skip pinning: env var %s not set\n", envVarName);
92 core = PinParseCpuList(envVarValue, mpiRank, nodeRank, threadNumber);
98 return PinCurrentThreadToCore(core);
102 // -----------------------------------------------------------------------
104 // Binds the calling thread to a core specified in the CPU list.
106 // Return value: 0 = success, else error.
108 // -----------------------------------------------------------------------
110 int PinCurrentThreadByCpuList(const char * cpuList,
111 int mpiRank, int nodeRank, int threadNumber)
115 if (cpuList == NULL) {
117 printf("ERROR: cpu list is NULL.\n");
123 core = PinParseCpuList(cpuList, mpiRank, nodeRank, threadNumber);
129 return PinCurrentThreadToCore(core);
133 // -----------------------------------------------------------------------
135 // Parses the provided cpu list and returns the core number for the
136 // specified MPI rank, local rank, and thread.
138 // The cpu list has for example a format of: 0,1,2 or 0,1,2_3,4,5
140 // Blocks (0,1,2 or 3,4,5) separated by "_" specify pinning inside a
141 // node rank. The first block maps to node rank 1, the second to node
144 // Inside a block the core numbers specify where the threads should
145 // be pinned to. They are separated by "," and the first number maps
146 // to the first core, the second number to the second core, etc.
148 // For example: 0,2,4_6,8,10
150 // Node rank 0 thread 0 pinned to core 0
157 // -----------------------------------------------------------------------
159 int PinParseCpuList(const char * cpuList,
160 int mpiRank, int nodeRank, int threadNumber)
164 if (cpuList == NULL) {
168 const char * c = cpuList;
170 // Ensure only valid characters are in the cpu list.
171 // Cpu list is in the format of "0,1,2_3,4,5".
172 while (((*c >= '0' && *c <= '9') || *c == ',' || *c == '_')) {
177 // Invalid character detected.
185 // Move variable c after the "nodeRank"th "_" in the cpu list.
186 while (i < nodeRank && *c != 0x00) {
191 if (i != nodeRank || *c < '0' || *c > '9') {
192 // Cpu list for this node rank not found.
196 // Now find the core for the specified thread.
200 while (t < threadNumber && *c != 0x00) {
204 else if (*c == '_') {
205 // Unexpected character at this position.
212 if (t != threadNumber || *c < '0' || *c > '9') {
213 // Cpu for this threadNumber not found.
224 // -----------------------------------------------------------------------
226 // Returns the first core from the calling thread's affinity set.
228 // On error a value < 0 is returned.
230 // -----------------------------------------------------------------------
240 err = sched_getaffinity((pid_t)0, sizeof(cpu_set_t), &cpu_set);
242 // constant CPU_SETSIZE is one larger than the maximum CPU
243 // number that can be stored in a CPU set
244 for (int i = 0; i < CPU_SETSIZE; ++i) {
245 if (CPU_ISSET(i, &cpu_set)) {
252 Error("getting thread affinty failed (%d): %s\n", err, strerror(err));
261 // -----------------------------------------------------------------------
263 // Returns the all cores from the calling thread's affinity set.
265 // On error a value < 0 is returned.
267 // -----------------------------------------------------------------------
269 typedef cpu_set_t CpuSet;
272 static CpuSet PinCurrentCores()
280 err = sched_getaffinity((pid_t)0, sizeof(cpu_set_t), &cpu_set);
285 Error("getting thread affinty failed (%d): %s\n", err, strerror(err));
292 static char * CpuSetToString(cpu_set_t * cpu_set)
294 int previousSetCore = -2;
295 int rangeBeginCore = -2;
297 char * buffer1 = (char *)malloc(1024);
298 Assert(buffer1 != NULL);
299 char * buffer2 = (char *)malloc(1024);
300 Assert(buffer2 != NULL);
305 char * buffer = buffer1;
306 char * bufferOld = buffer2;
308 const char * empty = "";
309 const char * realComma = ",";
310 const char * comma = empty;
312 // TODO: use snprintf
313 // TODO: increase allocated buffer if necessary
315 for (int i = 0; i < CPU_SETSIZE; ++i) {
316 if (!CPU_ISSET(i, cpu_set)) {
320 if (i == previousSetCore + 1) {
325 // Now we reached the end of a range.
326 // The range can also consist of only one core.
327 // Be aware, that this core is not part of the range.
329 // TODO: this code is repeated below -> use it only once
330 if (rangeBeginCore >= 0 && previousSetCore >= 0) {
337 if (rangeBeginCore < previousSetCore) {
338 sprintf(buffer, "%s%s%d-%d", bufferOld, comma, rangeBeginCore, previousSetCore);
341 sprintf(buffer, "%s%s%d", bufferOld, comma, previousSetCore);
347 // With this core a new range begins.
352 if (rangeBeginCore >= 0 && previousSetCore >= 0) {
359 if (rangeBeginCore < previousSetCore) {
360 sprintf(buffer, "%s%s%d-%d", bufferOld, comma, rangeBeginCore, previousSetCore);
363 sprintf(buffer, "%s%s%d", bufferOld, comma, previousSetCore);
367 free(bufferOld); bufferOld = NULL;
372 char * PinCpuListAsString()
374 CpuSet cpuSet = PinCurrentCores();
376 return CpuSetToString(&cpuSet);
381 int main(int argc, char * argv[])
383 char * cpuList = PinCpuListAsString();
385 printf("pinned to cores: %s\n", cpuList);
387 free(cpuList); cpuList = NULL;