ARGOBOTS  66b1c39742507d8df30e8d28c54839b961a14814
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups
abtd_affinity.c
Go to the documentation of this file.
1 /* -*- Mode: C; c-basic-offset:4 ; indent-tabs-mode:nil ; -*- */
2 /*
3  * See COPYRIGHT in top-level directory.
4  */
5 
6 /*
7  * The Argobots affinity module parses the following grammar while it ignores
8  * all white spaces between tokens. Double-quoted symbols in the following are
9  * part of the syntax.
10  *
11  * <list> = <interval>
12  * | <list> "," <interval>
13  * <interval> = <es-id-list> ":" <num> ":" <stride>
14  * | <es-id-list> ":" <num>
15  * | <es-id-list>
16  * <es-id-list> = <id>
17  * | "{" <id-list> "}"
18  * <id-list> = <id-interval>
19  * | <id-list> "," <id-interval>
20  * <id-interval> = <id> ":" <num> ":" <stride>
21  * | <id> ":" <num>
22  * | <id>
23  * <id> = <integer>
24  * <stride> = <integer>
25  * <num> = <positive integer>
26  *
27  * An execution stream with rank n refers to the (n % N)th CPU ID list
28  * (<es-id-list>) of the whole list (<list>) that has N items. The execution
29  * stream will be scheduled on a core that has a CPU ID in its CPU ID list
30  * (<es-id-list>). If a CPU ID is smaller than zero or larger than the number
31  * of cores recognized by the system, a modulo of the number of cores is used.
32  *
33  * This grammar supports a pattern-based syntax. <id-interval> can represent
34  * multiple CPU IDs by specifying a base CPU ID (<id>), the number of CPU IDs
35  * (<num>), and a stride (<stride>) as follows:
36  * <id>, <id> + <stride>, ..., <id> + <stride> * (<num> - 1)
37  * <interval> also accepts a pattern-based syntax as follows:
38  * <es-id-list>,
39  * {<es-id-list>[0] + <stride>, <es-id-list>[1] + <stride>, ...}, ...
40  * {<es-id-list>[0] + <stride> * (<num> - 1),
41  * <es-id-list>[1] + <stride> * (<num> - 1), ...}
42  * Note that <num> and <stride> are set to 1 if they are omitted.
43  *
44  * Let us assume a 12-core machine (NOTE: "12-core" does not mean 12 physical
45  * cores but indicates 12 hardware threads on modern CPUs). Examples are as
46  * follows:
47  *
48  * Example 1: bind ES with rank n to a core that has CPU ID = n % 12
49  *
50  * | ES0 | ES1 | ES2 | ES3 | ES4 | ES5 | ES6 | ES7 | ES8 | ES9 | ES10| ES11|
51  * |CPU00|CPU01|CPU02|CPU03|CPU04|CPU05|CPU06|CPU07|CPU08|CPU09|CPU10|CPU11|
52  *
53  * (1.1) ABT_SET_AFFINITY="{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},{11}"
54  * This explicitly sets all the CPU IDs.
55  * (1.2) ABT_SET_AFFINITY="0,1,2,3,4,5,6,7,8,9,10,11"
56  * It is similar to (1.1), but it omits "{}" since "{id}" = "id".
57  * (1.3) ABT_SET_AFFINITY="{0}:12:1"
58  * This creates 12 CPU ID lists starting from {0}. The stride is specified
59  * as 1, so the created lists are {0}, {1}, {2}, ... {11}.
60  * (1.4) ABT_SET_AFFINITY="{0}:12"
61  * The default stride is 1, so it is the same as (1.3)
62  * (1.5) ABT_SET_AFFINITY="0:12"
63  * It omits "{}" in (1.4) since "{id}" = "id".
64  *
65  * Example 2: bind ES with rank n to a core that has CPU ID = (n + 6) % 12
66  *
67  * | ES6 | ES7 | ES8 | ES9 | ES10| ES11| ES0 | ES1 | ES2 | ES3 | ES4 | ES5 |
68  * |CPU00|CPU01|CPU02|CPU03|CPU04|CPU05|CPU06|CPU07|CPU08|CPU09|CPU10|CPU11|
69  *
70  * (2.1) ABT_SET_AFFINITY="{6},{7},{8},{9},{10},{11},{0},{1},{2},{3},{4},{5}"
71  * This explicitly sets all the CPU IDs.
72  * (2.2) ABT_SET_AFFINITY="6,7,8,9,10,11,0,1,2,3,4,5"
73  * It is similar to (2.1), but it omits "{}" since "{id}" = "id".
74  * (2.3) ABT_SET_AFFINITY="{6}:6:1,{0}:6:1"
75  * This first creates 6 CPU ID lists starting from {6} and then 6 lists
76  * starting from {0}. The stride is "1", so the created lists are
77  * {6}, ... {11}, {0} ... {5}.
78  * (2.4) ABT_SET_AFFINITY="{6}:6,{0}:6"
79  * The default stride is 1, so it is the same as (2.3)
80  * (2.5) ABT_SET_AFFINITY="6:6,0:6"
81  * It omits "{}" in (2.4) since "{id}" = "id".
82  * (2.6) ABT_SET_AFFINITY="6:12"
83  * Affinity setting wraps around with respect to the number of cores, so
84  * this works the same as (2.5) on a 12-core machine.
85  *
86  * Example 3: bind ES with rank n to core that has CPU ID = (n * 4) % 12
87  *
88  * | ES0 | | | | ES1 | | | | ES2 | | | |
89  * |CPU00|CPU01|CPU02|CPU03|CPU04|CPU05|CPU06|CPU07|CPU08|CPU09|CPU10|CPU11|
90  *
91  * (3.1) ABT_SET_AFFINITY="{0},{4},{8}"
92  * This explicitly sets all the CPU IDs.
93  * (3.2) ABT_SET_AFFINITY="0,4,8"
94  * It is similar to (3.1), but it omits "{}" since "{id}" = "id".
95  * (3.3) ABT_SET_AFFINITY="{0}:3:4"
96  * This creates 3 CPU ID lists starting from {0}. The stride is "4", so
97  * creates lists are {0}, {4}, {8}.
98  * (3.4) ABT_SET_AFFINITY="0:3:4"
99  * It omits "{}" in (3.3) since "{id}" = "id".
100  *
101  * Example 4: bind ES with rank n to core that has
102  * CPU ID = (n * 4 + (n % 12) / 3) % 12
103  *
104  * | ES0 | ES3 | ES6 | ES9 | ES1 | ES4 | ES7 | ES10| ES2 | ES5 | ES9 | ES11|
105  * |CPU00|CPU01|CPU02|CPU03|CPU04|CPU05|CPU06|CPU07|CPU08|CPU09|CPU10|CPU11|
106  *
107  * (4.1) ABT_SET_AFFINITY="{0},{4},{8},{1},{5},{9},{2},{6},{10},{3},{7},{11}"
108  * This explicitly sets all the CPU IDs.
109  * (4.2) ABT_SET_AFFINITY="0,4,8,1,5,9,2,6,10,3,7,11"
110  * It is similar to (4.1), but it omits "{}" since "{id}" = "id".
111  * (4.3) ABT_SET_AFFINITY="{0}:3:4,{1}:3:4,{2}:3:4,{3}:3:4"
112  * This creates 3 CPU ID lists ({0}, {4}, {8}), then ({1}, {5}, {9}),
113  * ({2}, {6}, {10}), and ({3}, {7}, {11}).
114  * (4.4) ABT_SET_AFFINITY="0:3:4,1:3:4,2:3:4,3:3:4"
115  * It omits "{}" in (4.3) since "{id}" = "id".
116  *
117  * Example 5: bind ES with rank n to cores that have
118  * (n * 4) % 12 <= CPU ID < (n * 4) % 12 + 4
119  *
120  * | ES0 | ES1 | ES2 |
121  * |CPU00|CPU01|CPU02|CPU03|CPU04|CPU05|CPU06|CPU07|CPU08|CPU09|CPU10|CPU11|
122  *
123  * (5.1) ABT_SET_AFFINITY="{0,1,2,3},{4,5,6,7},{8,9,10,11}"
124  * This explicitly sets all the CPU IDs.
125  * (5.2) ABT_SET_AFFINITY="{0,1,2,3}:3:4"
126  * This creates 3 CPU ID lists starting from {0,1,2,3}. The stride is "4",
127  * so the created lists are "{0,1,2,3}, {4,5,6,7}, {8,9,10,11}".
128  * Note that "{0,1,2,3}:3:1" (or "{0,1,2,3}:3") is wrong: they create
129  * "{0,1,2,3}, {2,3,4,5}, {3,4,5,6}" since the stride is 1.
130  * (5.3) ABT_SET_AFFINITY="{0:4:1}:3:4"
131  * "{0:4:1}" means a CPU ID list of 4 CPU IDs that starts from 0 with a
132  * stride 1, so it is the same as "{0,1,2,3}".
133  * (5.4) ABT_SET_AFFINITY="{0:4}:3:4"
134  * The default stride is 1.
135  *
136  * Example 6: bind ES with rank n to cores that have either of the following:
137  * CPU ID = (n * 2) % 6
138  * CPU ID = (n * 2) % 6 + 6
139  *
140  * | ES0 | ES1 | ES2 | ES3 | ES4 | ES5 |
141  * |CPU00|CPU06|CPU01|CPU07|CPU02|CPU08|CPU03|CPU09|CPU04|CPU10|CPU05|CPU11|
142  *
143  * (6.1) ABT_SET_AFFINITY="{0,6},{1,7},{2,8},{3,9},{4,10},{5,11}"
144  * This explicitly sets all the CPU IDs.
145  * (6.2) ABT_SET_AFFINITY="{0,6}:6:1"
146  * This creates 6 CPU ID lists starting from {0,6}. The stride is "1", so
147  * the created lists are "{0,6}, {1,7}, {2,8}, {3,9}, {4,10}, {5,11}".
148  * (6.3) ABT_SET_AFFINITY="{0,6}:6"
149  * The default stride is 1.
150  *
151  * Example 7: bind ESs to cores except for those that have CPU ID = 0 or 1
152  *
153  * | | ES0, ES1, ES2, ... |
154  * |CPU00|CPU01|CPU02|CPU03|CPU04|CPU05|CPU06|CPU07|CPU08|CPU09|CPU10|CPU11|
155  *
156  * (7.1) ABT_SET_AFFINITY="{2,3,4,5,6,7,8,9,10,11}"
157  * This explicitly sets all the CPU IDs.
158  * (7.2) ABT_SET_AFFINITY="{2:10:1}"
159  * "{2:10:1}" means a CPU ID list that has 10 CPU IDs starting from 2 with
160  * a stride 1, so it is the same as "{2,3,4,5,6,7,8,9,10,11}".
161  * (7.3) ABT_SET_AFFINITY="{2:10}"
162  * The default stride is 1.
163  */
164 
165 #include "abti.h"
166 #include <unistd.h>
167 
168 #ifdef HAVE_PTHREAD_SETAFFINITY_NP
169 #ifdef __FreeBSD__
170 
171 #include <sys/param.h>
172 #include <sys/cpuset.h>
173 #include <pthread_np.h>
174 typedef cpuset_t cpu_set_t;
175 
176 #else /* !__FreeBSD__ */
177 
178 #define _GNU_SOURCE
179 #include <sched.h>
180 
181 #endif
182 #endif /* HAVE_PTHREAD_SETAFFINITY_NP */
183 
184 typedef struct {
186  size_t num_cpusets;
189 
191 
192 static inline int int_rem(int a, unsigned int b)
193 {
194  /* Return x where a = n * b + x and 0 <= x < b */
195  /* Because of ambiguity in the C specification, it uses a branch to check if
196  * the result is positive. */
197  int ret = (a % b) + b;
198  return ret >= b ? (ret - b) : ret;
199 }
200 
201 ABTU_ret_err static int get_num_cores(pthread_t native_thread, int *p_num_cores)
202 {
203 #ifdef HAVE_PTHREAD_SETAFFINITY_NP
204  int i, num_cores = 0;
205  /* Check the number of available cores by counting set bits. */
206  cpu_set_t cpuset;
207  int ret = pthread_getaffinity_np(native_thread, sizeof(cpu_set_t), &cpuset);
208  if (ret)
209  return ABT_ERR_OTHER;
210  for (i = 0; i < CPU_SETSIZE; i++) {
211  if (CPU_ISSET(i, &cpuset)) {
212  num_cores++;
213  }
214  }
215  *p_num_cores = num_cores;
216  return ABT_SUCCESS;
217 #else
218  return ABT_ERR_FEATURE_NA;
219 #endif
220 }
221 
222 ABTU_ret_err static int read_cpuset(pthread_t native_thread,
223  ABTD_affinity_cpuset *p_cpuset)
224 {
225 #ifdef HAVE_PTHREAD_SETAFFINITY_NP
226  cpu_set_t cpuset;
227  int ret = pthread_getaffinity_np(native_thread, sizeof(cpu_set_t), &cpuset);
228  if (ret)
229  return ABT_ERR_OTHER;
230  int i, j, num_cpuids = 0;
231  for (i = 0; i < CPU_SETSIZE; i++) {
232  if (CPU_ISSET(i, &cpuset))
233  num_cpuids++;
234  }
235  p_cpuset->num_cpuids = num_cpuids;
236  ret = ABTU_malloc(sizeof(int) * num_cpuids, (void **)&p_cpuset->cpuids);
237  ABTI_CHECK_ERROR(ret);
238  for (i = 0, j = 0; i < CPU_SETSIZE; i++) {
239  if (CPU_ISSET(i, &cpuset))
240  p_cpuset->cpuids[j++] = i;
241  }
242  return ABT_SUCCESS;
243 #else
244  return ABT_ERR_FEATURE_NA;
245 #endif
246 }
247 
248 ABTU_ret_err static int apply_cpuset(pthread_t native_thread,
249  const ABTD_affinity_cpuset *p_cpuset)
250 {
251 #ifdef HAVE_PTHREAD_SETAFFINITY_NP
252  size_t i;
253  cpu_set_t cpuset;
254  CPU_ZERO(&cpuset);
255  for (i = 0; i < p_cpuset->num_cpuids; i++) {
256  CPU_SET(int_rem(p_cpuset->cpuids[i], CPU_SETSIZE), &cpuset);
257  }
258  int ret = pthread_setaffinity_np(native_thread, sizeof(cpu_set_t), &cpuset);
259  return ret == 0 ? ABT_SUCCESS : ABT_ERR_OTHER;
260 #else
261  return ABT_ERR_FEATURE_NA;
262 #endif
263 }
264 
265 void ABTD_affinity_init(const char *affinity_str)
266 {
267  g_affinity.num_cpusets = 0;
268  g_affinity.cpusets = NULL;
269  g_affinity.initial_cpuset.cpuids = NULL;
270  pthread_t self_native_thread = pthread_self();
271  int i, ret;
272  ret = get_num_cores(self_native_thread, &gp_ABTI_global->num_cores);
273  if (ret != ABT_SUCCESS || gp_ABTI_global->num_cores == 0) {
275  return;
276  }
277  ret = read_cpuset(self_native_thread, &g_affinity.initial_cpuset);
278  if (ret != ABT_SUCCESS) {
280  return;
281  } else if (g_affinity.initial_cpuset.num_cpuids == 0) {
284  return;
285  }
287  ABTD_affinity_list *p_list = ABTD_affinity_list_create(affinity_str);
288  if (p_list) {
289  if (p_list->num == 0) {
290  ABTD_affinity_list_free(p_list);
291  p_list = NULL;
292  }
293  }
294  if (p_list) {
295  /* Create cpusets based on the affinity list.*/
296  g_affinity.num_cpusets = p_list->num;
297  ret = ABTU_calloc(g_affinity.num_cpusets, sizeof(ABTD_affinity_cpuset),
298  (void **)&g_affinity.cpusets);
299  ABTI_ASSERT(ret == ABT_SUCCESS);
300  for (i = 0; i < p_list->num; i++) {
301  const ABTD_affinity_id_list *p_id_list = p_list->p_id_lists[i];
302  int j, num_cpuids = 0, len_cpuids = 8;
303  ret = ABTU_malloc(sizeof(int) * len_cpuids,
304  (void **)&g_affinity.cpusets[i].cpuids);
305  ABTI_ASSERT(ret == ABT_SUCCESS);
307  ABTD_affinity_list_free(p_list);
309  return;
310  }
311  for (j = 0; j < p_id_list->num; j++) {
312  int cpuid_i = int_rem(p_id_list->ids[j],
313  g_affinity.initial_cpuset.num_cpuids);
314  int cpuid = g_affinity.initial_cpuset.cpuids[cpuid_i];
315  /* If it is unique, add it.*/
316  int k, is_unique = 1;
317  for (k = 0; k < num_cpuids; k++) {
318  if (g_affinity.cpusets[i].cpuids[k] == cpuid) {
319  is_unique = 0;
320  break;
321  }
322  }
323  if (is_unique) {
324  if (num_cpuids == len_cpuids) {
325  ret = ABTU_realloc(sizeof(int) * len_cpuids,
326  sizeof(int) * len_cpuids * 2,
327  (void **)&g_affinity.cpusets[i]
328  .cpuids);
329  ABTI_ASSERT(ret == ABT_SUCCESS);
330  len_cpuids *= 2;
331  }
332  g_affinity.cpusets[i].cpuids[num_cpuids] = cpuid;
333  num_cpuids++;
334  }
335  }
336  /* Adjust the size of cpuids. */
337  if (num_cpuids != len_cpuids)
338  ret = ABTU_realloc(sizeof(int) * len_cpuids,
339  sizeof(int) * num_cpuids,
340  (void **)&g_affinity.cpusets[i].cpuids);
341  ABTI_ASSERT(ret == ABT_SUCCESS);
342  g_affinity.cpusets[i].num_cpuids = num_cpuids;
343  }
344  ABTD_affinity_list_free(p_list);
345  } else {
346  /* Create default cpusets. */
347  g_affinity.num_cpusets = g_affinity.initial_cpuset.num_cpuids;
348  ret = ABTU_calloc(g_affinity.num_cpusets, sizeof(ABTD_affinity_cpuset),
349  (void **)&g_affinity.cpusets);
350  ABTI_ASSERT(ret == ABT_SUCCESS);
351  for (i = 0; i < g_affinity.num_cpusets; i++) {
352  g_affinity.cpusets[i].num_cpuids = 1;
353  ret = ABTU_malloc(sizeof(int) * g_affinity.cpusets[i].num_cpuids,
354  (void **)&g_affinity.cpusets[i].cpuids);
355  ABTI_ASSERT(ret == ABT_SUCCESS);
356  g_affinity.cpusets[i].cpuids[0] =
357  g_affinity.initial_cpuset.cpuids[i];
358  }
359  }
360 }
361 
363 {
364  pthread_t self_native_thread = pthread_self();
366  /* Set the affinity of the main native thread to the original one. */
367  int abt_errno =
368  apply_cpuset(self_native_thread, &g_affinity.initial_cpuset);
369  /* Even if this cpuset apply fails, there is no way to handle it (e.g.,
370  * possibly the CPU affinity policy has been changed while running
371  * a user program. Let's ignore this error. */
372  (void)abt_errno;
373  }
374  /* Free g_afinity. */
376  int i;
377  for (i = 0; i < g_affinity.num_cpusets; i++) {
378  ABTD_affinity_cpuset_destroy(&g_affinity.cpusets[i]);
379  }
380  ABTU_free(g_affinity.cpusets);
381  g_affinity.cpusets = NULL;
382  g_affinity.num_cpusets = 0;
383 }
384 
386  ABTD_affinity_cpuset *p_cpuset)
387 {
388  return read_cpuset(p_ctx->native_thread, p_cpuset);
389 }
390 
391 ABTU_ret_err int
393  const ABTD_affinity_cpuset *p_cpuset)
394 {
395  return apply_cpuset(p_ctx->native_thread, p_cpuset);
396 }
397 
399  int rank)
400 {
402  ABTD_affinity_cpuset *p_cpuset =
403  &g_affinity.cpusets[rank % g_affinity.num_cpusets];
404  return apply_cpuset(p_ctx->native_thread, p_cpuset);
405  }
406  return ABT_SUCCESS;
407 }
408 
410 {
411  if (p_cpuset) {
412  ABTU_free(p_cpuset->cpuids);
413  p_cpuset->cpuids = NULL;
414  }
415 }
static ABTU_ret_err int read_cpuset(pthread_t native_thread, ABTD_affinity_cpuset *p_cpuset)
void ABTD_affinity_finalize(void)
static ABTU_ret_err int get_num_cores(pthread_t native_thread, int *p_num_cores)
static ABTU_ret_err int apply_cpuset(pthread_t native_thread, const ABTD_affinity_cpuset *p_cpuset)
static global_affinity g_affinity
pthread_t native_thread
Definition: abtd.h:22
ABTU_ret_err int ABTD_affinity_cpuset_read(ABTD_xstream_context *p_ctx, ABTD_affinity_cpuset *p_cpuset)
int ABTD_affinity_cpuset_apply_default(ABTD_xstream_context *p_ctx, int rank)
void ABTD_affinity_init(const char *affinity_str)
ABTD_affinity_list * ABTD_affinity_list_create(const char *affinity_str)
static int int_rem(int a, unsigned int b)
#define ABT_ERR_OTHER
Definition: abt.h:67
static ABTU_ret_err int ABTU_malloc(size_t size, void **p_ptr)
Definition: abtu.h:142
ABT_bool set_affinity
Definition: abti.h:178
#define ABT_FALSE
Definition: abt.h:285
static ABTU_ret_err int ABTU_realloc(size_t old_size, size_t new_size, void **p_ptr)
Definition: abtu.h:165
ABTI_global * gp_ABTI_global
Definition: global.c:18
ABTD_affinity_id_list ** p_id_lists
Definition: abtd.h:73
#define ABT_SUCCESS
Definition: abt.h:64
void ABTD_affinity_list_free(ABTD_affinity_list *p_list)
#define ABT_TRUE
Definition: abt.h:284
#define ABT_ERR_FEATURE_NA
Definition: abt.h:116
ABTU_ret_err int ABTD_affinity_cpuset_apply(ABTD_xstream_context *p_ctx, const ABTD_affinity_cpuset *p_cpuset)
size_t num_cpuids
Definition: abtd.h:36
#define ABTI_ASSERT(cond)
Definition: abti_error.h:12
#define ABTI_CHECK_ERROR(abt_errno)
Definition: abti_error.h:127
ABTD_affinity_cpuset initial_cpuset
ABTD_affinity_cpuset * cpusets
#define ABTI_IS_ERROR_CHECK_ENABLED
Definition: abti.h:20
static ABTU_ret_err int ABTU_calloc(size_t num, size_t size, void **p_ptr)
Definition: abtu.h:152
static void ABTU_free(void *ptr)
Definition: abtu.h:135
int num_cores
Definition: abti.h:177
#define ABTU_ret_err
Definition: abtu.h:49
void ABTD_affinity_cpuset_destroy(ABTD_affinity_cpuset *p_cpuset)