add citation information
[LbmBenchmarkKernelsPublic.git] / src / Pinning.c
1 // --------------------------------------------------------------------------
2 //
3 // Copyright
4 //   Markus Wittmann, 2016-2017
5 //   RRZE, University of Erlangen-Nuremberg, Germany
6 //   markus.wittmann -at- fau.de or hpc -at- rrze.fau.de
7 //
8 //   Viktor Haag, 2016
9 //   LSS, University of Erlangen-Nuremberg, Germany
10 //
11 //  This file is part of the Lattice Boltzmann Benchmark Kernels (LbmBenchKernels).
12 //
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.
17 //
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.
22 //
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/>.
25 //
26 // --------------------------------------------------------------------------
27 #ifndef _GNU_SOURCE
28         #define _GNU_SOURCE
29 #endif
30 #include <sched.h>
31 #include <errno.h>
32
33
34 #include "Base.h"
35 #include "Pinning.h"
36
37 // -----------------------------------------------------------------------
38 //
39 // Binds the calling thread to specified core.
40 //
41 // Return value: 0 = success, else error.
42 //
43 // -----------------------------------------------------------------------
44
45 int PinCurrentThreadToCore(int coreNumber)
46 {
47         int error = 0;
48
49         cpu_set_t cpu_set;
50         CPU_ZERO(&cpu_set);
51         CPU_SET(coreNumber, &cpu_set);
52
53         error = sched_setaffinity((pid_t)0, sizeof(cpu_set_t), &cpu_set);
54
55         if (error != 0) {
56                 Error("pinning thread to core %d failed (%d): %s\n",
57                                 coreNumber, error, strerror(error));
58         }
59
60         return error;
61 }
62
63
64 // -----------------------------------------------------------------------
65 //
66 // Binds the calling thread to specified core by a cpu list specified
67 // in the given environment variable.
68 //
69 // Return value: 0 = success, else error.
70 //
71 // -----------------------------------------------------------------------
72
73 int PinCurrentThreadByEnvVar(const char * envVarName, int threadNumber)
74 {
75         const char * envVarValue;
76         int core;
77
78         envVarValue = getenv(envVarName);
79
80         if (envVarValue == NULL) {
81                 printf("WARNING: skip pinning: env var %s not set\n", envVarName);
82
83                 return 0;
84         }
85
86         core = PinParseCpuList(envVarValue, threadNumber);
87
88         if (core < 0) {
89                 return core;
90         }
91
92         return PinCurrentThreadToCore(core);
93 }
94
95
96 // -----------------------------------------------------------------------
97 //
98 // Binds the calling thread to a core specified in the CPU list.
99 //
100 // Return value: 0 = success, else error.
101 //
102 // -----------------------------------------------------------------------
103
104 int PinCurrentThreadByCpuList(const char * cpuList, int threadNumber)
105 {
106         int core;
107
108         if (cpuList == NULL) {
109                 printf("ERROR: cpu list is NULL.\n");
110
111                 exit(1);
112         }
113
114         core = PinParseCpuList(cpuList, threadNumber);
115
116         if (core < 0) {
117                 return core;
118         }
119
120         return PinCurrentThreadToCore(core);
121 }
122
123
124 // -----------------------------------------------------------------------
125 //
126 // Parses the provided cpu list and returns the core number for the
127 // specified thread.
128 //
129 // The cpu list has for example a format of: 0,1,2
130 //
131 // -----------------------------------------------------------------------
132
133 int PinParseCpuList(const char * cpuList, int threadNumber)
134 {
135         int cpu = -1;
136
137         if (cpuList == NULL) {
138                 return -1;
139         }
140
141         const char * c = cpuList;
142
143         // Ensure only valid characters are in the cpu list.
144         // Cpu list is in the format of "0,1,2,3,4,5".
145         while (((*c >= '0' && *c <= '9') || *c == ',')) {
146                 ++c;
147         }
148
149         if (*c != 0x00) {
150         // Invalid character detected.
151                 return -2;
152         }
153
154         c = cpuList;
155
156         // Now find the core for the specified thread.
157
158         int t = 0;
159
160         while (t < threadNumber && *c != 0x00) {
161                 if (*c == ',') {
162                         ++t;
163                 }
164                 ++c;
165         }
166
167         if (t != threadNumber || *c < '0' || *c > '9') {
168                 // Cpu for this threadNumber not found.
169                 return -4;
170         }
171
172         cpu = atoi(c);
173
174         return cpu;
175 }
176
177
178
179 // -----------------------------------------------------------------------
180 //
181 // Returns the first core from the calling thread's affinity set.
182 //
183 // On error a value < 0 is returned.
184 //
185 // -----------------------------------------------------------------------
186
187 int PinCurrentCore()
188 {
189         int core = -1;
190         int err;
191
192         cpu_set_t cpu_set;
193         CPU_ZERO(&cpu_set);
194
195         err = sched_getaffinity((pid_t)0, sizeof(cpu_set_t), &cpu_set);
196
197         // constant CPU_SETSIZE is one larger than the maximum CPU
198         // number that can be stored in a CPU set
199         for (int i = 0; i < CPU_SETSIZE; ++i) {
200                 if (CPU_ISSET(i, &cpu_set)) {
201                         core = i;
202                         break;
203                 }
204         }
205
206         if (err != 0) {
207                 Error("getting thread affinty failed (%d): %s\n", err, strerror(err));
208                 return -1;
209         }
210
211         return core;
212 }
213
214
215
216 // -----------------------------------------------------------------------
217 //
218 // Returns the all cores from the calling thread's affinity set.
219 //
220 // On error a value < 0 is returned.
221 //
222 // -----------------------------------------------------------------------
223
224 typedef cpu_set_t CpuSet;
225
226
227 static CpuSet PinCurrentCores()
228 {
229         CpuSet cpuSet;
230         int err;
231
232         cpu_set_t cpu_set;
233         CPU_ZERO(&cpu_set);
234
235         err = sched_getaffinity((pid_t)0, sizeof(cpu_set_t), &cpu_set);
236
237         cpuSet = cpu_set;
238
239         if (err != 0) {
240                 Error("getting thread affinty failed (%d): %s\n", err, strerror(err));
241                 return cpuSet;
242         }
243
244         return cpuSet;
245 }
246
247 static char * CpuSetToString(cpu_set_t * cpu_set)
248 {
249         int previousSetCore = -2;
250         int rangeBeginCore = -2;
251
252         char * buffer1 = (char *)malloc(1024);
253         Assert(buffer1 != NULL);
254         char * buffer2 = (char *)malloc(1024);
255         Assert(buffer2 != NULL);
256
257         buffer1[0] = 0x00;
258         buffer2[0] = 0x00;
259
260         char * buffer = buffer1;
261         char * bufferOld = buffer2;
262
263         const char * empty = "";
264         const char * realComma = ",";
265         const char * comma = empty;
266
267         // TODO: use snprintf
268         // TODO: increase allocated buffer if necessary
269
270         for (int i = 0; i < CPU_SETSIZE; ++i) {
271                 if (!CPU_ISSET(i, cpu_set)) {
272                         continue;
273                 }
274
275                 if (i == previousSetCore + 1) {
276                         previousSetCore = i;
277                         continue;
278                 }
279
280                 // Now we reached the end of a range.
281                 // The range can also consist of only one core.
282                 // Be aware, that this core is not part of the range.
283
284                 // TODO: this code is repeated below -> use it only once
285                 if (rangeBeginCore >= 0 && previousSetCore >= 0) {
286                         char * tmp;
287
288                         tmp = buffer;
289                         buffer = bufferOld;
290                         bufferOld = tmp;
291
292                         if (rangeBeginCore < previousSetCore) {
293                                 sprintf(buffer, "%s%s%d-%d", bufferOld, comma, rangeBeginCore, previousSetCore);
294                         }
295                         else {
296                                 sprintf(buffer, "%s%s%d", bufferOld, comma, previousSetCore);
297                         }
298
299                         comma = realComma;
300                 }
301
302                 // With this core a new range begins.
303                 rangeBeginCore = i;
304                 previousSetCore = i;
305         }
306
307         if (rangeBeginCore >= 0 && previousSetCore >= 0) {
308                 char * tmp;
309
310                 tmp = buffer;
311                 buffer = bufferOld;
312                 bufferOld = tmp;
313
314                 if (rangeBeginCore < previousSetCore) {
315                         sprintf(buffer, "%s%s%d-%d", bufferOld, comma, rangeBeginCore, previousSetCore);
316                 }
317                 else {
318                         sprintf(buffer, "%s%s%d", bufferOld, comma, previousSetCore);
319                 }
320         }
321
322         free(bufferOld); bufferOld = NULL;
323
324         return buffer;
325 }
326
327 char * PinCpuListAsString()
328 {
329         CpuSet cpuSet = PinCurrentCores();
330
331         return CpuSetToString(&cpuSet);
332 }
333
334 #ifdef TEST
335
336 int main(int argc, char * argv[])
337 {
338         char * cpuList = PinCpuListAsString();
339
340         printf("pinned to cores: %s\n", cpuList);
341
342         free(cpuList); cpuList = NULL;
343
344         return 0;
345 }
346
347 #endif // TEST
348
This page took 0.063361 seconds and 4 git commands to generate.