Merge branch 'charm' into typed_reductions
[charm.git] / src / ck-ldb / TopoCentLB.C
1 /**************************************************************************
2 ** Amit Sharma (asharma6@uiuc.edu)
3 ** November 23, 2004
4 **
5 ** This is a topology conscious load balancer.
6 ** It migrates objects to new processors based on the topology in which the processors are connected.
7 ****************************************************************************/
8
9 #include <math.h>
10 #include <stdlib.h>
11 #include "TopoCentLB.decl.h"
12 #include "TopoCentLB.h"
13
14 #define alpha PER_MESSAGE_SEND_OVERHEAD_DEFAULT  /*Startup time per message, seconds*/
15 #define beta PER_BYTE_SEND_OVERHEAD_DEFAULT     /*Long-message time per byte, seconds*/
16 #define DEG_THRES 0.50
17
18 //#define MAX_EDGE
19 //#define RAND_COMM
20 #define make_mapping 0
21
22 CreateLBFunc_Def(TopoCentLB,"Balance objects based on the network topology")
23
24
25 /*static void lbinit (void)
26 {
27   LBRegisterBalancer ("TopoCentLB",
28                       CreateTopoCentLB,
29                       AllocateTopoCentLB,
30                       "Balance objects based on the network topology");
31 }*/
32
33
34 TopoCentLB::TopoCentLB(const CkLBOptions &opt) : CentralLB (opt)
35 {
36   lbname = "TopoCentLB";
37   if (CkMyPe () == 0) {
38     CkPrintf ("[%d] TopoCentLB created\n",CkMyPe());
39   }
40 }
41
42
43 CmiBool TopoCentLB::QueryBalanceNow (int _step)
44 {
45   return CmiTrue;
46 }
47
48 TopoCentLB::~TopoCentLB(){
49         if(topo) delete topo;
50 }
51
52 /*This routine partitions the task graph minimizing the communication and balancing the object load on all partitions*/
53 /*It uses METIS library to accomplish that*/
54 void TopoCentLB::computePartitions(CentralLB::LDStats *stats,int count,int *newmap)
55 {
56         
57   int numobjs = stats->n_objs;
58         int i, j, m;
59
60   // allocate space for the computing data
61   double *objtime = new double[numobjs];
62   int *objwt = new int[numobjs];
63   int *origmap = new int[numobjs];
64   LDObjHandle *handles = new LDObjHandle[numobjs];
65   
66         for(i=0;i<numobjs;i++) {
67     objtime[i] = 0.0;
68     objwt[i] = 0;
69     origmap[i] = 0;
70   }
71
72   //Prepare compute loads for METIS library
73   for (i=0; i<stats->n_objs; i++) {
74     LDObjData &odata = stats->objData[i];
75     if (!odata.migratable) 
76       CmiAbort("MetisLB doesnot dupport nonmigratable object.\n");
77     int frompe = stats->from_proc[i];
78     origmap[i] = frompe;
79     objtime[i] = odata.wallTime*stats->procs[frompe].pe_speed;
80     handles[i] = odata.handle;
81   }
82
83   // to convert the weights on vertices to integers
84   double max_objtime = objtime[0];
85   for(i=0; i<numobjs; i++) {
86     if(max_objtime < objtime[i])
87       max_objtime = objtime[i];
88   }
89         int maxobj=0;
90         int totalwt=0;
91   double ratio = 1000.0/max_objtime;
92   for(i=0; i<numobjs; i++) {
93       objwt[i] = (int)(objtime[i]*ratio);
94                         if(maxobj<objwt[i])
95                                 maxobj=objwt[i];
96                         totalwt+=objwt[i];
97   }
98         
99   int **comm = new int*[numobjs];
100   for (i=0; i<numobjs; i++) {
101     comm[i] = new int[numobjs];
102     for (j=0; j<numobjs; j++)  {
103       comm[i][j] = 0;
104     }
105   }
106
107   //Prepare communication for METIS library
108   const int csz = stats->n_comm;
109   for(i=0; i<csz; i++) {
110       LDCommData &cdata = stats->commData[i];
111       //if(cdata.from_proc() || cdata.receiver.get_type() != LD_OBJ_MSG)
112         //continue;
113                         if(!cdata.from_proc() && cdata.receiver.get_type() == LD_OBJ_MSG){
114         int senderID = stats->getHash(cdata.sender);
115         int recverID = stats->getHash(cdata.receiver.get_destObj());
116         CmiAssert(senderID < numobjs);
117         CmiAssert(recverID < numobjs);
118                                 comm[senderID][recverID] += cdata.messages;
119         comm[recverID][senderID] += cdata.messages;
120                                 //Use bytes or messages -- do i include messages for objlist too...??
121                         }
122                         else if (cdata.receiver.get_type() == LD_OBJLIST_MSG) {
123                                 //CkPrintf("in objlist..\n");
124         int nobjs;
125         LDObjKey *objs = cdata.receiver.get_destObjs(nobjs);
126         int senderID = stats->getHash(cdata.sender);
127         for (j=0; j<nobjs; j++) {
128            int recverID = stats->getHash(objs[j]);
129            if((senderID == -1)||(recverID == -1))
130               if (_lb_args.migObjOnly()) continue;
131               else CkAbort("Error in search\n");
132            comm[senderID][recverID] += cdata.messages;
133            comm[recverID][senderID] += cdata.messages;
134         }
135                         }
136                 }
137
138 // ignore messages sent from an object to itself
139   for (i=0; i<numobjs; i++)
140     comm[i][i] = 0;
141
142   // construct the graph in CSR format
143   int *xadj = new int[numobjs+1];
144   int numedges = 0;
145   for(i=0;i<numobjs;i++) {
146     for(j=0;j<numobjs;j++) {
147       if(comm[i][j] != 0)
148         numedges++;
149     }
150   }
151   int *adjncy = new int[numedges];
152   int *edgewt = new int[numedges];
153         int factor = 10;
154   xadj[0] = 0;
155   int count4all = 0;
156   for (i=0; i<numobjs; i++) {
157     for (j=0; j<numobjs; j++) { 
158       if (comm[i][j] != 0) { 
159         adjncy[count4all] = j;
160         edgewt[count4all++] = comm[i][j]/factor;
161       }
162     }
163     xadj[i+1] = count4all;
164   }
165
166   //Call METIS routine
167   int wgtflag = 3; // Weights both on vertices and edges
168   int numflag = 0; // C Style numbering
169   int options[5];
170   int edgecut;
171   int sameMapFlag = 1;
172
173         options[0] = 0;
174
175   if (count < 1) {
176     CkPrintf("error: Number of Pe less than 1!");
177   }
178   else if (count == 1) {
179         for(m=0;m<numobjs;m++) 
180                         newmap[i] = origmap[i];
181         sameMapFlag = 1;
182   }
183   else {
184         sameMapFlag = 0;
185                 /*
186         if (count > 8)
187                         METIS_PartGraphKway(&numobjs, xadj, adjncy, objwt, edgewt, 
188                             &wgtflag, &numflag, &count, options, 
189                             &edgecut, newmap);
190           else
191                         METIS_PartGraphRecursive(&numobjs, xadj, adjncy, objwt, edgewt, 
192                                  &wgtflag, &numflag, &count, options, 
193                                  &edgecut, newmap);
194                 */
195                 METIS_PartGraphRecursive(&numobjs, xadj, adjncy, objwt, edgewt,
196                                  &wgtflag, &numflag, &count, options,
197                                  &edgecut, newmap);
198   }
199          
200  
201   //Debugging code: Checking load on each partition
202   if(_lb_args.debug() >=2){
203           int total=0;
204           int *chkwt = new int[count];
205           for(i=0;i<count;i++)
206                   chkwt[i]=0;
207           for(i=0;i<numobjs;i++){
208                 chkwt[newmap[i]] += objwt[i];
209                   total += objwt[i];
210           }
211           for(i=0;i<count;i++)
212                   CkPrintf("%d -- %d\n",i,chkwt[i]);
213           CkPrintf("Totalwt of all partitions after call to METIS:%d, Avg is %d\n",total,total/count);
214   }
215
216   //Clean up all the variables allocated in this routine
217   for(i=0;i<numobjs;i++)
218     delete[] comm[i];
219   delete[] comm;
220   delete[] objtime;
221   delete[] xadj;
222   delete[] adjncy;
223   delete[] objwt;
224   delete[] edgewt;
225         delete[] handles;
226   delete[] origmap;
227
228 }
229
230 int TopoCentLB::findMaxObjs(int *map,int totalobjs,int count)
231 {
232         int *max_num = new int[count];
233         int i;
234         int maxobjs=0;
235         
236         for(i=0;i<count;i++)
237                 max_num[i]=0;
238                 
239         for(i=0;i<totalobjs;i++)
240                 max_num[map[i]]++;
241         
242         for(i=0;i<count;i++)
243                 if(max_num[i]>maxobjs)
244                         maxobjs = max_num[i];
245         
246         delete[] max_num;
247
248         return maxobjs;
249 }
250
251 void TopoCentLB::Heapify(HeapNode *heap, int node, int heapSize)
252 {
253   int left = 2*node+1;
254   int right = 2*node+2;
255   int xchange;
256         
257   if (left < heapSize && (heap[left].key > heap[node].key))
258     xchange = left;
259   else 
260                 xchange = node;
261   
262   if (right < heapSize && (heap[right].key > heap[xchange].key))
263     xchange = right;
264
265   if (xchange != node) {
266     HeapNode tmp;
267     tmp = heap[node];
268     heap[node] = heap[xchange];
269     heap[xchange] = tmp;
270                 heapMapping[heap[node].node]=node;
271                 heapMapping[heap[xchange].node]=xchange;
272     Heapify(heap,xchange,heapSize);
273   }    
274 }
275
276
277 TopoCentLB::HeapNode TopoCentLB::extractMax(HeapNode *heap,int *heapSize){
278
279         if(*heapSize < 1)
280                 CmiAbort("Empty Heap passed to extractMin!\n");
281
282         HeapNode max = heap[0];
283         heap[0] = heap[*heapSize-1];
284         heapMapping[heap[0].node]=0;
285         *heapSize = *heapSize - 1;
286         Heapify(heap,0,*heapSize);
287         return max;
288 }
289
290 void TopoCentLB::BuildHeap(HeapNode *heap,int heapSize){
291         for(int i=heapSize/2; i >= 0; i--)
292                 Heapify(heap,i,heapSize);
293 }
294
295 void TopoCentLB :: increaseKey(HeapNode *heap,int i,double wt){
296         int val;
297         if(wt != -1.00){
298                 #ifdef MAX_EDGE
299                         if(wt>heap[i].key)
300                                 heap[i].key = wt;
301                 #else
302                         heap[i].key += wt;
303                 #endif
304         }
305         int parent = (i-1)/2;
306         
307         if(heap[parent].key >= heap[i].key)
308                 return;
309         else {
310                 HeapNode tmp = heap[parent];
311                 heap[parent] = heap[i];
312                 heap[i] = tmp;
313                 heapMapping[heap[parent].node]=parent;
314                 heapMapping[heap[i].node]=i;
315                 increaseKey(heap,parent,-1.00);
316         }
317 }
318
319 /*This routine implements the algorithm used to produce the partition-processor mapping*/
320 /*The algorithm uses an idea similar to the standard MST algorithm*/
321 void TopoCentLB :: calculateMST(PartGraph *partgraph,LBTopology *topo,int *proc_mapping,int max_comm_part){
322
323         int *inHeap;
324         double *keys;
325         int count = partgraph->n_nodes;
326         int i=0,j=0;
327
328   //Arrays needed for keeping information
329         inHeap = new int[partgraph->n_nodes];
330         keys = new double[partgraph->n_nodes];
331
332         int *assigned_procs = new int[count];
333
334         hopCount = new double*[count];
335         for(i=0;i<count;i++){
336                 proc_mapping[i]=-1;
337                 assigned_procs[i]=0;
338                 hopCount[i] = new double[count];
339                 for(j=0;j<count;j++)
340                         hopCount[i][j] = 0;
341         }
342
343   //Call a topology routine to fill up hopCount
344         topo->get_pairwise_hop_count(hopCount);
345         
346   int max_neighbors = topo->max_neighbors();
347         
348         HeapNode *heap = new HeapNode[partgraph->n_nodes];
349         heapMapping = new int[partgraph->n_nodes];
350         
351         int heapSize = 0;
352
353         for(i=0;i<partgraph->n_nodes;i++){
354                 heap[i].key = 0.00;
355                 heap[i].node = i;
356                 keys[i] = 0.00;
357                 inHeap[i] = 1;
358                 heapMapping[i]=i;
359         }
360
361         //Assign the max comm partition first
362         heap[max_comm_part].key = 1.00;
363         
364         heapSize = partgraph->n_nodes;
365         BuildHeap(heap,heapSize);
366
367         int k=0,comm_cnt=0,m=0,n=0;
368         int *commParts = new int[partgraph->n_nodes];
369         
370         //srand(count);
371
372         while(heapSize > 0){
373
374     /****Phase1: Extracting appropriate partition from heap****/
375
376                 HeapNode max = extractMax(heap,&heapSize);
377                 inHeap[max.node] = 0;
378
379                 for(i=0;i<partgraph->n_nodes;i++){
380                         commParts[i]=-1;
381                         PartGraph::Edge wt = partgraph->edges[max.node][i];
382                         if(wt == 0)
383                                 continue;
384                         if(inHeap[i]){
385                         #ifdef MAX_EDGE
386                                 if(wt>keys[i])
387                                         keys[i]=wt;
388                         #else
389                                 keys[i] += wt;
390                         #endif
391         /*This part has been COMMENTED out for optimizing the code: we handle the updation using heapMapping*/
392         /*array instead of searching for node in the heap everytime*/
393
394                                 //Update in the heap too
395                                 //First, find where this node is..in the heap
396                                 /*for(j=0;j<heapSize;j++)
397                                         if(heap[j].node == i)
398                                                 break;
399                                 if(j==heapSize)
400                                         CmiAbort("Some error in heap...\n");*/
401                                 increaseKey(heap,heapMapping[i],wt);
402                         }
403                 }
404                  
405     /*Phase2: ASSIGNING partition to processor*/
406                 
407     //Special case
408                 if(heapSize == partgraph->n_nodes-1){ //Assign max comm partition to 0th proc in the topology
409                         proc_mapping[max.node]=0;
410                         assigned_procs[0]=1;
411                         continue;
412                 }
413                 
414                 m=0;
415                 n=0;
416                 comm_cnt=0;
417
418                 int pot_proc=-1;
419                 double min_cost=-1;
420                 int min_cost_index=-1;
421                 double cost=0;
422                 int p=0;
423                 //int q=0;
424
425                 for(k=0;k<partgraph->n_nodes;k++){
426                         if(!inHeap[k] && partgraph->edges[k][max.node]){
427                                 commParts[comm_cnt]=k;
428                                 comm_cnt++;
429                         }
430                 }
431
432     //Optimized the loop by commenting out the get_hop_count code and getting all the hop counts initially
433                 for(m=0;m<count;m++){
434                         if(!assigned_procs[m]){
435                                 cost=0;
436                                 for(p=0;p<comm_cnt;p++){
437                                         //if(!hopCount[proc_mapping[commParts[p]]][m])
438                                                 //hopCount[proc_mapping[commParts[p]]][m]=topo->get_hop_count(proc_mapping[commParts[p]],m);
439                                         cost += hopCount[proc_mapping[commParts[p]]][m]*partgraph->edges[commParts[p]][max.node];
440                                 }
441                                 if(min_cost==-1 || cost<min_cost){
442                                         min_cost=cost;
443                                         min_cost_index=m;
444                                 }
445                         }
446                 }
447
448                 proc_mapping[max.node]=min_cost_index;
449                 assigned_procs[min_cost_index]=1;
450         }
451
452         //clear up memory
453         delete[] inHeap;
454         delete[] keys;
455         delete[] assigned_procs;
456         delete[] heap;
457         delete[] commParts;
458 }
459
460
461 void TopoCentLB :: work(LDStats *stats)
462 {
463   int proc;
464   int obj;
465   int dest;
466   int i,j;
467   LDObjData *odata;
468   int n_pes = stats->nprocs();
469         
470   if (_lb_args.debug() >= 2) {
471     CkPrintf("In TopoCentLB Strategy...\n");
472   }
473   
474   // Make sure that there is at least one available processor.
475   for (proc = 0; proc < n_pes; proc++) {
476     if (stats->procs[proc].available) {
477       break;
478     }
479   }
480
481   if (proc == n_pes) {
482     CmiAbort ("TopoCentLB: no available processors!");
483   }
484
485   
486   removeNonMigratable(stats, n_pes);
487   int *newmap = new int[stats->n_objs];
488
489
490   if(make_mapping)
491     computePartitions(stats, n_pes, newmap);
492   else {
493     //mapping taken from previous algo
494     for(i=0;i<stats->n_objs;i++) {
495       newmap[i]=stats->from_proc[i];
496     }
497   }
498
499   //Debugging Code
500   if(_lb_args.debug() >=2){
501           CkPrintf("Map obtained from partitioning:\n");
502     for(i=0;i<stats->n_objs;i++)
503                   CkPrintf(" %d,%d ",i,newmap[i]);
504   }
505
506         int max_objs = findMaxObjs(newmap,stats->n_objs, n_pes);
507         
508         partgraph = new PartGraph(n_pes, max_objs);
509
510         //Fill up the partition graph - first fill the nodes and then, the edges
511
512         for(i=0;i<stats->n_objs;i++)
513         {
514                 PartGraph::Node* n = &partgraph->nodes[newmap[i]];
515                 n->obj_list[n->num_objs]=i;
516                 n->num_objs++;
517         }
518
519         int *addedComm=new int[n_pes];
520   
521   stats->makeCommHash();
522   
523         int max_comm_part=-1;
524         
525         double max_comm=0;
526
527   //Try putting random amount of communication on the partition graph edges to see if things work fine
528   //This also checks the running time of the algorithm since number of edges is high than in a practical scenario
529         #ifdef RAND_COMM
530         for(i = 0; i < n_pes; i++) {
531                 for(j = i+1; j < n_pes; j++) {
532                         int val;
533                         if(rand()%5==0)
534                                 val=0;
535                         else
536                                 val= rand()%1000;
537                                 
538                         partgraph->edges[i][j] = val;
539                         partgraph->edges[j][i] = val;
540                         
541                         partgraph->nodes[i].comm += val;
542                         partgraph->nodes[j].comm += val;
543                         
544                         if(partgraph->nodes[i].comm > max_comm){
545                                 max_comm = partgraph->nodes[i].comm;
546                                 max_comm_part = i;
547                         }
548                         if(partgraph->nodes[j].comm > max_comm){
549                                 max_comm = partgraph->nodes[j].comm;
550                                 max_comm_part = j;
551                         }
552                 }
553         }
554         #else
555   //Adding communication to the partition graph edges
556         for(i=0;i<stats->n_comm;i++)
557         {
558                 //DO I consider other comm too....i.e. to or from a processor
559                 LDCommData &cdata = stats->commData[i];
560                 if(!cdata.from_proc() && cdata.receiver.get_type() == LD_OBJ_MSG){
561         int senderID = stats->getHash(cdata.sender);
562         int recverID = stats->getHash(cdata.receiver.get_destObj());
563                         CmiAssert(senderID < stats->n_objs);
564                 CmiAssert(recverID < stats->n_objs);
565                 
566                         if(newmap[senderID]==newmap[recverID])
567                                 continue;
568         
569                         if(partgraph->edges[newmap[senderID]][newmap[recverID]] == 0){
570                                 partgraph->nodes[newmap[senderID]].degree++;
571                                 partgraph->nodes[newmap[recverID]].degree++;
572                         }
573                 
574                         partgraph->edges[newmap[senderID]][newmap[recverID]] += cdata.bytes;
575                         partgraph->edges[newmap[recverID]][newmap[senderID]] += cdata.bytes;
576                         
577                         partgraph->nodes[newmap[senderID]].comm += cdata.bytes;
578                         partgraph->nodes[newmap[recverID]].comm += cdata.bytes;
579
580       //Keeping track of maximum communiacting partition
581                         if(partgraph->nodes[newmap[senderID]].comm > max_comm){
582                                 max_comm = partgraph->nodes[newmap[senderID]].comm;
583                                 max_comm_part = newmap[senderID];
584                         }
585                         if(partgraph->nodes[newmap[recverID]].comm > max_comm){
586                                 max_comm = partgraph->nodes[newmap[recverID]].comm;
587                                 max_comm_part = newmap[recverID];
588                         }
589                 }
590                 else if(cdata.receiver.get_type() == LD_OBJLIST_MSG) {
591                         int nobjs;
592         LDObjKey *objs = cdata.receiver.get_destObjs(nobjs);
593       int senderID = stats->getHash(cdata.sender);
594                         for(j = 0; j < n_pes; j++)
595                                 addedComm[j]=0;
596                         for (j=0; j<nobjs; j++) {
597         int recverID = stats->getHash(objs[j]);
598         if((senderID == -1)||(recverID == -1))
599                 if (_lb_args.migObjOnly()) continue;
600                 else CkAbort("Error in search\n");
601                                         
602                                 if(newmap[senderID]==newmap[recverID])
603                                         continue;
604         
605                                 if(partgraph->edges[newmap[senderID]][newmap[recverID]] == 0){
606                                         partgraph->nodes[newmap[senderID]].degree++;
607                                         partgraph->nodes[newmap[recverID]].degree++;
608                                 }
609
610         //Communication added only once for a message sent to many objects on a single processor
611                                 if(!addedComm[newmap[recverID]]){
612                                         partgraph->edges[newmap[senderID]][newmap[recverID]] += cdata.bytes;
613                                         partgraph->edges[newmap[recverID]][newmap[senderID]] += cdata.bytes;
614         
615                                         partgraph->nodes[newmap[senderID]].comm += cdata.bytes;
616                                         partgraph->nodes[newmap[recverID]].comm += cdata.bytes;
617
618                                         if(partgraph->nodes[newmap[senderID]].comm > max_comm){
619                                                 max_comm = partgraph->nodes[newmap[senderID]].comm;
620                                                 max_comm_part = newmap[senderID];
621                                         }
622                                         if(partgraph->nodes[newmap[recverID]].comm > max_comm){
623                                                 max_comm = partgraph->nodes[newmap[recverID]].comm;
624                                                 max_comm_part = newmap[recverID];
625                                         }
626                                         //bytesComm[newmap[senderID]][newmap[recverID]] += cdata.bytes;
627                                         //bytesComm[newmap[recverID]][newmap[senderID]] += cdata.bytes;
628                                         addedComm[newmap[recverID]]=1;
629                                 }
630       }
631                 }
632
633         }
634         #endif
635         
636         int *proc_mapping = new int[n_pes];
637         
638         delete [] addedComm;
639                 
640         LBtopoFn topofn;
641         int max_neighbors=0;
642         int *neigh;
643         int num_neigh=0;
644
645   //Parsing the command line input for getting the processor topology
646         char *lbcopy = strdup(_lbtopo);
647   char *ptr = strchr(lbcopy, ':');
648   if (ptr!=NULL)
649     ptr = strtok(lbcopy, ":");
650         else
651                 ptr=lbcopy;
652
653         topofn = LBTopoLookup(ptr);
654   if (topofn == NULL) {
655     char str[1024];
656     CmiPrintf("TopoCentLB> Fatal error: Unknown topology: %s. Choose from:\n", ptr);
657     printoutTopo();
658     sprintf(str, "TopoCentLB> Fatal error: Unknown topology: %s", ptr);
659     CmiAbort(str);
660   }
661   
662         topo = topofn(n_pes);
663
664   //Call the core routine to produce the partition processor mapping
665         calculateMST(partgraph,topo,proc_mapping,max_comm_part);
666         //Returned partition graph is a Maximum Spanning Tree -- converted in above function itself
667
668   //Debugging code: Result of mapping partition graph onto processor graph
669         if (_lb_args.debug()>1) {
670           CkPrintf("Resultant mapping..(partition,processor)\n");
671           for(i = 0; i < n_pes; i++)
672                   CkPrintf("%d,%d\n",i,proc_mapping[i]);
673   }
674
675   //Store the result in the load balancing database
676         int pe;
677         PartGraph::Node* n;
678         for(i = 0; i < n_pes; i++){
679                 pe = proc_mapping[i];
680                 n = &partgraph->nodes[i];
681                 for(j=0;j<n->num_objs;j++){
682                         stats->to_proc[n->obj_list[j]] = pe;
683                         if (_lb_args.debug()>1) 
684         CkPrintf("[%d] Obj %d migrating from %d to %d\n", CkMyPe(),n->obj_list[j],stats->from_proc[n->obj_list[j]],pe);
685                 }
686         }
687
688         delete[] newmap;
689         delete[] proc_mapping;
690         //Delete hopCount
691         for(i = 0; i < n_pes; i++)
692                 delete[] hopCount[i];
693
694         delete[] hopCount;
695         delete[] heapMapping;
696         
697   delete partgraph;
698 }
699
700 #include "TopoCentLB.def.h"