04bbc538f4facddb2a8ca55da418c6e796be4e8f
[charm.git] / src / conv-core / cputopology.C
1 /*****************************************************************************
2  * $Source$
3  * $Author$
4  * $Date$
5  * $Revision$
6  *****************************************************************************/
7
8 #include "converse.h"
9 #include "sockRoutines.h"
10 #include "cklists.h"
11
12 #define DEBUGP(x)  /* CmiPrintf x;  */
13
14 /*
15  This scheme relies on using IP address to identify physical nodes 
16
17   written by Gengbin Zheng  9/2008
18 */
19 #if 1
20
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <unistd.h>
24
25 #include <stdlib.h>
26 #include <stdio.h>
27
28 #if CMK_BLUEGENEP
29 #include <dcmf.h>
30 #endif
31
32 #if defined(__APPLE__)  && CMK_HAS_MULTIPROCESSING_H
33 #include <Carbon/Carbon.h>
34 #include <Multiprocessing.h>
35 #endif
36
37 extern "C" int CmiNumCores(void) {
38   int a = 1;
39 #ifdef _WIN32
40 struct _SYSTEM_INFO sysinfo;
41 #endif  
42
43   /* Allow the user to override the number of CPUs for use
44      in scalability testing, debugging, etc. */
45   char *forcecount = getenv("VMDFORCECPUCOUNT");
46   if (forcecount != NULL) {
47     if (sscanf(forcecount, "%d", &a) == 1) {
48       return a; /* if we got a valid count, return it */
49     } else {
50       a = 1;      /* otherwise use the real available hardware CPU count */
51     }
52   }
53
54 #if defined(__APPLE__)  && CMK_HAS_MULTIPROCESSING_H
55   a = MPProcessorsScheduled(); /* Number of active/running CPUs */
56 #endif
57
58 #ifdef _WIN32
59   //struct _SYSTEM_INFO sysinfo;  
60   GetSystemInfo(&sysinfo);
61   a = sysinfo.dwNumberOfProcessors; /* total number of CPUs */
62 #endif /* _MSC_VER */
63
64
65 #ifdef _SC_NPROCESSORS_ONLN
66   a = sysconf(_SC_NPROCESSORS_ONLN); /* number of active/running CPUs */
67 #elif defined(_SC_CRAY_NCPU)
68   a = sysconf(_SC_CRAY_NCPU);
69 #elif defined(_SC_NPROC_ONLN)
70   a = sysconf(_SC_NPROC_ONLN); /* number of active/running CPUs */
71 #endif
72   if (a == -1) a = 1;
73
74 #if defined(ARCH_HPUX11) || defined(ARCH_HPUX10)
75   a = mpctl(MPC_GETNUMSPUS, 0, 0); /* total number of CPUs */
76 #endif /* HPUX */
77
78   return a;
79 }
80
81 static int cpuTopoHandlerIdx;
82 static int cpuTopoRecvHandlerIdx;
83
84 typedef struct _hostnameMsg {
85   char core[CmiMsgHeaderSizeBytes];
86   int pe;
87   skt_ip_t ip;
88   int ncores;
89   int rank;
90   int nodenum;
91 } hostnameMsg;
92
93 typedef struct _nodeTopoMsg {
94   char core[CmiMsgHeaderSizeBytes];
95   int *nodes;
96 } nodeTopoMsg;
97
98 static nodeTopoMsg *topomsg = NULL;
99 static CmmTable hostTable;
100
101 // nodenum[pe] is the node number of processor pe
102 class CpuTopology {
103 public:
104 static int *nodenum;
105 static int numNodes;
106 static CkVec<int> *bynodes;
107
108 int numUniqNodes() {
109                if (numNodes != 0) return numNodes;
110                int n = 0;
111                for (int i=0; i<CmiNumPes(); i++) 
112                  if (nodenum[i] > n) n = nodenum[i];
113                numNodes = n+1;
114                return numNodes;
115              }
116 void sort() {
117                int i;
118                numUniqNodes();
119                bynodes = new CkVec<int>[numNodes];
120                for (i=0; i<CmiNumPes(); i++) 
121                  bynodes[nodenum[i]].push_back(i);
122              }
123 void print() {
124                int i;
125                CmiPrintf("Charm++> Cpu topology info:\n");
126                for (i=0; i<CmiNumPes(); i++) CmiPrintf("%d ", nodenum[i]);
127                CmiPrintf("\n");
128                for (i=0; i<numNodes; i++) {
129                  CmiPrintf("Chip #%d: ", i);
130                  for (int j=0; j<bynodes[i].size(); j++) CmiPrintf("%d ", bynodes[i][j]);
131                  CmiPrintf("\n");
132                }
133              }
134 };
135
136 int *CpuTopology::nodenum = NULL;
137 int CpuTopology::numNodes = 0;
138 CkVec<int> *CpuTopology::bynodes = NULL;
139
140 static CpuTopology cpuTopo;
141 static CmiNodeLock topoLock;
142
143 /* called on PE 0 */
144 static void cpuTopoHandler(void *m)
145 {
146   static int count = 0;
147   static int nodecount = 0;
148   hostnameMsg *rec;
149   hostnameMsg *msg = (hostnameMsg *)m;
150   hostnameMsg *tmpm;
151   char str[128];
152   int tag, tag1, pe, myrank;
153   CmiAssert(topomsg != NULL);
154
155 /*   for debug
156   skt_print_ip(str, msg->ip);
157   printf("hostname: %d %s\n", msg->pe, str);
158 */
159   tag = *(int*)&msg->ip;
160   pe = msg->pe;
161   if ((rec = (hostnameMsg *)CmmProbe(hostTable, 1, &tag, &tag1)) != NULL) {
162     CmiFree(msg);
163   }
164   else {
165     msg->nodenum = nodecount++;
166     rec = msg;
167     CmmPut(hostTable, 1, &tag, msg);
168   }
169   myrank = rec->rank%rec->ncores;
170   topomsg->nodes[pe] = rec->nodenum;
171   rec->rank ++;
172   count ++;
173   if (count == CmiNumPes()) {
174     CmiPrintf("Charm++> %d unique compute nodes detected! \n", CmmEntries(hostTable));
175     //hostnameMsg *tmpm;
176     tag = CmmWildCard;
177     while (tmpm = (hostnameMsg *)CmmGet(hostTable, 1, &tag, &tag1)) CmiFree(tmpm);
178     CmmFree(hostTable);
179     CmiSyncBroadcastAllAndFree(sizeof(nodeTopoMsg)+CmiNumPes()*sizeof(int), (char *)topomsg);
180   }
181 }
182
183 /* called on each processor */
184 static void cpuTopoRecvHandler(void *msg)
185 {
186   int myrank;
187   nodeTopoMsg *m = (nodeTopoMsg *)msg;
188   m->nodes = (int *)((char*)m + sizeof(nodeTopoMsg));
189
190   CmiLock(topoLock);
191   if (cpuTopo.nodenum == NULL) {
192     cpuTopo.nodenum = m->nodes;
193     cpuTopo.sort();
194   }
195   else
196     CmiFree(m);
197   CmiUnlock(topoLock);
198
199   // if (CmiMyPe() == 0) cpuTopo.print();
200 }
201
202
203 extern "C" int CmiOnSamePhysicalNode(int pe1, int pe2)
204 {
205   int *nodenum = cpuTopo.nodenum;
206   return nodenum==NULL?-1:nodenum[pe1] == nodenum[pe2];
207 }
208
209 extern "C" int CmiNumPhysicalNodes()
210 {
211   return cpuTopo.numUniqNodes();
212 }
213
214 extern "C" int CmiNumPesOnPhysicalNode(int pe)
215 {
216   return cpuTopo.bynodes==NULL?-1:cpuTopo.bynodes[cpuTopo.nodenum[pe]].size();
217 }
218
219 extern "C" void CmiGetPesOnPhysicalNode(int pe, int **pelist, int *num)
220 {
221   CmiAssert(pe >=0 && pe < CmiNumPes());
222   *num = cpuTopo.numUniqNodes();
223   if (pelist!=NULL && *num>0) *pelist = cpuTopo.bynodes[cpuTopo.nodenum[pe]].getVec();
224 }
225
226 #if CMK_CRAYXT
227 extern "C" int getXTNodeID(int mype, int numpes);
228 #endif
229
230 extern "C" void CmiInitCPUTopology(char **argv)
231 {
232   static skt_ip_t myip;
233   int ret, i;
234   hostnameMsg  *msg;
235  
236   if (CmiMyRank() ==0) {
237      topoLock = CmiCreateLock();
238   }
239
240   int obtain_flag = CmiGetArgFlagDesc(argv,"+obtain_cpu_topology",
241                                                 "obtain cpu topology info");
242   obtain_flag = 1;
243
244   cpuTopoHandlerIdx =
245      CmiRegisterHandler((CmiHandler)cpuTopoHandler);
246   cpuTopoRecvHandlerIdx =
247      CmiRegisterHandler((CmiHandler)cpuTopoRecvHandler);
248
249   if (!obtain_flag) return;
250   else if (CmiMyPe() == 0) {
251      CmiPrintf("Charm++> cpu topology info is being gathered! \n");
252   }
253
254   if (CmiMyPe() >= CmiNumPes()) {
255       /* comm thread either can float around, or pin down to the last rank.
256          however it seems to be reportedly slower if it is floating */
257     CmiNodeAllBarrier();
258     return;    /* comm thread return */
259   }
260
261 #if 0
262   if (gethostname(hostname, 999)!=0) {
263       strcpy(hostname, "");
264   }
265 #endif
266
267     /* get my ip address */
268   if (CmiMyRank() == 0)
269   {
270 #if CMK_BLUEGENEP
271     //CmiAbort("Can not get unique name for the compute nodes. \n");
272     ret = DCMF_Messager_rank();
273     memcpy(&myip, &ret, sizeof(int));
274 #elif CMK_CRAYXT
275     ret = getXTNodeID(CmiMyPe(), CmiNumPes());
276     memcpy(&myip, &ret, sizeof(int));
277 #elif CMK_HAS_GETHOSTNAME
278     myip = skt_my_ip();        /* not thread safe, so only calls on rank 0 */
279 #else
280     CmiAbort("Can not get unique name for the compute nodes. \n");
281 #endif
282   }
283   CmiNodeAllBarrier();
284
285     /* prepare a msg to send */
286   msg = (hostnameMsg *)CmiAlloc(sizeof(hostnameMsg));
287   CmiSetHandler((char *)msg, cpuTopoHandlerIdx);
288   msg->pe = CmiMyPe();
289   msg->ip = myip;
290   msg->ncores = CmiNumCores();
291   msg->rank = 0;
292   CmiSyncSendAndFree(0, sizeof(hostnameMsg), (char *)msg);
293
294   if (CmiMyPe() == 0) {
295     int i;
296     hostTable = CmmNew();
297     topomsg = (nodeTopoMsg *)CmiAlloc(sizeof(nodeTopoMsg)+CmiNumPes()*sizeof(int));
298     CmiSetHandler((char *)topomsg, cpuTopoRecvHandlerIdx);
299     topomsg->nodes = (int *)((char*)topomsg + sizeof(nodeTopoMsg));
300     for (i=0; i<CmiNumPes(); i++) topomsg->nodes[i] = -1;
301     CsdScheduleCount(CmiNumPes());
302   }
303
304     // receive broadcast from PE 0
305   CsdScheduleCount(1);
306
307     // now every one should have the node info
308 }
309
310 #else           /* not supporting affinity */
311
312
313 extern "C" void CmiInitCPUTopology(char **argv)
314 {
315   /* do nothing */
316   int obtain_flag = CmiGetArgFlagDesc(argv,"+obtain_cpu_topology",
317                                                 "obtain cpu topology info");
318 }
319
320 #endif