Commit | Line | Data |
---|---|---|
10988083 MW |
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 | ||
10988083 MW |
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 | ||
e3f82424 | 73 | int PinCurrentThreadByEnvVar(const char * envVarName, int threadNumber) |
10988083 MW |
74 | { |
75 | const char * envVarValue; | |
76 | int core; | |
77 | ||
78 | envVarValue = getenv(envVarName); | |
79 | ||
80 | if (envVarValue == NULL) { | |
e3f82424 | 81 | printf("WARNING: skip pinning: env var %s not set\n", envVarName); |
10988083 MW |
82 | |
83 | return 0; | |
84 | } | |
85 | ||
e3f82424 | 86 | core = PinParseCpuList(envVarValue, threadNumber); |
10988083 MW |
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 | ||
e3f82424 | 104 | int PinCurrentThreadByCpuList(const char * cpuList, int threadNumber) |
10988083 MW |
105 | { |
106 | int core; | |
107 | ||
108 | if (cpuList == NULL) { | |
e3f82424 | 109 | printf("ERROR: cpu list is NULL.\n"); |
10988083 MW |
110 | |
111 | exit(1); | |
112 | } | |
113 | ||
e3f82424 | 114 | core = PinParseCpuList(cpuList, threadNumber); |
10988083 MW |
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 | |
e3f82424 | 127 | // specified thread. |
10988083 | 128 | // |
e3f82424 | 129 | // The cpu list has for example a format of: 0,1,2 |
10988083 MW |
130 | // |
131 | // ----------------------------------------------------------------------- | |
132 | ||
e3f82424 | 133 | int PinParseCpuList(const char * cpuList, int threadNumber) |
10988083 MW |
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. | |
e3f82424 MW |
144 | // Cpu list is in the format of "0,1,2,3,4,5". |
145 | while (((*c >= '0' && *c <= '9') || *c == ',')) { | |
10988083 MW |
146 | ++c; |
147 | } | |
148 | ||
149 | if (*c != 0x00) { | |
150 | // Invalid character detected. | |
151 | return -2; | |
152 | } | |
153 | ||
154 | c = cpuList; | |
155 | ||
10988083 MW |
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 | } | |
10988083 MW |
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 |