/////////////////////////////////////////////////////////////// // Contract.c // // Jorge Vittes // // Code for dynamic parallel tree contraction (RC-Trees) // This file handles the structural, and data propagation // algorithm by Umut Acar, Guy Blelloch, and Jorge Vittes ////////////////////////////////////////////////////////////// #include #include #include #include #include #include "Globals.h" #include "BinCluster.h" #include "FinalCluster.h" #include "UnaryCluster.h" #include "Data.h" #include "Interface.h" #include "Queue.h" #include "Tree.h" #include "Vertex.h" #include "Contract.h" #define DO_RAKE 1 #define DO_COMPRESS 2 #define DO_LIVE 3 #define DO_END 4 #define CLUSTER_OFFSET_ZERO 0 #define CLUSTER_OFFSET_ONE 1 /*DEBUG STUFF */ int delf; node* glNode; int currentrun = 0; int morework=0; Queue *currentQueue; tree_t* currentTree; clusterList oldRootList; clusterList newRootList; /* END DEGUG STUFF */ /////////////////////////////////////////////////////////// // Insert node nd into the queue if it isn't already there /////////////////////////////////////////////////////////// int insertQueue (node* nd, Queue *q) { // printVertex (nd); deprintf("insertQueue: Queue = %p \n",q); //If the node is not already marked if(nd->mark != QUEUED) { //then mark it and enqueue it nd->mark = QUEUED; deprintf("Enqueued %d\n",nd->nId); enqueue(q, nd); //return success return 1; } deprintf("Already Queued\n"); return 0; //otherwise return failure } //////////////////////////////////////////// // return the end of the queue ie dequeue //////////////////////////////////////////// node* removeQueue (Queue *q) { assert (!isEmpty (q)); node* nd; deprintf("removeQueue: Queue = %p \n",q); nd = (node *) dequeue(q); assert (nd); nd -> mark = NOT_QUEUED; return nd; } /* Code to empty the queue */ void emptyQueue(Queue* q) { while(!isEmpty(q)) removeQueue(q); } ////////////////////////////////////////////// // Delete entire family tree ///////////////////////////////////////////// void deleteFam(node* n) { node *tmp; int i; while (n) { tmp = n; n = n->descendant; tmp->deleted = 1; tmp->mark = NOT_QUEUED; tmp->descendant = NULL; for(i=0;iscars[i].backscar=NULL; tmp->scars[i].cl = NULL; } FREE_NODE(tmp); } } ////////////////////////////////////////////////////////////////////////// // Delete a node for the contraction by marking it deleted and freeing // all of its decendants ///////////////////////////////////////////////////////////////////////// void deleteNode(node* thisNode) { thisNode->descendant->deleted = 1; thisNode->descendant->mark = NOT_QUEUED; deleteFam (thisNode->descendant->descendant); thisNode->descendant->descendant = NULL; } //////////////////////////////////////////////////////////////////////// // check what need to be done for the contraction here and return // the appropriate response //////////////////////////////////////////////////////////////////////// int contractCheck (node* thisNode) { node *left, *right; scar *lbscar, *rbscar; switch (thisNode->degree) { case 0: return DO_END; case 1: //if the degree is one, and the others degree is not 1, or my address //is less than the neighbor then contract lbscar = thisNode->scars[thisNode->left].backscar; left = GET_NEIGHBOR(lbscar); if((left < thisNode) || (left->degree !=1)) return DO_RAKE; return DO_LIVE; case 2: lbscar = thisNode->scars[thisNode->left].backscar; rbscar = thisNode->scars[thisNode->right].backscar; left = GET_NEIGHBOR(lbscar); right = GET_NEIGHBOR(rbscar); /* And if there are the correct coin ids then contract */ if ((left->degree != 1) && (thisNode->id > left->id) && (right->degree != 1) && (thisNode->id >right->id) ) return DO_COMPRESS; return DO_LIVE; default: return DO_LIVE; } } //////////////////////////////////////////////////////////////////////////////////// // Setup the scar on left's descendant to point to right's descendant //////////////////////////////////////////////////////////////////////////////////// void shortCut (node* thisNode,node* left,node* right,int lindex,int rindex, int dir) { scar* lscar; //get the scar lscar = (scar* ) ((int) (left->descendant) + (int) (thisNode->scars[lindex].backscar) - (int) left); //update the backscar, and neighbor by making it point to the other neighbor lscar->backscar = (scar *) ((int) right->descendant + (((int)(thisNode->scars[rindex].backscar)) - (int)right)); lscar->cl = (cluster*)((int) (thisNode->vertex->cl) + dir); } ////////////////////////////////////////////////////////////////////////////// // Update the neighbors descendant by making it point to the other neighbor's // descendant, neighbor being neigh, other neighbor is other ////////////////////////////////////////////////////////////////////////////// void doCompress(node* thisNode, node* left, node* right,int lindex,int rindex) { deprintf("Contracting %d \n", thisNode->nId); setupVertexCluster (thisNode, COMPRESS_EVENT, currentTree, &oldRootList); shortCut (thisNode,left,right,lindex,rindex, CLUSTER_OFFSET_ZERO); shortCut (thisNode,right,left,rindex,lindex, CLUSTER_OFFSET_ONE); bin_cluster* cl = (bin_cluster*) thisNode->vertex->cl; cl->affected = IS_AFFECTED; //cl->endpoints = 2; cl->endp1 = left->nId; cl->endp2 = right->nId; } /////////////////////////////////////////////////////////////////// // Do the raking /////////////////////////////////////////////////////////////////// void doRake(node* thisNode,node* left, int lindex) { deprintf("Raking \n"); scar* lscar; setupVertexCluster (thisNode,RAKE_EVENT, currentTree, &oldRootList); unary_cluster* cl= (unary_cluster*) thisNode->vertex->cl; deprintf("the cluster is %p\n",cl); //set the neighbors scars correctly lscar = (scar *)((int)(left->descendant) + (int)(thisNode->scars[lindex].backscar) -(int) left); lscar->cl = thisNode->vertex->cl; lscar->backscar = NULL; drun(deprintf("the neighbor is %d\n",((cluster*)((int) lscar - ((int) (lscar) % sizeof(node))))->id )); cl->affected = IS_AFFECTED; //cl->endpoints = 1; cl->endp1 = left->nId; } /////////////////////////////////////////////////////////////////////////////// // Queue all the descendant's neighbors since they are affected /////////////////////////////////////////////////////////////////////////////// void queueAllDescNeighbors(node* thisNode,Queue* q) { int i; scar* bscar; node* neigh; node* nd= thisNode->descendant; for(i=0;iscars[i].backscar; if(bscar && nd->scars[i].cl) { if((bscar->backscar) == &(nd->scars[i])) { neigh = GET_NEIGHBOR(bscar); insertQueue(neigh,q); } } } } ///////////////////////////////////////////////////////////////////// // doLive (for the initial run in the inintial run if the node // is going to live then copy down the neighbor's scars associated // with the node, and copy down the empty scars for the node //////////////////////////////////////////////////////////////////// void doLive(node* thisNode) { int i,new_degree; int neighs[MAX_DEGREE]; node* neigh; node* desc; scar *neigh_scar, *desc_scar; drun(int j); drun(cluster* cl); neighs[0]=-1;neighs[1]=-1; deprintf("I live!!! I am %d\n",thisNode->nId); drun(deprintf("Degree is %d\n",thisNode->degree)); // Compute degree, remember left and right new_degree = 0; for(i=0;iscars[i].backscar; if (neigh_scar) { neigh = GET_NEIGHBOR(neigh_scar); deprintf("i is %d\n",i); if(neigh->degree > 1) { neighs[new_degree] = i; ++new_degree; } } } desc = thisNode->descendant; desc->degree = new_degree; desc->left = neighs[0]; desc->right = neighs[1]; // copy node data and your neighbors scars down to the next level desc->data = thisNode->data; for(i=0;iscars[i].backscar; if (neigh_scar) { neigh = GET_NEIGHBOR(neigh_scar); desc_scar= (scar *) ((int)neigh->descendant+ (int) neigh_scar - (int)neigh); desc_scar->cl = neigh_scar->cl; desc_scar->backscar = desc->scars+i; deprintf("Wrote %p at address %p\n",desc,GET_NEIGHBOR(desc_scar->backscar)); } else { // no neighbor: just copy this scar down desc->scars[i].cl = thisNode->scars[i].cl; desc->scars[i].backscar = NULL; drun(j= (int) thisNode->scars->cl & 3); drun(cl = GET_CL(thisNode->scars[i].cl)); drun(deprintf("the cl is %p\n", cl)); } } } ///////////////////////////////////////////////////////////////////////// // Check if the leaf status has changed, ie. if it was degree one, but // now it isn't, or visa-versa //////////////////////////////////////////////////////////////////////// void checkLeafStatusChange(int old_degree, node* desc) { int i; if (old_degree != desc->degree) if ((desc->degree==1) || (old_degree==1)) { node* neigh; scar* neigh_scar; for(i=0;iscars[i].backscar; if (neigh_scar) if(GET_NEIGHBOR(neigh_scar->backscar) == desc) { neigh = GET_NEIGHBOR(neigh_scar); insertQueue(neigh,currentQueue); } } } } ////////////////////////////////////////////////////////////////////// // check if the node has been undeleted ////////////////////////////////////////////////////////////////////// void checkUndelete(node* thisNode, node* desc) { node *neigh; scar* neigh_scar; if(desc->deleted) { int i; desc->deleted = 0; for(i=0;iscars[i].backscar; if (neigh_scar) { neigh = GET_NEIGHBOR(neigh_scar); insertQueue(neigh->descendant,currentQueue); } } } } /////////////////////////////////////////////////////////////////// // basic function that determines what to do with all nodes ////////////////////////////////////////////////////////////////// void runNode(node* thisNode, Queue* q) { int lindex,rindex; scar *lscar, *rscar; node *left=NULL,*right=NULL; deprintf("The degree is %d\n", thisNode->degree); deprintf("The cluster is %p\n",thisNode->data); deprintf("The node is %p\n", thisNode); switch (contractCheck(thisNode)) { case DO_END: setupVertexCluster(thisNode,END_EVENT, currentTree, &oldRootList); //end event the event to end them all! thisNode->vertex->cl->affected = IS_AFFECTED; deleteNode(thisNode); insertCluster(thisNode->vertex->cl,&newRootList); break; case DO_RAKE: morework =1; lindex = thisNode->left; lscar = thisNode->scars[lindex].backscar; left = GET_NEIGHBOR(lscar); doRake(thisNode,left,lindex); insertQueue(left->descendant,q); deleteNode(thisNode); break; case DO_COMPRESS: morework =1; lindex = thisNode->left; rindex = thisNode->right; lscar = thisNode->scars[lindex].backscar; rscar = thisNode->scars[rindex].backscar; left = GET_NEIGHBOR(lscar); right = GET_NEIGHBOR(rscar); doCompress(thisNode, left, right, lindex, rindex); insertQueue(left->descendant,q); insertQueue(right->descendant,q); deleteNode(thisNode); break; case DO_LIVE: { morework = 1; node *desc = thisNode->descendant; int old_degree = desc->degree; doLive(thisNode); // Queue all affected nodes. insertQueue(desc,q); deprintf("Check Undeleted \n"); // check if thisNode is un-deleted, queue neighbors if so checkUndelete(thisNode,desc); deprintf("Leaf status change \n"); // Check if leaf status of the descendant changes and queue neighbors if so checkLeafStatusChange(old_degree,desc); // Allocate a new node for the next round if needed if(!desc->descendant) { deprintf("Allocated new node \n"); desc -> descendant = copyVertex (desc,currentTree->nodeList); } } break; default: printf ("Error in runNode"); exit (-9); } } ////////////////////////////////////////////////////////////// // Return whether the cluster has been deleted ////////////////////////////////////////////////////////////// int isDeleted(cluster* cl) { if(cl->affected == DELETED) return 1; return 0; } /* */ void pushDownFun(cluster* parent, cluster* child) { if(parent->endpoints == 2) if(child->endpoints == 2) { bin_cluster *bcp,*bc; bcp = (bin_cluster*) parent; bc = (bin_cluster*) child; deprintf("Pushing down from %d, to %d \n",parent->id, child->id); pushDownData(&(bcp->data), &(bc->data)); } } ///////////////////////////////////////////////////////////// // To free the cluster, find out which pool it came from and // put it back there ///////////////////////////////////////////////////////////// void freeCluster(cluster* cl) { deprintf("freeCluster %p\n",cl); if(cl->endpoints == 2) { drun(bcs--); deprintf("free binary\n"); freeBlock(currentTree->BClusters,(char*) cl); } else { deprintf("free non-bnary\n"); drun(ucs--); freeBlock(currentTree->UClusters,(char*) cl); } } //////////////////////////////////////////////////////////// // Push down any data from the old tree onto the new tree // by doing a traversal over the deleted clusters ////////////////////////////////////////////////////////// void pushDown(cluster* cl) { cluster* ccl; int i; assert(cl); ccl = GET_CL(cl); deprintf("pushDown: cl = %d\n",ccl->id); cluster** clusters = ccl->getClusters(); for(i=0; i < MAX_DEGREE; ++i) { cluster* child = clusters[i]; cluster* ccp = GET_CL(child); if(child) { pushDownFun(ccl,ccp); if(isDeleted(ccp)) { if(ccp->child) { deprintf("Child\n"); pushDown(child); deprintf("ret\n"); } freeCluster(ccp); } } } free(clusters); } /////////////////////////////////////////////////////// // Go over the list of old trees, and for each one of // them push down their data onto the new trees /////////////////////////////////////////////////////// void pushDownList(clusterList* rootList) { while(rootList->head != NULL) { cluster* cl = removeCluster(rootList); pushDown(cl); freeBlock(currentTree->UClusters,(char*) cl); deprintf("done 1\n"); } deprintf("done\n"); } void updateClusterContract(cluster* cl) { PURIFY (&cl); cl->synchronize (); cl->affected = NOT_AFFECTED; } cluster* synchronizeClusterContract(cluster* cl) { int i; cluster** clusters = cl->getClusters(); for(i=0; i < MAX_DEGREE; ++i) { if(clusters[i]) { cluster* ccp = GET_CL(clusters[i]); deprintf("Child->affected is %d\n",ccp->affected); if(ccp->affected == IS_AFFECTED) { synchronizeClusterContract(ccp); } } } free(clusters); updateClusterContract(cl); return cl; } ///////////////////////////////////////////////////////////////// // To do the data propagation travese each new tree, and do a // post-order update. ///////////////////////////////////////////////////////////////// cluster* syncList(clusterList* rootList) { cluster* cl=NULL; int odebug = debug; //debug =1; while(rootList->head != NULL) { deprintf("Synch \n"); cl = removeCluster(rootList); synchronizeClusterContract(cl); deprintf("done 1\n"); } debug = odebug; deprintf("done\n"); return cl; } /////////////////////////////////////////////////////////////// // rerun is to do a rerun // Takes the elements of the queue, if there is no more // work it breaks, if it is a copy event is calculates the // degree puts it in the node and makes sure the node has // a child, and if it is a run event it runs the wake up // code ///////////////////////////////////////////////////////////// cluster* rerun(Queue *q,tree_t* tree, int doSynch) { cluster* root; node *nd = NULL; int curheight = -1; deprintf(" \n Rerunning \n"); currentTree = tree; currentQueue = q; morework=1; //debug=1; while (!isEmpty(q)) { // Loop // Free the qnode drun(assert(!isEmpty(q))); nd = removeQueue(q); if(!(nd->deleted)) { //If it is the end of a round then check if all is done, otherwise continue, resetting //the morework flag if(nd->height != curheight) { curheight = nd->height; deprintf("Height is %d\n",curheight); if(morework == 0) break; morework = 0; } deprintf("Running %d\n",nd->nId); runNode(nd,q); drun(assert(verifyVertex(nd))); } }//while // Empty the queue emptyQueue(q); if(PUSHDOWN) { deprintf("push down\n"); pushDownList(&oldRootList); } if(doSynch) { root = syncList(&newRootList); } return root; } /////////////////////////////////////////////////////////////////////// // Initial Run //////////////////////////////////////////////////////////////////////// void initTreeContraction (Queue* q, tree_t* tree) { int i,n; node* v; n = tree->n; currentTree = tree; initClusterList(); oldRootList.head = NULL; newRootList.head = NULL; for (i = 1; i <= n; ++i) { v = tree ->vertexArray + i; deprintf("Enqueueuing %d\n",i); insertQueue(v,q); deprintf("Making descendant copy\n"); v->descendant = copyVertex (v,currentTree->nodeList); } } ////////////////////////////////////////////////////// // This is the initial run, All it does now is call // rerun since that is what an initial run is, a rerun // of all the code ////////////////////////////////////////////////////// cluster* initialrun(Queue *q, tree_t* tree) { cluster* root; root = rerun(q,tree,1); return(root); }