4fcce5aae791a848f7266360ccfba70f2dc78b8d
[charm.git] / tests / charm++ / kNeighbor / kNeighbor.C
1 #include "kNeighbor.decl.h"
2 #include <stdio.h>
3 #include <stdlib.h>
4
5 #define STRIDEK 3
6 #define CALCPERSTEP 100
7 #define WRAPROUND 1
8 #define ALLCROSSNODE 0
9 #define BLOCKMAPPING 1
10 #define USE_ARRAY_REDUCTION 0
11
12 #define DEBUG 0
13 #define REUSE_ITER_MSG 0
14 #define TOUCH_MSGDATA 0
15
16 CProxy_Main mainProxy;
17 int gMsgSize;
18
19 class toNeighborMsg: public CMessage_toNeighborMsg {
20 public:
21     int *data;
22     int size;
23     int fromX;
24     int nID;
25
26 public:
27     toNeighborMsg() {};
28     toNeighborMsg(int s): size(s) {  
29 #if TOUCH_MSGDATA
30         init(); 
31 #endif
32     }
33
34     void setMsgSrc(int X, int id) {
35         fromX = X;
36         nID = id;
37     }
38     void init() {
39         for (int i=0; i<size; i++)
40           data[i] = i;
41     }
42     int sum() {
43         int s=0;
44         for (int i=0; i<size; i++)
45           s += data[i];
46         return s;
47     }
48 };
49
50 //#define MSGSIZECNT 1
51
52 int cmpFunc(const void *a, const void *b){
53    if(*(double *)a < *(double *)b) return -1;
54    if(*(double *)a > *(double *)b) return 1;
55    return 0;
56    
57 }
58
59 class Main: public CBase_Main {
60 public:
61     CProxy_Block array;
62
63     //static int msgSizeArr[MSGSIZECNT];
64
65     int numSteps;
66     int currentStep;
67     int currentMsgSize;
68     int totalElems;
69
70     int elementsRecved;
71     double totalTime;
72     double maxTime;
73     double minTime;
74     double *timeRec;
75
76     double gStarttime;
77
78 public:
79     Main(CkArgMsg *m) {
80         mainProxy = thisProxy;
81
82         if (m->argc!=4) {
83             CkPrintf("Usage: %s <#elements> <#iterations> <msg size>\n", m->argv[0]);
84             delete m;
85             CkExit();
86         }
87
88         int numElems = atoi(m->argv[1]);
89         if(numElems < CkNumPes()){
90                 printf("Warning: #elements is forced to be euqal to #pes\n");
91                 numElems = CkNumPes();
92         }
93
94         numSteps = atoi(m->argv[2]);
95
96         currentMsgSize = atoi(m->argv[3]);
97
98         #if REUSE_ITER_MSG
99         gMsgSize = currentMsgSize;
100         #endif
101
102         currentStep = -1;
103
104         totalElems = numElems;
105         timeRec = new double[numSteps];
106
107         CProxy_MyMap myMap = CProxy_MyMap::ckNew(totalElems);
108         CkArrayOptions opts(totalElems);
109         opts.setMap(myMap);
110         array = CProxy_Block::ckNew(totalElems, opts);
111
112         CkCallback *cb = new CkCallback(CkIndex_Main::nextStep(NULL), thisProxy);
113         array.ckSetReductionClient(cb);
114
115         beginIteration();
116     }
117
118     void beginIteration() {
119         currentStep++;
120         if (currentStep==numSteps) {
121             CkPrintf("kNeighbor program finished!\n");
122             //CkCallback *cb = new CkCallback(CkIndex_Main::terminate(NULL), thisProxy);
123             //array.ckSetReductionClient(cb);
124             //array.printSts(numSteps);
125             terminate(NULL);
126             return;
127             //CkExit();
128         }
129
130         elementsRecved = 0;
131         totalTime = 0.0;
132         maxTime = 0.0;
133         minTime = 3600.0;
134
135         //int msgSize = msgSizeArr[currentStep%MSGSIZECNT];
136         //int msgSize = msgSizeArr[rand()%MSGSIZECNT];
137         //currentMsgSize = msgSize;
138
139         gStarttime = CmiWallTimer();
140 #if REUSE_ITER_MSG
141         for (int i=0; i<totalElems; i++)
142             array(i).commWithNeighbors();
143 #else
144         for (int i=0; i<totalElems; i++)
145             array(i).commWithNeighbors(currentMsgSize);
146 #endif  
147         //array.commWithNeighbors(currentMsgSize);
148     }
149
150     void terminate(CkReductionMsg  *msg){
151         delete msg;
152         double total = 0.0;
153         for (int i=0; i<numSteps; i++) timeRec[i] = timeRec[i]*1e6;
154         qsort(timeRec, numSteps, sizeof(double), cmpFunc);
155         printf("Time stats: lowest: %f, median: %f, highest: %f\n", timeRec[0], timeRec[numSteps/2], timeRec[numSteps-1]);
156         int samples = 100;
157         if(numSteps<=samples) samples = numSteps-1;
158         for (int i=0; i<samples; i++) total += timeRec[i];
159         total /= samples;
160         CkPrintf("The average time for each %d-kNeighbor iteration with msg size %d is %f (us)\n", STRIDEK, currentMsgSize, total);
161         CkExit();
162     }
163
164     void nextStep_plain(double iterTime) {
165         elementsRecved++;
166         totalTime += iterTime;
167         maxTime = maxTime>iterTime?maxTime:iterTime;
168         minTime = minTime<iterTime?minTime:iterTime;
169
170         if (elementsRecved == totalElems) {
171             double wholeStepTime = CmiWallTimer() - gStarttime;
172             timeRec[currentStep] = wholeStepTime/CALCPERSTEP;
173             //CkPrintf("Step %d with msg size %d finished: max=%f, total=%f\n", currentStep, currentMsgSize, maxTime/CALCPERSTEP, wholeStepTime/CALCPERSTEP);
174
175             beginIteration();
176         }
177     }
178
179     void nextStep(CkReductionMsg  *msg) {
180         maxTime = *((double *)msg->getData());
181         delete msg;
182         double wholeStepTime = CmiWallTimer() - gStarttime;
183         timeRec[currentStep] = wholeStepTime/CALCPERSTEP;
184         //CkPrintf("Step %d with msg size %d finished: max=%f, total=%f\n", currentStep, currentMsgSize, maxTime/CALCPERSTEP, wholeStepTime/CALCPERSTEP);
185         beginIteration();
186     }
187
188 };
189
190 //int Main::msgSizeArr[MSGSIZECNT] = {16, 32, 128, 256, 512, 1024, 2048, 4096};
191 //int Main::msgSizeArr[MSGSIZECNT] = {10000};
192
193
194 class MyMap : public CkArrayMap{
195 private:
196     int totalElems;
197 public:
198     MyMap(int n) {
199         totalElems = n;
200     }
201     MyMap(CkMigrateMessage *m){}
202     /*int registerArray(CkArrayMapRegisterMessage *m){
203         delete m;
204         return 0;
205     }*/
206     int procNum(int arrayHdl, const CkArrayIndex &idx){
207         int elem = *(int *)idx.data();
208         int penum;
209 #if BLOCKMAPPING
210         int blkSize = totalElems/CkNumPes();
211         penum = (elem/blkSize)%CkNumPes();
212 #elif NODECYCLICMAPPING
213         int nid = (elem/CkMyNodeSize())%CkNumNodes();
214         int cid = elem % CkMyNodeSize();
215         penum = CkNodeFirst(nid)+cid; 
216 #else
217         //Default is RoundRobin Mapping
218         penum = elem%CkNumPes();
219 #endif
220         return penum;
221     }
222 };
223
224 //#define WORKSIZECNT 5
225 #define WORKSIZECNT 1
226 //no wrap around for sending messages to neighbors
227 class Block: public CBase_Block {
228 public:
229     /** actual work size is of workSize^3 */
230     static int workSizeArr[WORKSIZECNT];
231     int totalElems;
232
233     int numNeighbors;
234     int neighborsRecved;
235     int *neighbors;
236     double *recvTimes;
237
238     double startTime;
239
240     int random;
241
242     int curIterMsgSize;
243     int curIterWorkSize;
244     int internalStepCnt;
245
246     int sum;
247
248 #if REUSE_ITER_MSG
249     toNeighborMsg **iterMsg;
250 #endif
251
252 public:
253     Block(int numElems) {
254         //srand(thisIndex.x+thisIndex.y);
255
256         totalElems = numElems;
257
258 #if WRAPROUND
259         numNeighbors = 2*STRIDEK;
260         neighbors = new int[numNeighbors];
261         recvTimes = new double[numNeighbors];
262         int nidx=0;
263         //setting left neighbors
264         for (int i=thisIndex-STRIDEK; i<thisIndex; i++, nidx++) {
265             int tmpnei = i;
266             while (tmpnei<0) tmpnei += totalElems;
267             neighbors[nidx] = tmpnei;
268         }
269         //setting right neighbors
270         for (int i=thisIndex+1; i<=thisIndex+STRIDEK; i++, nidx++) {
271             int tmpnei = i;
272             while (tmpnei>=totalElems) tmpnei -= totalElems;
273             neighbors[nidx] = tmpnei;
274         }
275 #elif ALLCROSSNODE
276         if(CkNumNodes()==1){
277             if(thisIndex==0){
278                 CkPrintf("This version has to run with more than 2 nodes!\n");
279                 CkExit();
280             }
281             return;
282         }
283         numNeighbors = CkNumNodes()-1;
284         neighbors = new int[numNeighbors];
285         recvTimes = new double[numNeighbors];
286         for(int i=0; i<numNeighbors; i++){
287             neighbors[i] = (thisIndex+(i+1)*CmiMyNodeSize())%CkNumPes();
288         }
289 #else
290         //calculate the neighbors this element has
291         numNeighbors = 0;
292         numNeighbors += thisIndex - MAX(0, thisIndex-STRIDEK); //left
293         numNeighbors += MIN(totalElems-1, thisIndex+STRIDEK)-thisIndex; //right
294         neighbors = new int[numNeighbors];
295         recvTimes = new double[numNeighbors];
296         int nidx=0;
297         for (int i=MAX(0, thisIndex-STRIDEK); i<thisIndex; i++, nidx++) neighbors[nidx]=i;
298         for (int i=thisIndex+1; i<=MIN(totalElems-1, thisIndex+STRIDEK); i++, nidx++) neighbors[nidx] = i;
299 #endif
300
301         for (int i=0; i<numNeighbors; i++)
302             recvTimes[i] = 0.0;
303
304 #if REUSE_ITER_MSG
305         iterMsg = new toNeighborMsg *[numNeighbors];
306         for (int i=0; i<numNeighbors; i++)
307             iterMsg[i] = NULL;  
308 #endif
309
310 #if DEBUG
311         CkPrintf("Neighbors of %d: ", thisIndex);
312         for (int i=0; i<numNeighbors; i++)
313             CkPrintf("%d ", neighbors[i]);
314         CkPrintf("\n");
315 #endif
316
317         //CkPrintf("Elem [%d] on proc %d (node=%d, rank=%d)\n", thisIndex, CkMyPe(), CkMyNode(), CkMyRank());
318         random = thisIndex*31+73;
319     }
320
321     ~Block() {
322         delete [] neighbors;
323         delete [] recvTimes;
324 #if REUSE_ITER_MSG
325         delete [] iterMsg;
326 #endif
327     }
328
329     Block(CkMigrateMessage *m) {}
330
331     void printSts(int totalSteps){
332         /*for(int i=0; i<numNeighbors; i++){
333                 CkPrintf("Elem[%d]: avg RTT from neighbor %d (actual elem id %d): %lf\n", thisIndex, i, neighbors[i], recvTimes[i]/totalSteps);
334         }*/
335         contribute(0,0,CkReduction::max_int);
336     }
337
338     void startInternalIteration() {
339 #if DEBUG
340         CkPrintf("[%d]: Start internal iteration \n", thisIndex);
341 #endif
342
343         neighborsRecved = 0;
344 #ifdef DOCOMP
345         //1: pick a work size and do some computation
346         int sum=0;
347         int N=curIterWorkSize;
348         for (int i=0; i<N; i++)
349             for (int j=0; j<N; j++)
350                 for (int k=0; k<N; k++)
351                     sum += (thisIndex*i+thisIndex*j+k)%WORKSIZECNT;
352 #endif
353         //2. send msg to K neighbors
354         int msgSize = curIterMsgSize;
355
356         //Send msgs to neighbors
357         for (int i=0; i<numNeighbors; i++) {
358             //double memtimer = CmiWallTimer();
359
360 #if REUSE_ITER_MSG
361             toNeighborMsg *msg = iterMsg[i];
362 #else
363             toNeighborMsg *msg = new(msgSize/4, 0) toNeighborMsg(msgSize/4);
364 #endif
365
366 #if DEBUG
367             CkPrintf("[%d]: send msg to neighbor[%d]=%d\n", thisIndex, i, neighbors[i]);
368 #endif
369             msg->setMsgSrc(thisIndex, i);
370             //double entrytimer = CmiWallTimer();
371             thisProxy(neighbors[i]).recvMsgs(msg);
372             //double entrylasttimer = CmiWallTimer();
373             //if(thisIndex==0){
374             //  CkPrintf("At current step %d to neighbor %d, msg creation time: %f, entrymethod fire time: %f\n", internalStepCnt, neighbors[i], entrytimer-memtimer, entrylasttimer-entrytimer);
375             //}
376         }
377     }
378
379     void commWithNeighbors(int msgSize) {
380         internalStepCnt = 0;
381         curIterMsgSize = msgSize;
382         //currently the work size is only changed every big steps (which
383         //are initiated by the main proxy
384         curIterWorkSize = workSizeArr[random%WORKSIZECNT];
385         random++;
386
387         startTime = CmiWallTimer();
388         startInternalIteration();
389     }
390
391     void commWithNeighbors() {
392         internalStepCnt = 0;
393         curIterMsgSize = gMsgSize;
394         //currently the work size is only changed every big steps (which
395         //are initiated by the main proxy
396         curIterWorkSize = workSizeArr[random%WORKSIZECNT];
397         random++;
398         
399 #if REUSE_ITER_MSG
400         if(iterMsg[0]==NULL){ //indicating the messages have not been created
401             for(int i=0; i<numNeighbors; i++)
402                 iterMsg[i] = new(curIterMsgSize/4, 0) toNeighborMsg(curIterMsgSize/4);
403         }
404 #endif
405         
406         startTime = CmiWallTimer();
407         startInternalIteration();
408     }
409
410     void recvReplies(toNeighborMsg *m) {
411         int fromNID = m->nID;
412
413 #if DEBUG
414         CkPrintf("[%d]: receive ack from neighbor[%d]=%d\n", thisIndex, fromNID, neighbors[fromNID]);
415 #endif
416
417 #if REUSE_ITER_MSG
418         iterMsg[fromNID] = m;
419 #else
420         delete m;
421 #endif
422         //recvTimes[fromNID] += (CmiWallTimer() - startTime);
423
424         //get one step time and send it back to mainProxy
425         neighborsRecved++;
426         if (neighborsRecved == numNeighbors) {
427             internalStepCnt++;
428             if (internalStepCnt==CALCPERSTEP) {
429                 double iterCommTime = CmiWallTimer() - startTime;
430             #if USE_ARRAY_REDUCTION
431                 contribute(sizeof(double), &iterCommTime, CkReduction::max_double);
432             #else
433                 mainProxy.nextStep_plain(iterCommTime);
434             #endif
435                 /*if(thisIndex==0){
436                         for(int i=0; i<numNeighbors; i++){
437                                 CkPrintf("RTT time from neighbor %d (actual elem id %d): %lf\n", i, neighbors[i], recvTimes[i]);
438                         }
439                 }*/
440             } else {
441                 startInternalIteration();
442             }
443         }
444     }
445
446     void recvMsgs(toNeighborMsg *m) {
447 #if DEBUG
448         CkPrintf("[%d]: recv msg from %d as its %dth neighbor\n", thisIndex, m->fromX, m->nID);
449 #endif
450
451 #if TOUCH_MSGDATA
452         sum = m->sum();
453 #endif
454         thisProxy(m->fromX).recvReplies(m);
455     }
456
457     inline int MAX(int a, int b) {
458         return (a>b)?a:b;
459     }
460     inline int MIN(int a, int b) {
461         return (a<b)?a:b;
462     }
463 };
464
465 //int Block::workSizeArr[WORKSIZECNT] = {20, 60, 120, 180, 240};
466 int Block::workSizeArr[WORKSIZECNT] = {20};
467
468 #include "kNeighbor.def.h"