doc: Add serial to list of ci file reserved words
[charm.git] / src / conv-core / cputopology.C
1 #include <map>
2 #include "converse.h"
3 #include "sockRoutines.h"
4 #include "cklists.h"
5
6 #define DEBUGP(x)  /** CmiPrintf x; */
7
8 /** This scheme relies on using IP address to identify physical nodes 
9  * written by Gengbin Zheng  9/2008
10  *
11  * last updated 10/4/2009   Gengbin Zheng
12  * added function CmiCpuTopologyEnabled() which retuens 1 when supported
13  * when not supported return 0
14  * all functions when cputopology not support, now act like a normal non-smp 
15  * case and all PEs are unique.
16  *
17  * major changes 10/28/09   Gengbin Zheng
18  * - parameters changed from pe to node to be consistent with the function name
19  * - two new functions:   CmiPhysicalNodeID and CmiPhysicalRank
20  *
21  * 3/5/2010   Gengbin Zheng
22  * - use CmiReduce to optimize the collection of node info
23  */
24
25 #if 1
26
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <unistd.h>
30
31 #include <stdlib.h>
32 #include <stdio.h>
33
34 #if CMK_BLUEGENEL || CMK_BLUEGENEP || CMK_BLUEGENEQ
35 #include "TopoManager.h"
36 #endif
37
38 #if CMK_CRAYXT || CMK_CRAYXE
39 extern "C" int getXTNodeID(int mpirank, int nummpiranks);
40 #endif
41
42 #if defined(__APPLE__)  && CMK_HAS_MULTIPROCESSING_H
43 #include <Carbon/Carbon.h>
44 #include <Multiprocessing.h>
45 #endif
46
47 #if CMK_BIGSIM_CHARM
48 #include "middle-blue.h"
49 using namespace BGConverse;
50 #endif
51
52 extern "C" int CmiNumCores(void) {
53   int a = 1;
54 #ifdef _WIN32
55 struct _SYSTEM_INFO sysinfo;
56 #endif  
57
58   /* Allow the user to override the number of CPUs for use
59      in scalability testing, debugging, etc. */
60   char *forcecount = getenv("FORCECPUCOUNT");
61   if (forcecount != NULL) {
62     if (sscanf(forcecount, "%d", &a) == 1) {
63       return a; /* if we got a valid count, return it */
64     } else {
65       a = 1;      /* otherwise use the real available hardware CPU count */
66     }
67   }
68
69 #if defined(__APPLE__)  && CMK_HAS_MULTIPROCESSING_H
70   a = MPProcessorsScheduled(); /* Number of active/running CPUs */
71 #endif
72
73 #ifdef _WIN32
74   //struct _SYSTEM_INFO sysinfo;  
75   GetSystemInfo(&sysinfo);
76   a = sysinfo.dwNumberOfProcessors; /* total number of CPUs */
77 #endif /* _MSC_VER */
78
79
80 #ifdef _SC_NPROCESSORS_ONLN
81   a = sysconf(_SC_NPROCESSORS_ONLN); /* number of active/running CPUs */
82 #elif defined(_SC_CRAY_NCPU)
83   a = sysconf(_SC_CRAY_NCPU);
84 #elif defined(_SC_NPROC_ONLN)
85   a = sysconf(_SC_NPROC_ONLN); /* number of active/running CPUs */
86 #endif
87   if (a == -1) a = 1;
88
89 #if defined(ARCH_HPUX11) || defined(ARCH_HPUX10)
90   a = mpctl(MPC_GETNUMSPUS, 0, 0); /* total number of CPUs */
91 #endif /* HPUX */
92
93   return a;
94 }
95
96 static int cpuTopoHandlerIdx;
97 static int cpuTopoRecvHandlerIdx;
98
99 struct _procInfo {
100   skt_ip_t ip;
101   int pe;
102   int ncores;
103   int rank;
104   int nodeID;
105 };
106
107 typedef struct _hostnameMsg {
108   char core[CmiMsgHeaderSizeBytes];
109   int n;
110   _procInfo *procs;
111 } hostnameMsg;
112
113 typedef struct _nodeTopoMsg {
114   char core[CmiMsgHeaderSizeBytes];
115   int *nodes;
116 } nodeTopoMsg;
117
118 static nodeTopoMsg *topomsg = NULL;
119 static CmmTable hostTable;
120
121 // nodeIDs[pe] is the node number of processor pe
122 class CpuTopology {
123 public:
124   static int *nodeIDs;
125   static int numPes;
126   static int numNodes;
127   static CkVec<int> *bynodes;
128   static int supported;
129
130     // return -1 when not supported
131   int numUniqNodes() {
132 #if 0
133     if (numNodes != 0) return numNodes;
134     int n = 0;
135     for (int i=0; i<CmiNumPes(); i++) 
136       if (nodeIDs[i] > n)
137         n = nodeIDs[i];
138     numNodes = n+1;
139     return numNodes;
140 #else
141     if (numNodes > 0) return numNodes;     // already calculated
142     CkVec<int> unodes;
143     int i;
144     for (i=0; i<numPes; i++)  unodes.push_back(nodeIDs[i]);
145     //unodes.bubbleSort(0, numPes-1);
146     unodes.quickSort();
147     int last = -1;
148     std::map<int, int> nodemap;  // nodeIDs can be out of range of [0,numNodes]
149     for (i=0; i<numPes; i++)  { 
150         if (unodes[i] != last) {
151           last=unodes[i];
152           nodemap[unodes[i]] = numNodes;
153           numNodes++; 
154         }
155     }
156     if (numNodes == 0)  {
157       numNodes = CmiNumNodes();
158       numPes = CmiNumPes();
159     }
160     else {
161         // re-number nodeIDs, which may be necessary e.g. on BlueGene/P
162       for (i=0; i<numPes; i++) nodeIDs[i] = nodemap[nodeIDs[i]];
163       CpuTopology::supported = 1;
164     }
165     return numNodes;
166 #endif
167   }
168
169   void sort() {
170     int i;
171     numUniqNodes();
172     bynodes = new CkVec<int>[numNodes];
173     if (supported) {
174       for (i=0; i<numPes; i++){
175         CmiAssert(nodeIDs[i] >=0 && nodeIDs[i] <= numNodes); // Sanity check for bug that occurs on mpi-crayxt
176         bynodes[nodeIDs[i]].push_back(i);
177       }
178     }
179     else {    /* not supported/enabled */
180       for (i=0;i<CmiNumPes();i++)  bynodes[CmiNodeOf(i)].push_back(i);
181     }
182   }
183
184   void print() {
185     int i;
186     CmiPrintf("Charm++> Cpu topology info:\n");
187     CmiPrintf("PE to node map: ");
188     for (i=0; i<CmiNumPes(); i++)
189       CmiPrintf("%d ", nodeIDs[i]);
190     CmiPrintf("\n");
191     CmiPrintf("Node to PE map:\n");
192     for (i=0; i<numNodes; i++) {
193       CmiPrintf("Chip #%d: ", i);
194       for (int j=0; j<bynodes[i].size(); j++)
195         CmiPrintf("%d ", bynodes[i][j]);
196       CmiPrintf("\n");
197     }
198   }
199
200 };
201
202 int *CpuTopology::nodeIDs = NULL;
203 int CpuTopology::numPes = 0;
204 int CpuTopology::numNodes = 0;
205 CkVec<int> *CpuTopology::bynodes = NULL;
206 int CpuTopology::supported = 0;
207
208 static CpuTopology cpuTopo;
209 static CmiNodeLock topoLock = 0; /* Not spelled 'NULL' to quiet warnings when CmiNodeLock is just 'int' */
210 static int done = 0;
211
212 /* called on PE 0 */
213 static void cpuTopoHandler(void *m)
214 {
215   _procInfo *rec;
216   hostnameMsg *msg = (hostnameMsg *)m;
217   char str[256];
218   int tag, tag1, pe;
219
220   if (topomsg == NULL) {
221     int i;
222     hostTable = CmmNew();
223     topomsg = (nodeTopoMsg *)CmiAlloc(sizeof(nodeTopoMsg)+CmiNumPes()*sizeof(int));
224     CmiSetHandler((char *)topomsg, cpuTopoRecvHandlerIdx);
225     topomsg->nodes = (int *)((char*)topomsg + sizeof(nodeTopoMsg));
226     for (i=0; i<CmiNumPes(); i++) topomsg->nodes[i] = -1;
227   }
228   CmiAssert(topomsg != NULL);
229
230   msg->procs = (_procInfo*)((char*)msg + sizeof(hostnameMsg));
231   CmiAssert(msg->n == CmiNumPes());
232   for (int i=0; i<msg->n; i++) 
233   {
234     _procInfo *proc = msg->procs+i;
235
236 /*   for debug
237   skt_print_ip(str, msg->ip);
238   printf("hostname: %d %s\n", msg->pe, str);
239 */
240     tag = *(int*)&proc->ip;
241     pe = proc->pe;
242     if ((rec = (_procInfo *)CmmProbe(hostTable, 1, &tag, &tag1)) != NULL) {
243     }
244     else {
245       proc->nodeID = pe;           // we will compact the node ID later
246       rec = proc;
247       CmmPut(hostTable, 1, &tag, proc);
248     }
249     topomsg->nodes[pe] = rec->nodeID;
250     rec->rank ++;
251   }
252
253     // assume all nodes have same number of cores
254   int ncores = CmiNumCores();
255   if (ncores > 1)
256     sprintf(str, "Charm++> Running on %d unique compute nodes (%d-way SMP).\n", CmmEntries(hostTable), ncores);
257   else
258     sprintf(str, "Charm++> Running on %d unique compute nodes.\n", CmmEntries(hostTable));
259   CmiPrintf(str);
260     // clean up CmmTable
261   hostnameMsg *tmpm;
262   tag = CmmWildCard;
263   while (tmpm = (hostnameMsg *)CmmGet(hostTable, 1, &tag, &tag1));
264   CmmFree(hostTable);
265   CmiFree(msg);
266
267   CmiSyncBroadcastAllAndFree(sizeof(nodeTopoMsg)+CmiNumPes()*sizeof(int), (char *)topomsg);
268 }
269
270 /* called on each processor */
271 static void cpuTopoRecvHandler(void *msg)
272 {
273   nodeTopoMsg *m = (nodeTopoMsg *)msg;
274   m->nodes = (int *)((char*)m + sizeof(nodeTopoMsg));
275
276   CmiLock(topoLock);
277   if (cpuTopo.nodeIDs == NULL) {
278     cpuTopo.nodeIDs = m->nodes;
279     cpuTopo.sort();
280   }
281   else
282     CmiFree(m);
283   done++;
284   CmiUnlock(topoLock);
285
286   //if (CmiMyPe() == 0) cpuTopo.print();
287 }
288
289 // reduction function
290 static void * combineMessage(int *size, void *data, void **remote, int count) 
291 {
292   int i, j;
293   int nprocs = ((hostnameMsg *)data)->n;
294   if (count == 0) return data;
295   for (i=0; i<count; i++) nprocs += ((hostnameMsg *)remote[i])->n;
296   *size = sizeof(hostnameMsg)+sizeof(_procInfo)*nprocs;
297   hostnameMsg *msg = (hostnameMsg *)CmiAlloc(*size);
298   msg->procs = (_procInfo*)((char*)msg + sizeof(hostnameMsg));
299   msg->n = nprocs;
300   CmiSetHandler((char *)msg, cpuTopoHandlerIdx);
301
302   int n=0;
303   hostnameMsg *m = (hostnameMsg*)data;
304   m->procs = (_procInfo*)((char*)m + sizeof(hostnameMsg));
305   for (j=0; j<m->n; j++)
306     msg->procs[n++] = m->procs[j];
307   for (i=0; i<count; i++) {
308     m = (hostnameMsg*)remote[i];
309     m->procs = (_procInfo*)((char*)m + sizeof(hostnameMsg));
310     for (j=0; j<m->n; j++)
311       msg->procs[n++] = m->procs[j];
312   }
313   return msg;
314 }
315
316 /******************  API implementation **********************/
317
318 extern "C" int LrtsCpuTopoEnabled()
319 {
320   return CpuTopology::supported;
321 }
322
323 extern "C" int LrtsPeOnSameNode(int pe1, int pe2)
324 {
325   int *nodeIDs = cpuTopo.nodeIDs;
326   if (!cpuTopo.supported || nodeIDs == NULL) return CmiNodeOf(pe1) == CmiNodeOf(pe2);
327   else return nodeIDs[pe1] == nodeIDs[pe2];
328 }
329
330 // return -1 when not supported
331 extern "C" int LrtsNumNodes()
332 {
333   if (!cpuTopo.supported) return CmiNumNodes();
334   else return cpuTopo.numUniqNodes();
335 }
336
337 extern "C" int LrtsNodeSize(int node)
338 {
339   return !cpuTopo.supported?CmiNodeSize(node):(int)cpuTopo.bynodes[node].size();
340 }
341
342 // pelist points to system memory, user should not free it
343 extern "C" void LrtsPeOnNode(int node, int **pelist, int *num)
344 {
345   *num = cpuTopo.bynodes[node].size();
346   if (pelist!=NULL && *num>0) *pelist = cpuTopo.bynodes[node].getVec();
347 }
348
349 extern "C" int LrtsRankOf(int pe)
350 {
351   if (!cpuTopo.supported) return CmiRankOf(pe);
352   const CkVec<int> &v = cpuTopo.bynodes[cpuTopo.nodeIDs[pe]];
353   int rank = 0;  
354   int npes = v.size();
355   while (rank < npes && v[rank] < pe) rank++;       // already sorted
356   CmiAssert(v[rank] == pe);
357   return rank;
358 }
359
360 extern "C" int LrtsNodeOf(int pe)
361 {
362   if (!cpuTopo.supported) return CmiNodeOf(pe);
363   return cpuTopo.nodeIDs[pe];
364 }
365
366 // the least number processor on the same physical node
367 extern "C"  int LrtsNodeFirst(int node)
368 {
369   if (!cpuTopo.supported) return CmiNodeFirst(node);
370   return cpuTopo.bynodes[node][0];
371 }
372
373
374 static int _noip = 0;
375 extern "C" void LrtsInitCpuTopo(char **argv)
376 {
377   static skt_ip_t myip;
378   hostnameMsg  *msg;
379   double startT;
380  
381   int obtain_flag = 1;              // default on
382   int show_flag = 0;                // default not show topology
383
384   if (CmiMyRank() ==0) {
385      topoLock = CmiCreateLock();
386   }
387
388 #if __FAULT__
389   obtain_flag = 0;
390 #endif
391   if(CmiGetArgFlagDesc(argv,"+obtain_cpu_topology",
392                                            "obtain cpu topology info"))
393     obtain_flag = 1;
394   if (CmiGetArgFlagDesc(argv,"+skip_cpu_topology",
395                                "skip the processof getting cpu topology info"))
396     obtain_flag = 0;
397   if(CmiGetArgFlagDesc(argv,"+show_cpu_topology",
398                                            "Show cpu topology info"))
399     show_flag = 1;
400
401 #if CMK_BIGSIM_CHARM
402   if (BgNodeRank() == 0)
403 #endif
404   {
405   cpuTopoHandlerIdx =
406      CmiRegisterHandler((CmiHandler)cpuTopoHandler);
407   cpuTopoRecvHandlerIdx =
408      CmiRegisterHandler((CmiHandler)cpuTopoRecvHandler);
409   }
410
411   if (!obtain_flag) {
412     if (CmiMyRank() == 0) cpuTopo.sort();
413     CmiNodeAllBarrier();
414     CcdRaiseCondition(CcdTOPOLOGY_AVAIL);      // call callbacks
415     return;
416   }
417
418   if (CmiMyPe() == 0) {
419 #if CMK_BIGSIM_CHARM
420     if (BgNodeRank() == 0)
421 #endif
422       startT = CmiWallTimer();
423   }
424
425 #if CMK_BIGSIM_CHARM
426   if (BgNodeRank() == 0)
427   {
428     //int numPes = BgNumNodes()*BgGetNumWorkThread();
429     int numPes = cpuTopo.numPes = CkNumPes();
430     cpuTopo.nodeIDs = new int[numPes];
431     CpuTopology::supported = 1;
432     int wth = BgGetNumWorkThread();
433     for (int i=0; i<numPes; i++) {
434       int nid = i / wth;
435       cpuTopo.nodeIDs[i] = nid;
436     }
437     cpuTopo.sort();
438   }
439   return;
440 #else
441
442 #if CMK_USE_GM
443   CmiBarrier();
444 #endif
445
446
447 #if 0
448   if (gethostname(hostname, 999)!=0) {
449       strcpy(hostname, "");
450   }
451 #endif
452 #if CMK_BLUEGENEL || CMK_BLUEGENEP
453   if (CmiMyRank() == 0) {
454     TopoManager tmgr;
455
456     int numPes = cpuTopo.numPes = CmiNumPes();
457     cpuTopo.nodeIDs = new int[numPes];
458     CpuTopology::supported = 1;
459
460     int x, y, z, t, nid;
461     for(int i=0; i<numPes; i++) {
462       tmgr.rankToCoordinates(i, x, y, z, t);
463       nid = tmgr.coordinatesToRank(x, y, z, 0);
464       cpuTopo.nodeIDs[i] = nid;
465     }
466     cpuTopo.sort();
467     if (CmiMyPe()==0)  CmiPrintf("Charm++> Running on %d unique compute nodes (%d-way SMP).\n", cpuTopo.numNodes, CmiNumCores());
468   }
469   CmiNodeAllBarrier();
470 #elif CMK_BLUEGENEQ
471   if (CmiMyRank() == 0) {
472    TopoManager tmgr;
473
474     int numPes = cpuTopo.numPes = CmiNumPes();
475     cpuTopo.nodeIDs = new int[numPes];
476     CpuTopology::supported = 1;
477
478     int a, b, c, d, e, t, nid;
479     for(int i=0; i<numPes; i++) {
480       tmgr.rankToCoordinates(i, a, b, c, d, e, t);
481       nid = tmgr.coordinatesToRank(a, b, c, d, e, 0);
482       cpuTopo.nodeIDs[i] = nid;
483     }
484     cpuTopo.sort();
485     if (CmiMyPe()==0)  CmiPrintf("Charm++> Running on %d unique compute nodes (%d-way SMP).\n", cpuTopo.numNodes, CmiNumCores());
486   }
487   CmiNodeAllBarrier();
488 #elif CMK_CRAYXT || CMK_CRAYXE
489   if(CmiMyRank() == 0) {
490     int numPes = cpuTopo.numPes = CmiNumPes();
491     int numNodes = CmiNumNodes();
492     cpuTopo.nodeIDs = new int[numPes];
493     CpuTopology::supported = 1;
494
495     int nid;
496     for(int i=0; i<numPes; i++) {
497       nid = getXTNodeID(CmiNodeOf(i), numNodes);
498       cpuTopo.nodeIDs[i] = nid;
499     }
500     int prev = -1;
501     nid = -1;
502
503     // this assumes that all cores on a node have consecutive MPI rank IDs
504     // and then changes nodeIDs to 0 to numNodes-1
505     for(int i=0; i<numPes; i++) {
506       if(cpuTopo.nodeIDs[i] != prev) {
507         prev = cpuTopo.nodeIDs[i];
508         cpuTopo.nodeIDs[i] = ++nid;
509       }
510       else
511         cpuTopo.nodeIDs[i] = nid;
512     }
513     cpuTopo.sort();
514     if (CmiMyPe()==0)  CmiPrintf("Charm++> Running on %d unique compute nodes (%d-way SMP).\n", cpuTopo.numNodes, CmiNumCores());
515   }
516   CmiNodeAllBarrier();
517
518 #else
519
520   if (CmiMyPe() >= CmiNumPes()) {
521     CmiNodeAllBarrier();         // comm thread waiting
522 #if CMK_MACHINE_PROGRESS_DEFINED
523 #if ! CMK_CRAYXT
524     while (done < CmiMyNodeSize()) CmiNetworkProgress();
525 #endif
526 #endif
527     return;    /* comm thread return */
528   }
529
530     /* get my ip address */
531   if (CmiMyRank() == 0)
532   {
533   #if CMK_HAS_GETHOSTNAME && !CMK_BLUEGENEQ
534     myip = skt_my_ip();        /* not thread safe, so only calls on rank 0 */
535     // fprintf(stderr, "[%d] IP is %d.%d.%d.%d\n", CmiMyPe(), myip.data[0],myip.data[1],myip.data[2],myip.data[3]);
536   #elif CMK_BPROC
537     myip = skt_innode_my_ip();
538   #else
539     if (!CmiMyPe())
540     CmiPrintf("CmiInitCPUTopology Warning: Can not get unique name for the compute nodes. \n");
541     _noip = 1; 
542   #endif
543   }
544   cpuTopo.numPes = CmiNumPes();
545
546   CmiNodeAllBarrier();
547   if (_noip) return; 
548
549     /* prepare a msg to send */
550   msg = (hostnameMsg *)CmiAlloc(sizeof(hostnameMsg)+sizeof(_procInfo));
551   msg->n = 1;
552   msg->procs = (_procInfo*)((char*)msg + sizeof(hostnameMsg));
553   CmiSetHandler((char *)msg, cpuTopoHandlerIdx);
554   msg->procs[0].pe = CmiMyPe();
555   msg->procs[0].ip = myip;
556   msg->procs[0].ncores = CmiNumCores();
557   msg->procs[0].rank = 0;
558   msg->procs[0].nodeID = 0;
559   CmiReduce(msg, sizeof(hostnameMsg)+sizeof(_procInfo), combineMessage);
560
561     // blocking here
562   while (done != CmiMyNodeSize())
563     CsdSchedulePoll();
564
565   if (CmiMyPe() == 0) {
566 #if CMK_BIGSIM_CHARM
567     if (BgNodeRank() == 0)
568 #endif
569       CmiPrintf("Charm++> cpu topology info is gathered in %.3f seconds.\n", CmiWallTimer()-startT);
570   }
571 #endif
572
573 #endif   /* __BIGSIM__ */
574
575   // now every one should have the node info
576   CcdRaiseCondition(CcdTOPOLOGY_AVAIL);      // call callbacks
577   if (CmiMyPe() == 0 && show_flag) cpuTopo.print();
578 }
579
580 #else           /* not supporting cpu topology */
581
582 extern "C" void LrtsInitCpuTopo(char **argv)
583 {
584   /* do nothing */
585   int obtain_flag = CmiGetArgFlagDesc(argv,"+obtain_cpu_topology",
586                                                 "obtain cpu topology info");
587   CmiGetArgFlagDesc(argv,"+skip_cpu_topology",
588                                "skip the processof getting cpu topology info");
589   CmiGetArgFlagDesc(argv,"+show_cpu_topology",
590                                            "Show cpu topology info");
591 }
592
593 #endif
594
595 extern "C" int CmiCpuTopologyEnabled()
596 {
597   return LrtsCpuTopoEnabled();
598 }
599 extern "C" int CmiPeOnSamePhysicalNode(int pe1, int pe2)
600 {
601   return LrtsPeOnSameNode(pe1, pe2);
602 }
603 extern "C" int CmiNumPhysicalNodes()
604 {
605   return LrtsNumNodes();
606 }
607 extern "C" int CmiNumPesOnPhysicalNode(int node)
608 {
609   return LrtsNodeSize(node);
610 }
611 extern "C" void CmiGetPesOnPhysicalNode(int node, int **pelist, int *num)
612 {
613   LrtsPeOnNode(node, pelist, num);
614 }
615 extern "C" int CmiPhysicalRank(int pe)
616 {
617   return LrtsRankOf(pe);
618 }
619 extern "C" int CmiPhysicalNodeID(int pe)
620 {
621   return LrtsNodeOf(pe);
622 }
623 extern "C" int CmiGetFirstPeOnPhysicalNode(int node)
624 {
625   return LrtsNodeFirst(node);
626 }
627 extern "C" void CmiInitCPUTopology(char **argv)
628 {
629   LrtsInitCpuTopo(argv);
630 }
631