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