Refactoring control point and critical path detection code.
[charm.git] / src / ck-cp / arrayRedistributor.h
1 /** 
2
3     A system for exposing application and runtime "control points" 
4     to the dynamic optimization framework.
5
6 */
7 #ifndef __ARRAYREDISTRIBUTOR_H__
8 #define __ARRAYREDISTRIBUTOR_H__
9
10 #include <vector>
11 #include <map>
12 #include <cmath>
13 #include "ControlPoints.decl.h"
14
15 #include<pup_stl.h>
16
17
18
19 /**
20  * \addtogroup ControlPointFramework
21  *   @{
22  */
23
24
25 /// A message containing a chunk of a data array used when redistributing to a different set of active chares 
26 class redistributor2DMsg : public CMessage_redistributor2DMsg { 
27  public: 
28   int top;         
29   int left; 
30   int height;      
31   int width; 
32   int new_chare_cols; 
33   int  new_chare_rows; 
34   int which_array; 
35   double *data;
36 }; 
37  
38
39
40 /// Integer Maximum
41 static int maxi(int a, int b){
42   if(a>b)
43     return a;
44   else
45     return b;
46 }
47
48 /// Integer Minimum
49 static int mini(int a, int b){
50   if(a<b)
51     return a;
52   else
53     return b;
54 }
55
56
57 /// A chare group that can redistribute user data arrays. It is used by binding it to a user's Chare Array
58 class redistributor2D: public CBase_redistributor2D {
59  public:
60
61   std::map<int,double*> data_arrays;
62   std::map<int,int> data_arrays_sizes;
63
64   /// The array associated with this data redistribution
65   CProxyElement_ArrayElement associatedArray;
66   
67   int incoming_count;
68   std::map<int,double*> data_arrays_incoming;
69   std::map<int,int> data_arrays_incoming_sizes;
70
71   /// Is this array element active
72   bool thisElemActive;
73
74  private:
75
76
77   void *fakeMemoryUsage;
78
79
80   CkCallback dataRedistributedCallback;
81
82   int x_chares; // number of active chares in x dimension
83   int y_chares; // number of active chares in y dimension
84
85   int data_width;  // The width of the global array, not the local piece
86   int data_height; // The height of the global array, not the local piece
87
88   int data_x_ghost; // The padding in the x dimension on each side of the data
89   int data_y_ghost; // The padding in the y dimension on each side of the data
90
91
92  public:
93
94   void pup(PUP::er &p) {
95     CBase_redistributor2D::pup(p);
96
97     p | data_arrays_sizes;
98     p | data_arrays_incoming_sizes;
99     p | incoming_count;
100     p | associatedArray;
101     p | thisElemActive;
102
103     p | dataRedistributedCallback;
104
105     p | x_chares;
106     p | y_chares;
107     p | data_width;
108     p | data_height;
109     p | data_x_ghost;
110     p | data_y_ghost;
111
112     if(p.isPacking() && fakeMemoryUsage!=NULL)
113       free(fakeMemoryUsage);
114
115     fakeMemoryUsage = NULL;
116
117     ////////////////////////////////
118     // when packing, iterate through data_arrays
119     // when unpacking
120
121     {
122       std::map<int,int>::iterator iter;
123       for(iter = data_arrays_sizes.begin(); iter != data_arrays_sizes.end(); iter++){
124         int whichArray = iter->first;
125         int arraySize = iter->second;
126
127         //      CkPrintf("Pupping data array %d\n",whichArray);
128         p | whichArray;
129
130         if(p.isUnpacking())
131           data_arrays[whichArray] = new double[arraySize];
132
133         PUParray(p,data_arrays[whichArray] ,arraySize);
134         
135         if(p.isPacking())
136           delete[] data_arrays[whichArray];
137         
138       }
139     }
140
141
142     ///////////////////////////////
143     {
144       std::map<int,int>::iterator iter;
145       for(iter = data_arrays_incoming_sizes.begin(); iter != data_arrays_incoming_sizes.end(); iter++){
146         int whichArray = iter->first;
147         int arraySize = iter->second;
148
149         //      CkPrintf("Pupping incoming array %d\n",whichArray);
150         p | whichArray;
151
152         if(p.isUnpacking() && data_arrays_incoming_sizes[whichArray] > 0)
153           data_arrays_incoming[whichArray] = new double[arraySize];
154         
155         PUParray(p,data_arrays_incoming[whichArray],arraySize);
156         
157         if(p.isPacking())
158           delete[] data_arrays_incoming[whichArray];
159         
160       }
161     }
162
163     //    CkPrintf("pup redistributor2D\n");
164
165   } 
166
167
168   void ckJustMigrated(){
169     //   CkPrintf("redistributor element %02d %02d migrated to %d", thisIndex.x, thisIndex.y, CkMyPe());
170   }
171
172
173   // ------------ Some routines for computing the array bounds for this chare  ------------ 
174
175   // The index in the global array for my top row  
176   int top_data_idx();
177  
178   int bottom_data_idx();
179  
180   int left_data_idx();
181  
182   int right_data_idx();
183  
184   int top_neighbor();
185    
186   int bottom_neighbor();
187    
188   int left_neighbor();
189  
190   int right_neighbor();
191   
192   
193   /// the width of the non-ghost part of the local partition 
194   int mywidth();
195     
196     
197   // the height of the non-ghost part of the local partition 
198   int myheight();
199     
200
201
202   // ------------ Some routines for computing the array bounds for arbitrary chares  ------------ 
203
204   int top_data_idx(int y, int y_total){
205     return (data_height * y) / y_total;
206   }
207
208   int bottom_data_idx(int y, int y_total){
209     return ((data_height * (y+1)) / y_total) - 1;
210   }
211
212   int left_data_idx(int x, int x_total){
213     return (data_width * x) / x_total;
214   }
215
216   int right_data_idx(int x, int x_total){
217     return ((data_width * (x+1)) / x_total) - 1;
218   }
219
220
221   int top_data_idx(int y){
222     return (data_height * y) / y_chares;
223   }
224
225   int bottom_data_idx(int y){
226     return ((data_height * (y+1)) / y_chares) - 1;
227   }
228
229   int left_data_idx(int x){
230     return (data_width * x) / x_chares;
231   }
232
233   int right_data_idx(int x){
234     return ((data_width * (x+1)) / x_chares) - 1;
235   }
236
237   /// Return which chare array element(x index) owns the global data item i
238   int who_owns_idx_x(int i){
239     int w=0;
240     while(1){
241       if( i >= left_data_idx(w) && i <= right_data_idx(w) ){
242         return w;
243       }
244       w++;
245     }
246   }
247   
248   /// Return which chare array element(y index) owns the global data item i
249   int who_owns_idx_y(int i){
250     int w=0;
251     while(1){
252       if( i >= top_data_idx(w) && i <= bottom_data_idx(w) ){
253         return w;
254       }
255       w++;
256     }
257   }
258   
259
260
261
262   
263   // Convert a local column,row id (0 to mywidth-1, 0 to data_width-1) to the index in the padded array
264   int local_to_padded(int x, int y){
265     return (mywidth()+2*data_x_ghost)*(y+data_y_ghost)+x+data_x_ghost;
266   }
267
268   // get a data value
269   double data_local(int which, int x, int y){
270     CkAssert(local_to_padded(x,y) < data_arrays_sizes[which]);
271     return data_arrays[which][local_to_padded(x,y)];
272   }
273
274
275   // Convert a local column id (0 to mywidth-1) to the global column id (0 to data_width-1)
276   int local_to_global_x(int x){
277     return left_data_idx() + x;
278   }
279
280   // Convert a local row id (0 to myheight-1) to the global row id (0 to data_height-1)
281   int local_to_global_y(int y){
282     return top_data_idx() + y;
283   }
284
285   int global_array_width(){
286     return data_width;
287   }
288
289   int global_array_height(){
290     return data_height;
291   }
292
293   int global_array_size(){
294     return global_array_width() * global_array_height();
295   }
296
297   int my_array_width(){
298     return mywidth()+2*data_x_ghost;
299   }
300
301   int my_array_height(){
302     return myheight()+2*data_y_ghost;
303   }
304
305   // Total size of arrays including ghost layers
306   int my_array_size(){
307     return my_array_width() * my_array_height();
308   }
309
310   /// Create an array. If multiple arrays are needed, each should have its own index
311   template <typename t> t* createDataArray(int which=0) {
312     t* data = new t[my_array_size()];
313     data_arrays[which] = data;
314     data_arrays_sizes[which] = my_array_size();
315
316     if(thisIndex.x==0 && thisIndex.y==0)  
317       CkPrintf("data_arrays_sizes[which] set to %d\n", data_arrays_sizes[which] );  
318
319
320     CkAssert(data_arrays[which] != NULL);
321 #if DEBUG > 2
322     CkPrintf("Allocated array of size %d at %p\n", my_array_size(), data_arrays[which] );
323 #endif
324     return data;
325   }
326   
327   template <typename t> t* getDataArray(int which=0) {
328     return data_arrays[which]; 
329   }
330
331   /// Constructor takes in the dimensions of the array, including any desired ghost layers
332   /// The local part of the arrays will have (mywidth+x_ghosts*2)*(myheight+y_ghosts*2) elements 
333   void setInitialDimensions(int width, int height, int x_chares_, int y_chares_, int x_ghosts=0, int y_ghosts=0){
334     data_width = width;      // These values cannot change after this method is called.
335     data_height = height;
336     data_x_ghost = x_ghosts;
337     data_y_ghost = y_ghosts;
338     
339     setDimensions(x_chares_, y_chares_);
340   }
341   
342
343   void setDimensions( int x_chares_, int y_chares_){
344     x_chares = x_chares_;
345     y_chares = y_chares_;
346     
347     
348     if( thisIndex.x < x_chares && thisIndex.y < y_chares ){
349       thisElemActive = true;
350     } else {
351       thisElemActive = false;
352     }
353     
354   }
355
356
357   redistributor2D(){
358     incoming_count = 0;
359     fakeMemoryUsage = NULL;
360   }
361
362
363   redistributor2D(CkMigrateMessage*){
364   }
365
366
367   void startup(){
368 #if DEBUG > 3 
369    CkPrintf("redistributor 2D startup %03d,%03d\n", thisIndex.x, thisIndex.y);
370 #endif
371     contribute();
372   }
373   
374
375   void printArrays(){
376 #if DEBUG > 2
377     CkAssert(data_arrays.size()==2);
378     for(std::map<int,double*>::iterator diter = data_arrays.begin(); diter != data_arrays.end(); diter++){
379       int which_array = diter->first;
380       double *data = diter->second;
381       CkPrintf("%d,%d data_arrays[%d] = %p\n", thisIndex.x, thisIndex.y, which_array, data);
382     }
383 #endif
384   }
385
386   
387   // Called on all elements involved with the new granularity or containing part of the old data
388   void resizeGranules(int new_active_chare_cols, int new_active_chare_rows){
389
390 #if DEBUG>1
391     CkPrintf("Resize Granules called for elem %d,%d\n", thisIndex.x, thisIndex.y);      
392 #endif
393
394     const bool previouslyActive = thisElemActive;
395     const int old_top = top_data_idx();
396     const int old_left = left_data_idx();
397     const int old_bottom = top_data_idx()+myheight()-1;
398     const int old_right = left_data_idx()+mywidth()-1;
399     const int old_myheight = myheight();
400     const int old_mywidth = mywidth();
401
402     setDimensions(new_active_chare_cols, new_active_chare_rows);
403     
404     const int new_mywidth = mywidth();
405     const int new_myheight = myheight();
406
407     // Transpose Data
408     // Assume only one new owner of my data
409
410     if(previouslyActive){
411      
412       // Send all my data to any blocks that will need it
413
414       int newOwnerXmin = who_owns_idx_x(old_left);
415       int newOwnerXmax = who_owns_idx_x(old_right);
416       int newOwnerYmin = who_owns_idx_y(old_top);
417       int newOwnerYmax = who_owns_idx_y(old_bottom);
418
419       for(int newx=newOwnerXmin; newx<=newOwnerXmax; newx++){
420         for(int newy=newOwnerYmin; newy<=newOwnerYmax; newy++){
421           
422           // Determine overlapping region between my data and this destination
423 #if DEBUG > 2
424           CkPrintf("newy(%d)*new_myheight(%d)=%d, old_top=%d\n",newy,new_myheight,newy*new_myheight,old_top);
425 #endif
426           // global range for overlapping area
427           int global_top = maxi(top_data_idx(newy),old_top);
428           int global_left = maxi(left_data_idx(newx),old_left);
429           int global_bottom = mini(bottom_data_idx(newy),old_bottom);
430           int global_right = mini(right_data_idx(newx),old_right);
431           int w = global_right-global_left+1;
432           int h = global_bottom-global_top+1;
433          
434           CkAssert(w*h>0);
435
436           int x_offset = global_left - old_left;
437           int y_offset = global_top - old_top;
438
439 #if DEBUG > 2     
440           CkPrintf("w=%d h=%d x_offset=%d y_offset=%d\n", w, h, x_offset, y_offset);
441 #endif
442           
443           std::map<int,double*>::iterator diter;
444           for(diter =data_arrays.begin(); diter != data_arrays.end(); diter++){
445             
446             redistributor2DMsg* msg = new(w*h) redistributor2DMsg;  
447             //      CkPrintf("Created message msg %p\n", msg);  
448             
449             int which_array = diter->first;
450             double *t = diter->second;
451             int s = data_arrays_sizes[which_array];
452             
453             for(int j=0; j<h; j++){
454               for(int i=0; i<w; i++){           
455                 CkAssert(j*w+i < w*h);
456                 CkAssert((data_x_ghost*2+old_mywidth)*(j+y_offset+data_y_ghost)+(i+ x_offset+data_x_ghost) < s);
457                 msg->data[j*w+i] = t[(data_x_ghost*2+old_mywidth)*(j+y_offset+data_y_ghost)+(i+ x_offset+data_x_ghost)];
458               }
459             }
460             
461             msg->top = global_top;
462             msg->left = global_left;
463             msg->height = h;
464             msg->width = w;
465             msg->new_chare_cols = new_active_chare_cols;
466             msg->new_chare_rows = new_active_chare_rows; 
467             msg->which_array = which_array;
468
469             //      CkPrintf("Sending message msg %p\n", msg);      
470             thisProxy(newx, newy).receiveTransposeData(msg);
471             
472           }
473           
474         }
475         
476         
477       }
478     } 
479     
480     if(!thisElemActive){
481 #if DEBUG > 2
482       CkPrintf("Element %d,%d is no longer active\n", thisIndex.x, thisIndex.y);
483 #endif
484
485       // Free my arrays
486       for(std::map<int,double*>::iterator diter = data_arrays.begin(); diter != data_arrays.end(); diter++){
487         int which_array = diter->first;
488         delete data_arrays[which_array]; 
489         data_arrays[which_array] = NULL;
490         data_arrays_sizes[which_array] = 0;
491       }
492       continueToNextStep();
493       
494     }
495
496     int newPe = (thisIndex.y * new_active_chare_cols + thisIndex.x) % CkNumPes();
497     
498     if(newPe == CkMyPe()){
499       //      CkPrintf("Keeping %02d , %02d on PE %d\n", thisIndex.x, thisIndex.y, newPe);
500     }
501     else{
502       // CkPrintf("Migrating %02d , %02d to PE %d\n", thisIndex.x, thisIndex.y, newPe);
503       migrateMe(newPe);
504     }
505     
506   }
507   
508
509   void continueToNextStep(){
510 #if DEBUG > 2
511     CkPrintf("Elem %d,%d is ready to continue\n", thisIndex.x, thisIndex.y);
512 #endif
513     for(std::map<int,double*>::iterator diter =data_arrays.begin(); diter != data_arrays.end(); diter++){
514       int which_array = diter->first;
515       double *data = diter->second;
516       CkAssert( (data==NULL && !thisElemActive) || (data!=NULL && thisElemActive) );
517     }
518
519
520 #if USE_EXTRAMEMORY
521 #error NO USE_EXTRAMEMORY ALLOWED YET
522     if(thisElemActive){
523
524       long totalArtificialMemory = controlPoint("Artificial Memory Usage", 100, 500);
525       long artificialMemoryPerChare = totalArtificialMemory *1024*1024 / x_chares / y_chares;
526       
527       CkPrintf("Allocating fake memory of %d MB (of the total %d MB) (xchares=%d y_chares=%d)\n", artificialMemoryPerChare/1024/1024, totalArtificialMemory, x_chares, y_chares);
528       free(fakeMemoryUsage);
529       fakeMemoryUsage = malloc(artificialMemoryPerChare);
530       CkAssert(fakeMemoryUsage != NULL);
531     } else {
532       free(fakeMemoryUsage);
533       fakeMemoryUsage = NULL;
534     }
535 #endif
536
537
538
539     incoming_count = 0; // prepare for future granularity change 
540     contribute();
541   }
542   
543   
544
545
546
547   void receiveTransposeData(redistributor2DMsg *msg){
548     int top_new = top_data_idx(thisIndex.y, msg->new_chare_rows);
549     int bottom_new = bottom_data_idx(thisIndex.y, msg->new_chare_rows);
550     int left_new = left_data_idx(thisIndex.x, msg->new_chare_cols);
551     int right_new = right_data_idx(thisIndex.x, msg->new_chare_cols);    
552
553     int new_height = bottom_new - top_new + 1;
554     int new_width = right_new - left_new + 1;
555
556     if(incoming_count == 0){
557       // Allocate new arrays 
558       std::map<int,double*>::iterator diter;
559       for(diter =data_arrays.begin(); diter != data_arrays.end(); diter++){
560         int w = diter->first;
561         data_arrays_incoming[w] = new double[(new_width+2*data_x_ghost)*(new_height+2*data_y_ghost)];
562         data_arrays_incoming_sizes[w] = (new_width+2*data_x_ghost)*(new_height+2*data_y_ghost);
563
564         //      CkPrintf("data_arrays_incoming_sizes[%d] set to %d\n", w, data_arrays_incoming_sizes[w] );  
565
566       }
567     }
568     
569     
570     // Copy values from the incoming array to the appropriate place in data_arrays_incoming
571     // Current top left of my new array
572
573
574     double *localData = data_arrays_incoming[msg->which_array];
575     int s = data_arrays_incoming_sizes[msg->which_array];
576
577     //    CkPrintf("%d,%d data_arrays_incoming.size() = %d\n", thisIndex.x, thisIndex.y, data_arrays_incoming.size() );
578     //    CkPrintf("msg->which_array=%d   localData=%p   s=%d\n", msg->which_array, localData, s);
579     CkAssert(localData != NULL);
580
581     for(int j=0; j<msg->height; j++){
582       for(int i=0; i<msg->width; i++){
583
584         if( (msg->top+j >= top_new) && (msg->top+j <= bottom_new) && (msg->left+i >= left_new) && (msg->left+i <= right_new) ) {
585           CkAssert(j*msg->width+i<msg->height*msg->width);
586           CkAssert((msg->top+j-top_new)*new_width+(msg->left+i-left_new) < new_width*new_height);
587           CkAssert((msg->top+j-top_new)*new_width+(msg->left+i-left_new) >= 0);
588           
589           CkAssert((msg->top+j-top_new+data_y_ghost)*(new_width+2*data_x_ghost)+(msg->left+i-left_new+data_x_ghost) < s);
590           localData[(msg->top+j-top_new+data_y_ghost)*(new_width+2*data_x_ghost)+(msg->left+i-left_new+data_x_ghost)] = msg->data[j*msg->width+i];
591           incoming_count++;
592           
593         }
594         
595       }
596     }
597     
598     //    CkPrintf("Deleting message msg %p\n", msg); 
599     delete msg;
600
601
602     if(incoming_count == new_height*new_width*data_arrays.size()){
603
604       std::map<int,double*>::iterator diter;
605       for(diter =data_arrays.begin(); diter != data_arrays.end(); diter++){
606         int w = diter->first;
607         delete[] data_arrays[w];
608         data_arrays[w] = data_arrays_incoming[w];
609         data_arrays_sizes[w] = data_arrays_incoming_sizes[w];
610         data_arrays_incoming[w] = NULL;
611         data_arrays_incoming_sizes[w] = 0;
612
613         //        if(thisIndex.x==0 && thisIndex.y==0)   
614           //          CkPrintf("data_arrays_incoming_sizes[%d] set to %d\n",w, data_arrays_incoming_sizes[w] );   
615
616           //        if(thisIndex.x==0 && thisIndex.y==0) 
617           //  CkPrintf("data_arrays_sizes[%d] set to %d\n",w, data_arrays_sizes[w] ); 
618
619       }
620
621       continueToNextStep();
622     }
623     
624   }
625 };
626
627 /** @} */
628 #endif