a new function CmiPrintCPUAffinty() that can be called on any processor to print...
[charm.git] / src / conv-core / cpuaffinity.c
1
2 /*
3  This scheme relies on using IP address to identify nodes and assigning 
4 cpu affinity.  
5
6  when CMK_NO_SOCKETS, which is typically on cray xt3 and bluegene/L.
7  There is no hostname for the compute nodes.
8  *
9  * last updated 3/20/2010   Gengbin Zheng
10  * new options +pemap +commmap takes complex pattern of a list of cores
11 */
12
13 #include "converse.h"
14 #include "sockRoutines.h"
15
16 #define DEBUGP(x)   /* CmiPrintf x;  */
17
18 #if CMK_HAS_SETAFFINITY || defined (_WIN32) || CMK_HAS_BINDPROCESSOR
19
20 #include <stdlib.h>
21 #include <stdio.h>
22 #include <unistd.h>
23
24
25 #include <stdlib.h>
26 #include <stdio.h>
27
28 #ifdef _WIN32
29 #include <windows.h>
30 #include <winbase.h>
31 #else
32 #define _GNU_SOURCE
33 #include <sched.h>
34 long sched_setaffinity(pid_t pid, unsigned int len, unsigned long *user_mask_ptr);
35 long sched_getaffinity(pid_t pid, unsigned int len, unsigned long *user_mask_ptr);
36 #endif
37
38 #if defined(__APPLE__) 
39 #include <Carbon/Carbon.h> /* Carbon APIs for Multiprocessing */
40 #endif
41
42 #if defined(ARCH_HPUX11) ||  defined(ARCH_HPUX10)
43 #include <sys/mpctl.h>
44 #endif
45
46 #define MAX_EXCLUDE      16
47 static int excludecore[MAX_EXCLUDE] = {-1};
48 static int excludecount = 0;
49
50 static int affinity_doneflag = 0;
51
52 static int in_exclude(int core)
53 {
54   int i;
55   for (i=0; i<excludecount; i++) if (core == excludecore[i]) return 1;
56   return 0;
57 }
58
59 static void add_exclude(int core)
60 {
61   if (in_exclude(core)) return;
62   CmiAssert(excludecount < MAX_EXCLUDE);
63   excludecore[excludecount++] = core;
64 }
65
66 #if CMK_HAS_BINDPROCESSOR
67 #include <sys/processor.h>
68 #endif
69
70 #define SET_MASK(cpuid)    \
71   /* set the affinity mask if possible */      \
72   if ((cpuid / 8) > len) {      \
73     printf("Mask size too small to handle requested CPU ID\n");   \
74     return -1;      \
75   } else {    \
76     mask = 1 << cpuid;   /* set the affinity mask exclusively to one CPU */ \
77   }
78
79
80 /* This implementation assumes the default x86 CPU mask size used by Linux */
81 /* For a large SMP machine, this code should be changed to use a variable sized   */
82 /* CPU affinity mask buffer instead, as the present code will fail beyond 32 CPUs */
83 int set_cpu_affinity(unsigned int cpuid) {
84   unsigned long mask = 0xffffffff;
85   unsigned int len = sizeof(mask);
86   int retValue = 0;
87   int pid;
88
89  #ifdef _WIN32
90    HANDLE hProcess;
91  #endif
92  
93 #ifdef _WIN32
94   SET_MASK(cpuid)
95   hProcess = GetCurrentProcess();
96   if (SetProcessAffinityMask(hProcess, mask) == 0) {
97     return -1;
98   }
99 #elif CMK_HAS_BINDPROCESSOR
100   pid = getpid();
101   if (bindprocessor(BINDPROCESS, pid, cpuid) == -1) return -1;
102 #else
103   SET_MASK(cpuid)
104
105   /* PID 0 refers to the current process */
106   if (sched_setaffinity(0, len, &mask) < 0) {
107     perror("sched_setaffinity");
108     return -1;
109   }
110 #endif
111
112   return 0;
113 }
114
115 int set_thread_affinity(int cpuid) {
116 #if CMK_SMP
117   unsigned long mask = 0xffffffff;
118   unsigned int len = sizeof(mask);
119
120 #ifdef _WIN32
121   HANDLE hThread;
122 #endif  
123   
124 #ifdef _WIN32
125   SET_MASK(cpuid)
126   hThread = GetCurrentThread();
127   if (SetThreadAffinityMask(hThread, mask) == 0) {
128     return -1;
129   }
130 #elif  CMK_HAS_PTHREAD_SETAFFINITY
131   SET_MASK(cpuid)
132   /* PID 0 refers to the current process */
133   if (pthread_setaffinity_np(pthread_self(), len, &mask) < 0) {
134     perror("pthread_setaffinity");
135     return -1;
136   }
137 #elif CMK_HAS_BINDPROCESSOR
138   if (bindprocessor(BINDTHREAD, thread_self(), cpuid) != 0)
139     return -1;
140 #else
141   return set_cpu_affinity(cpuid);
142 #endif
143
144   return 0;
145 #else
146   return -1;
147 #endif
148 }
149
150 /* This implementation assumes the default x86 CPU mask size used by Linux */
151 /* For a large SMP machine, this code should be changed to use a variable sized   */
152 /* CPU affinity mask buffer instead, as the present code will fail beyond 32 CPUs */
153 int print_cpu_affinity() {
154 #ifdef _WIN32
155   unsigned long pMask, sMask;
156   HANDLE hProcess = GetCurrentProcess();
157   if(GetProcessAffinityMask(hProcess, &pMask, &sMask)){
158         perror("On Windows: GetProcessAffinityMask");
159     return -1;
160   }
161   
162  CmiPrintf("[%d] CPU affinity mask is: 0x%08lx\n", CmiMype(), pMask);
163   
164 #elif CMK_HAS_BINDPROCESSOR
165   printf("[%d] CPU affinity mask is unknown for AIX. \n", CmiMyPe());
166 #else
167   unsigned long mask;
168   unsigned int len = sizeof(mask);
169
170   /* PID 0 refers to the current process */
171   if (sched_getaffinity(0, len, &mask) < 0) {
172     perror("sched_getaffinity");
173     return -1;
174   }
175
176   CmiPrintf("[%d] CPU affinity mask is: 0x%08lx\n", CmiMyPe(), mask);
177 #endif
178   return 0;
179 }
180
181 int print_thread_affinity() {
182 #if CMK_SMP
183   unsigned long mask;
184   size_t len = sizeof(mask);
185
186 #if  CMK_HAS_PTHREAD_SETAFFINITY
187   if (pthread_getaffinity_np(pthread_self(), len, &mask) < 0) {
188     perror("pthread_setaffinity");
189     return -1;
190   }
191   CmiPrintf("[%d] %s affinity mask is: 0x%08lx\n", CmiMyPe(), CmiMyPe()>=CmiNumPes()?"communication pthread":"pthread", mask);
192 #endif
193 #endif
194   return 0;
195 }
196
197 int CmiPrintCPUAffinity()
198 {
199 #if CMK_SMP
200   print_thread_affinity();
201 #else
202   print_cpu_affinity();
203 #endif
204 }
205
206 static int cpuAffinityHandlerIdx;
207 static int cpuAffinityRecvHandlerIdx;
208
209 typedef struct _hostnameMsg {
210   char core[CmiMsgHeaderSizeBytes];
211   int pe;
212   skt_ip_t ip;
213   int ncores;
214   int rank;
215   int seq;
216 } hostnameMsg;
217
218 typedef struct _rankMsg {
219   char core[CmiMsgHeaderSizeBytes];
220   int *ranks;                  /* PE => core rank mapping */
221   int *nodes;                  /* PE => node number mapping */
222 } rankMsg;
223
224 static rankMsg *rankmsg = NULL;
225 static CmmTable hostTable;
226 static CmiNodeLock affLock = NULL;
227
228 /* called on PE 0 */
229 static void cpuAffinityHandler(void *m)
230 {
231   static int count = 0;
232   static int nodecount = 0;
233   hostnameMsg *rec;
234   hostnameMsg *msg = (hostnameMsg *)m;
235   hostnameMsg *tmpm;
236   int tag, tag1, pe, myrank;
237   int npes = CmiNumPes();
238
239 /*   for debug
240   char str[128];
241   skt_print_ip(str, msg->ip);
242   printf("hostname: %d %s\n", msg->pe, str);
243 */
244   CmiAssert(CmiMyPe()==0 && rankmsg != NULL);
245   tag = *(int*)&msg->ip;
246   pe = msg->pe;
247   if ((rec = (hostnameMsg *)CmmProbe(hostTable, 1, &tag, &tag1)) != NULL) {
248     CmiFree(msg);
249   }
250   else {
251     rec = msg;
252     rec->seq = nodecount;
253     nodecount++;                          /* a new node record */
254     CmmPut(hostTable, 1, &tag, msg);
255   }
256   myrank = rec->rank%rec->ncores;
257   while (in_exclude(myrank)) {             /* skip excluded core */
258     myrank = (myrank+1)%rec->ncores;
259     rec->rank ++;
260   }
261   rankmsg->ranks[pe] = myrank;             /* core rank */
262   rankmsg->nodes[pe] = rec->seq;           /* on which node */
263   rec->rank ++;
264   count ++;
265   if (count == CmiNumPes()) {
266     /* CmiPrintf("Cpuaffinity> %d unique compute nodes detected! \n", CmmEntries(hostTable)); */
267     tag = CmmWildCard;
268     while (tmpm = CmmGet(hostTable, 1, &tag, &tag1)) CmiFree(tmpm);
269     CmmFree(hostTable);
270 #if 1
271     /* bubble sort ranks on each node according to the PE number */
272     {
273     int i,j;
274     for (i=0; i<npes-1; i++)
275       for(j=i+1; j<npes; j++) {
276         if (rankmsg->nodes[i] == rankmsg->nodes[j] && 
277               rankmsg->ranks[i] > rankmsg->ranks[j]) 
278         {
279           int tmp = rankmsg->ranks[i];
280           rankmsg->ranks[i] = rankmsg->ranks[j];
281           rankmsg->ranks[j] = tmp;
282         }
283       }
284     }
285 #endif
286     CmiSyncBroadcastAllAndFree(sizeof(rankMsg)+CmiNumPes()*sizeof(int)*2, (void *)rankmsg);
287   }
288 }
289
290 static int set_myaffinitity(int mycore)
291 {
292   int core = mycore;
293   if (core < 0) {
294     core = CmiNumCores() + core;
295   }
296   if (core < 0) {
297     CmiError("Error: Invalid cpu affinity core number: %d\n", mycore);
298     CmiAbort("set_myaffinitity failed");
299   }
300   /* set cpu affinity */
301 #if CMK_SMP
302   return set_thread_affinity(core);
303 #else
304   return set_cpu_affinity(core);
305   /* print_cpu_affinity(); */
306 #endif
307 }
308
309 /* called on each processor */
310 static void cpuAffinityRecvHandler(void *msg)
311 {
312   int myrank, mynode;
313   rankMsg *m = (rankMsg *)msg;
314   m->ranks = (int *)((char*)m + sizeof(rankMsg));
315   m->nodes = (int *)((char*)m + sizeof(rankMsg) + CmiNumPes()*sizeof(int));
316   myrank = m->ranks[CmiMyPe()];
317   mynode = m->nodes[CmiMyPe()];
318
319   /*CmiPrintf("[%d %d] set to core #: %d\n", CmiMyNode(), CmiMyPe(), myrank);*/
320
321   if (-1 != set_myaffinitity(myrank)) {
322     DEBUGP(("Processor %d is bound to core #%d on node #%d\n", CmiMyPe(), myrank, mynode));
323   }
324   else
325     CmiPrintf("Processor %d set affinity failed!\n", CmiMyPe());
326
327   CmiFree(m);
328 }
329
330 #if defined(_WIN32) && ! defined(__CYGWIN__)
331   /* strtok is thread safe in VC++ */
332 #define strtok_r(x,y,z) strtok(x,y)
333 #endif
334
335 static int search_pemap(char *pecoremap, int pe)
336 {
337   int *map = (int *)malloc(CmiNumPes()*sizeof(int));
338   char *ptr = NULL;
339   int i, count;
340   char *str;
341
342   char *mapstr = (char*)malloc(strlen(pecoremap)+1);
343   strcpy(mapstr, pecoremap);
344
345   str = strtok_r(mapstr, ",", &ptr);
346   count = 0;
347   while (str)
348   {
349       int hasdash=0, hascolon=0;
350       int start, end, stride=1;
351       for (i=0; i<strlen(str); i++) {
352           if (str[i] == '-' && i!=0) hasdash=1;
353           if (str[i] == ':') hascolon=1;
354       }
355       if (hasdash) {
356           if (hascolon) {
357             if (sscanf(str, "%d-%d:%d", &start, &end, &stride) != 3)
358                  printf("Warning: Check the format of \"%s\".\n", str);
359           }
360           else {
361             if (sscanf(str, "%d-%d", &start, &end) != 2)
362                  printf("Warning: Check the format of \"%s\".\n", str);
363           }
364       }
365       else {
366           sscanf(str, "%d", &start);
367           end = start;
368       }
369       for (i = start; i<=end; i+=stride) {
370         map[count++] = i;
371         if (count == CmiNumPes()) break;
372       }
373       if (count == CmiNumPes()) break;
374       str = strtok_r(NULL, ",", &ptr);
375   }
376   i = map[pe % count];
377
378   free(map);
379   free(mapstr);
380   return i;
381 }
382
383 #if CMK_CRAYXT
384 extern int getXTNodeID(int mype, int numpes);
385 #endif
386
387 void CmiInitCPUAffinity(char **argv)
388 {
389   static skt_ip_t myip;
390   int ret, i, exclude;
391   hostnameMsg  *msg;
392   char *pemap = NULL;
393   char *commap = NULL;
394   char *coremap = NULL;
395  
396   int affinity_flag = CmiGetArgFlagDesc(argv,"+setcpuaffinity",
397                                                 "set cpu affinity");
398
399   while (CmiGetArgIntDesc(argv,"+excludecore", &exclude, "avoid core when setting cpuaffinity")) 
400     if (CmiMyRank() == 0) add_exclude(exclude);
401   
402     /* obsolete */
403   CmiGetArgStringDesc(argv, "+coremap", &coremap, "define core mapping");
404   if (coremap!=NULL && excludecount>0)
405     CmiAbort("Charm++> +excludecore and +coremap can not be used togetehr!");
406
407   CmiGetArgStringDesc(argv, "+pemap", &pemap, "define pe to core mapping");
408   if (pemap!=NULL && (coremap != NULL || excludecount>0))
409     CmiAbort("Charm++> +pemap can not be used with either +excludecore or +coremap.");
410   CmiGetArgStringDesc(argv, "+commap", &commap, "define comm threads to core mapping");
411   int show_affinity_flag = CmiGetArgFlagDesc(argv,"+showcpuaffinity",
412                                                 "print cpu affinity");
413
414   cpuAffinityHandlerIdx =
415        CmiRegisterHandler((CmiHandler)cpuAffinityHandler);
416   cpuAffinityRecvHandlerIdx =
417        CmiRegisterHandler((CmiHandler)cpuAffinityRecvHandler);
418
419   if (CmiMyRank() ==0) {
420      affLock = CmiCreateLock();
421   }
422
423   if (!affinity_flag) {
424     if (show_affinity_flag) CmiPrintCPUAffinity();
425     return;
426   }
427   else if (CmiMyPe() == 0) {
428      CmiPrintf("Charm++> cpu affinity enabled. \n");
429      if (excludecount > 0) {
430        CmiPrintf("Charm++> cpuaffinity excludes core: %d", excludecore[0]);
431        for (i=1; i<excludecount; i++) CmiPrintf(" %d", excludecore[i]);
432        CmiPrintf(".\n");
433      }
434      if (coremap!=NULL)
435        CmiPrintf("Charm++> cpuaffinity core map : %s\n", coremap);
436      if (pemap!=NULL)
437        CmiPrintf("Charm++> cpuaffinity PE-core map : %s\n", pemap);
438   }
439
440   if (CmiMyPe() >= CmiNumPes()) {         /* this is comm thread */
441       /* comm thread either can float around, or pin down to the last rank.
442          however it seems to be reportedly slower if it is floating */
443     CmiNodeAllBarrier();
444     if (commap != NULL) {
445       int mycore = search_pemap(commap, CmiMyPe()-CmiNumPes());
446       printf("Charm++> set comm %d on node %d to core #%d\n", CmiMyPe()-CmiNumPes(), CmiMyNode(), mycore); 
447       if (-1 == set_myaffinitity(mycore))
448         CmiAbort("set_cpu_affinity abort!");
449     }
450     else {
451     /* if (set_myaffinitity(CmiNumCores()-1) == -1) CmiAbort("set_cpu_affinity abort!"); */
452     }
453     if (coremap == NULL && pemap == NULL) {
454 #if CMK_MACHINE_PROGRESS_DEFINED
455     while (affinity_doneflag < CmiMyNodeSize())  CmiNetworkProgress();
456 #else
457 #if CMK_SMP
458     #error "Machine progress call needs to be implemented for cpu affinity!"
459 #endif
460 #endif
461     }
462     CmiNodeAllBarrier();
463     if (show_affinity_flag) CmiPrintCPUAffinity();
464     return;    /* comm thread return */
465   }
466
467   if (pemap != NULL) {
468     int mycore = search_pemap(pemap, CmiMyPe());
469     printf("Charm++> set PE %d on node %d to core #%d\n", CmiMyPe(), CmiMyNode(), mycore); 
470     if (mycore >= CmiNumCores()) {
471       CmiPrintf("Error> Invalid core number %d, only have %d cores (0-%d) on the node. \n", mycore, CmiNumCores(), CmiNumCores()-1);
472       CmiAbort("Invalid core number");
473     }
474     if (set_myaffinitity(mycore) == -1) CmiAbort("set_cpu_affinity abort!");
475     CmiNodeAllBarrier();
476     CmiNodeAllBarrier();
477     if (show_affinity_flag) CmiPrintCPUAffinity();
478     return;
479   }
480
481   if (coremap!= NULL) {
482     /* each processor finds its mapping */
483     int i, ct=1, myrank;
484     for (i=0; i<strlen(coremap); i++) if (coremap[i]==',' && i<strlen(coremap)-1) ct++;
485 #if CMK_SMP
486     ct = CmiMyRank()%ct;
487 #else
488     ct = CmiMyPe()%ct;
489 #endif
490     i=0;
491     while(ct>0) if (coremap[i++]==',') ct--;
492     myrank = atoi(coremap+i);
493     printf("Charm++> set PE%d on node #%d to core #%d\n", CmiMyPe(), CmiMyNode(), myrank); 
494     if (myrank >= CmiNumCores()) {
495       CmiPrintf("Error> Invalid core number %d, only have %d cores (0-%d) on the node. \n", myrank, CmiNumCores(), CmiNumCores()-1);
496       CmiAbort("Invalid core number");
497     }
498     if (set_myaffinitity(myrank) == -1) CmiAbort("set_cpu_affinity abort!");
499     CmiNodeAllBarrier();
500     CmiNodeAllBarrier();
501     return;
502   }
503
504     /* get my ip address */
505   if (CmiMyRank() == 0)
506   {
507 #if CMK_CRAYXT
508     ret = getXTNodeID(CmiMyPe(), CmiNumPes());
509     memcpy(&myip, &ret, sizeof(int));
510 #elif CMK_HAS_GETHOSTNAME
511     myip = skt_my_ip();        /* not thread safe, so only calls on rank 0 */
512 #else
513     CmiAbort("Can not get unique name for the compute nodes. \n");
514 #endif
515   }
516   CmiNodeAllBarrier();
517
518     /* prepare a msg to send */
519   msg = (hostnameMsg *)CmiAlloc(sizeof(hostnameMsg));
520   CmiSetHandler((char *)msg, cpuAffinityHandlerIdx);
521   msg->pe = CmiMyPe();
522   msg->ip = myip;
523   msg->ncores = CmiNumCores();
524   DEBUGP(("PE %d's node has %d number of cores. \n", CmiMyPe(), msg->ncores));
525   msg->rank = 0;
526   CmiSyncSendAndFree(0, sizeof(hostnameMsg), (void *)msg);
527
528   if (CmiMyPe() == 0) {
529     int i;
530     hostTable = CmmNew();
531     rankmsg = (rankMsg *)CmiAlloc(sizeof(rankMsg)+CmiNumPes()*sizeof(int)*2);
532     CmiSetHandler((char *)rankmsg, cpuAffinityRecvHandlerIdx);
533     rankmsg->ranks = (int *)((char*)rankmsg + sizeof(rankMsg));
534     rankmsg->nodes = (int *)((char*)rankmsg + sizeof(rankMsg) + CmiNumPes()*sizeof(int));
535     for (i=0; i<CmiNumPes(); i++) {
536       rankmsg->ranks[i] = 0;
537       rankmsg->nodes[i] = -1;
538     }
539
540     for (i=0; i<CmiNumPes(); i++) CmiDeliverSpecificMsg(cpuAffinityHandlerIdx);
541   }
542
543     /* receive broadcast from PE 0 */
544   CmiDeliverSpecificMsg(cpuAffinityRecvHandlerIdx);
545   CmiLock(affLock);
546   affinity_doneflag++;
547   CmiUnlock(affLock);
548   CmiNodeAllBarrier();
549
550   if (show_affinity_flag) CmiPrintCPUAffinity();
551 }
552
553 #else           /* not supporting affinity */
554
555
556 int CmiPrintCPUAffinity()
557 {
558   CmiPrintf("Warning: CmiPrintCPUAffinity not supported.\n");
559 }
560
561 void CmiInitCPUAffinity(char **argv)
562 {
563   char *pemap = NULL;
564   char *commap = NULL;
565   char *coremap = NULL;
566   int excludecore = -1;
567   int affinity_flag = CmiGetArgFlagDesc(argv,"+setcpuaffinity",
568                                                 "set cpu affinity");
569   while (CmiGetArgIntDesc(argv,"+excludecore",&excludecore, "avoid core when setting cpuaffinity"));
570   CmiGetArgStringDesc(argv, "+coremap", &coremap, "define core mapping");
571   CmiGetArgStringDesc(argv, "+pemap", &pemap, "define pe to core mapping");
572   CmiGetArgStringDesc(argv, "+commap", &commap, "define comm threads to core mapping");
573   if (affinity_flag && CmiMyPe()==0)
574     CmiPrintf("sched_setaffinity() is not supported, +setcpuaffinity disabled.\n");
575 }
576
577 #endif