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