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