diff --git a/Makefile.common b/Makefile.common index 29a54e0..9f86ef7 100644 --- a/Makefile.common +++ b/Makefile.common @@ -1,13 +1,13 @@ #set the following variables to absolute paths -CHARM_DIR = /Users/raghu/work/charm/charm -UNION_FIND_DIR = /Users/raghu/work/charm/unionFind/unionFind +CHARM_DIR = /Users/johnnychen/Documents/Projects/research/charm +UNION_FIND_DIR = /Users/johnnychen/Documents/Projects/research/unionfind PREFIX_LIB_DIR = $(UNION_FIND_DIR)/prefixLib #charmc options ANCHOR= -DANCHOR_ALGO #ANCHOR= PROFILE= -DPROFILING -CHARMC = $(CHARM_DIR)/bin/charmc $(ANCHOR) $(PROFILE) +CHARMC = $(CHARM_DIR)/bin/charmc $(PROFILE) #$(ANCHOR) OPTS = -std=c++11 -O3 -g LD_OPTS = @@ -16,4 +16,4 @@ PREFIX_LIBS = -L${PREFIX_LIB_DIR} -lprefix PREFIX_INC = -I${PREFIX_LIB_DIR} UNION_FIND_LIBS = -L${UNION_FIND_DIR} -lunionFind ${PREFIX_LIBS} -UNION_FIND_INC = -I${UNION_FIND_DIR} ${PREFIX_INC} +UNION_FIND_INC = -I${UNION_FIND_DIR} ${PREFIX_INC} diff --git a/examples/simple_graph/graph.C b/examples/simple_graph/graph.C index 1379dab..a9be5e0 100644 --- a/examples/simple_graph/graph.C +++ b/examples/simple_graph/graph.C @@ -130,7 +130,7 @@ class TreePiece : public CBase_TreePiece { // function that must be always defined by application // return type -> std::pair // this specific logic assumes equal distribution of vertices across all tps - static std::pair getLocationFromID(long int vid); + static std::pair getLocationFromID(uint64_t vid); void initializeLibVertices() { // provide vertices data to library @@ -203,11 +203,11 @@ TreePiece::getLocationFromID(long int vid) { */ std::pair -TreePiece::getLocationFromID(long int vid) { +TreePiece::getLocationFromID(uint64_t vid) { int chareIdx = (vid-1) % NUM_TREEPIECES; int arrIdx = (vid-1) / NUM_TREEPIECES; return std::make_pair(chareIdx, arrIdx); } -#include "graph.def.h" +#include "graph.def.h" \ No newline at end of file diff --git a/prefixLib/Makefile b/prefixLib/Makefile index d5ced14..0351d03 100644 --- a/prefixLib/Makefile +++ b/prefixLib/Makefile @@ -3,7 +3,7 @@ CHARMC=/Users/raghu/work/charm/charm/bin/charmc $(OPTS) all: libprefix.a libprefix.a: prefixBalance.o - $(CHARMC) prefixBalance.o -o libprefix.a -language charm++ + $(CHARMC) prefixBalance.o -o libprefix.a -language charm++ # -DCK_TEMPLATES_ONLY prefixBalance.o : prefixBalance.C prefixBalance.def.h prefixBalance.decl.h $(CHARMC) -c prefixBalance.C diff --git a/types.h b/types.h index 73e85d4..215e695 100644 --- a/types.h +++ b/types.h @@ -12,6 +12,16 @@ struct findBossData { } }; +struct needBossData { + uint64_t arrIdx; + uint64_t senderID; + + void pup(PUP::er &p) { + p|arrIdx; + p|senderID; + } +}; + #ifdef ANCHOR_ALGO struct anchorData { uint64_t arrIdx; @@ -26,7 +36,7 @@ struct anchorData { struct shortCircuitData { uint64_t arrIdx; - uint64_t grandparentID; + int64_t grandparentID; void pup(PUP::er &p) { p|arrIdx; diff --git a/unionFindLib.C b/unionFindLib.C index feb603b..fcfe32a 100644 --- a/unionFindLib.C +++ b/unionFindLib.C @@ -45,8 +45,21 @@ static void register_merge_count_maps_reduction() { // class function implementations +/** + * @brief registers a function that takes a vertexID and returns its location + * + * unionFindLib allows users to specify a vertexID scheme that suits their + * usecase, as long as it encodes the chare index of the vertex and array index + * of the vertex on the chare's myVertices field. This function registers the + * function provided by the user that achieves this decoding, so that the user's + * function may be used by unionFindLib for internal functionality + * + * @param gloc a function that takes a uint64_t vertexID and returns its chare + * index and local array index on that chare's myVertices field as + * a std::pair + */ void UnionFindLib:: -registerGetLocationFromID(std::pair (*gloc)(long int vid)) { +registerGetLocationFromID(std::pair (*gloc)(uint64_t vid)) { getLocationFromID = gloc; } @@ -58,35 +71,50 @@ register_phase_one_cb(CkCallback cb) { CkStartQD(cb); } +/** + * @brief Adds vertices to this union find chare + * + * Takes an array of unionFindVertex with vertex info populated (ID, etc.) + * and the length of that array, and stores locally the vertex info + * that should be associated with this union find chare. + * + * @param appVertices an array of unionFindVertex storing the vertices on the + * corresponding partition chare + * @param numVertices the number of vertices in the appVertices array + */ void UnionFindLib:: initialize_vertices(unionFindVertex *appVertices, int numVertices) { // local vertices corresponding to one treepiece in application numMyVertices = numVertices; - myVertices = appVertices; // no need to do memcpy, array in same address space as app - /*myVertices = (unionFindVertex*)malloc(numVertices * sizeof(unionFindVertex)); - memcpy(myVertices, appVertices, sizeof(unionFindVertex)*numVertices);*/ - /*for (int i = 0; i < numMyVertices; i++) { - CkPrintf("[LibProxy %d] myVertices[%d] - vertexID: %ld, parent: %ld, component: %d\n", this->thisIndex, i, myVertices[i].vertexID, myVertices[i].parent, myVertices[i].componentNumber); - }*/ + myVertices = appVertices; } +/** + * @brief performs a union on two vertices given their vertexIDs + * + * assumes the vertexIDs encode the information about the location of the vertex + * (it's chare index in the union find lib proxy and the local array index + * of the vertex). performs the actual union operation and carries + * it's associated runtime cost (cost depends on implementation selected) + */ #ifndef ANCHOR_ALGO void UnionFindLib:: -union_request(long int vid1, long int vid2) { +union_request(uint64_t vid1, uint64_t vid2) { + assert(vid1!=vid2); if (vid2 < vid1) { // found a back edge, flip and reprocess union_request(vid2, vid1); } else { - //std::pair vid1_loc = appPtr->getLocationFromID(vid1); std::pair vid1_loc = getLocationFromID(vid1); + //message the chare containing first vertex to find boss1 //pass the initilizer ID for initiating path compression findBossData d; d.arrIdx = vid1_loc.second; d.partnerOrBossID = vid2; - d.senderID = -1; // TODO: Is this okay? Or use INT_MIN + d.senderID = -1; d.isFBOne = 1; this->thisProxy[vid1_loc.first].insertDataFindBoss(d); @@ -97,7 +125,7 @@ union_request(long int vid1, long int vid2) { } #else void UnionFindLib:: -union_request(long int v, long int w) { +union_request(uint64_t v, uint64_t w) { std::pair w_loc = getLocationFromID(w); // message w to anchor to v anchorData d; @@ -109,8 +137,9 @@ union_request(long int v, long int w) { #ifndef ANCHOR_ALGO void UnionFindLib:: -find_boss1(int arrIdx, long int partnerID, long int senderID) { +find_boss1(int arrIdx, uint64_t partnerID, uint64_t senderID) { unionFindVertex *src = &myVertices[arrIdx]; + CkAssert(src->vertexID != src->parent); src->findOrAnchorCount++; if (src->parent == -1) { @@ -202,8 +231,9 @@ find_boss1(int arrIdx, long int partnerID, long int senderID) { void UnionFindLib:: -find_boss2(int arrIdx, long int boss1ID, long int senderID) { - unionFindVertex *src = &myVertices[arrIdx]; +find_boss2(int arrIdx, uint64_t boss1ID, uint64_t senderID) { + unionFindVertex *src = &myVertices[arrIdx]; // vid1, other field is vid2 (boss1ID) - same for find_boss1 + CkAssert(src->vertexID != src->parent); src->findOrAnchorCount++; if (src->parent == -1) { @@ -272,6 +302,7 @@ find_boss2(int arrIdx, long int boss1ID, long int senderID) { // check if sender and current vertex are on different chares if (senderID != -1 && !check_same_chares(senderID, curr->vertexID)) { // short circuit the sender to point to grandparent + std::pair sender_loc = getLocationFromID(senderID); shortCircuitData scd; scd.arrIdx = sender_loc.second; @@ -285,7 +316,7 @@ find_boss2(int arrIdx, long int boss1ID, long int senderID) { } #else void UnionFindLib:: -anchor(int w_arrIdx, long int v, long int path_base_arrIdx) { +anchor(int w_arrIdx, uint64_t v, long int path_base_arrIdx) { unionFindVertex *w = &myVertices[w_arrIdx]; w->findOrAnchorCount++; @@ -365,7 +396,7 @@ anchor(int w_arrIdx, long int v, long int path_base_arrIdx) { // perform local path compression void UnionFindLib:: -local_path_compression(unionFindVertex *src, long int compressedParent) { +local_path_compression(unionFindVertex *src, uint64_t compressedParent) { unionFindVertex* tmp; // An infinite loop if this function is called on itself (a node which does not have itself as its parent) while (src->parent != compressedParent) { @@ -378,7 +409,7 @@ local_path_compression(unionFindVertex *src, long int compressedParent) { // check if two vertices are on same chare bool UnionFindLib:: -check_same_chares(long int v1, long int v2) { +check_same_chares(uint64_t v1, uint64_t v2) { std::pair v1_loc = getLocationFromID(v1); std::pair v2_loc = getLocationFromID(v2); if (v1_loc.first == v2_loc.first) @@ -392,11 +423,12 @@ short_circuit_parent(shortCircuitData scd) { unionFindVertex *src = &myVertices[scd.arrIdx]; //CkPrintf("[TP %d] Short circuiting %ld from current parent %ld to grandparent %ld\n", thisIndex, src->vertexID, src->parent, grandparentID); src->parent = scd.grandparentID; + CkAssert(src->parent != src->vertexID); // TODO: remove assert } // function to implement simple path compression; currently unused void UnionFindLib:: -compress_path(int arrIdx, long int compressedParent) { +compress_path(int arrIdx, uint64_t compressedParent) { unionFindVertex *src = &myVertices[arrIdx]; //message the parent before reseting it if (src->vertexID != compressedParent) {//reached the top of path @@ -415,6 +447,13 @@ return_vertices() { /** Functions for finding connected components **/ +/** + * @brief After performing all union_request calls, labels connected components + * across all union find chares with coherent indexing starting with index 0 for + * component 0 + * + * @param cb Callback to be invoked after this function has finished + */ void UnionFindLib:: find_components(CkCallback cb) { postComponentLabelingCb = cb; @@ -433,8 +472,7 @@ find_components(CkCallback cb) { // send local count to prefix library CkCallback doneCb(CkReductionTarget(UnionFindLib, boss_count_prefix_done), thisProxy); - Prefix* myPrefixElem = prefixLibArray[thisIndex].ckLocal(); - myPrefixElem->startPrefixCalculation(myLocalNumBosses, doneCb); + prefixLibArray[thisIndex].startPrefixCalculation(myLocalNumBosses, doneCb); //CkPrintf("[%d] Local num bosses: %d\n", thisIndex, myLocalNumBosses); } @@ -446,7 +484,7 @@ boss_count_prefix_done(int totalCount) { Prefix* myPrefixElem = prefixLibArray[thisIndex].ckLocal(); int v = myPrefixElem->getValue(); int myStartIndex = v - myLocalNumBosses; - //CkPrintf("[%d] My start index: %d\n", thisIndex, myStartIndex); + CkAssert(myStartIndex >= 0); // start labeling my local bosses from myStartIndex // ensures sequential numbering of components @@ -487,7 +525,10 @@ start_component_labeling() { // an internal node or leaf node, request parent for boss std::pair parent_loc = getLocationFromID(v->parent); //this->thisProxy[parent_loc.first].need_boss(parent_loc.second, v->vertexID); - uint64_t data = ((uint64_t) parent_loc.second) << 32 | ((uint64_t) v->vertexID); + // uint64_t data = ((uint64_t) parent_loc.second) << 32 | ((uint64_t) v->vertexID); + needBossData data; + data.arrIdx = parent_loc.second; + data.senderID = v->vertexID; this->thisProxy[parent_loc.first].insertDataNeedBoss(data); } } @@ -512,9 +553,9 @@ insertDataFindBoss(const findBossData & data) { } void UnionFindLib:: -insertDataNeedBoss(const uint64_t & data) { - int arrIdx = (int)(data >> 32); - long int fromID = (long int)(data & 0xffffffff); +insertDataNeedBoss(const needBossData & data) { + int arrIdx = data.arrIdx; + uint64_t fromID = data.senderID; this->need_boss(arrIdx, fromID); } @@ -526,17 +567,18 @@ insertDataAnchor(const anchorData & data) { #endif void UnionFindLib:: -need_boss(int arrIdx, long int fromID) { - // one of children of this node needs boss, handle by either replying immediately - // or queueing the request +need_boss(int arrIdx, uint64_t fromID) { + // one of children of this node needs boss, handle by either + // replying immediately or queueing the request if (myVertices[arrIdx].componentNumber != -1) { // component already set, reply back std::pair requestor_loc = getLocationFromID(fromID); - if (requestor_loc.first == thisIndex) + if (requestor_loc.first == thisIndex) { set_component(requestor_loc.second, myVertices[arrIdx].componentNumber); - else + } else { this->thisProxy[requestor_loc.first].set_component(requestor_loc.second, myVertices[arrIdx].componentNumber); + } } else { // boss still not found, queue the request @@ -549,19 +591,28 @@ set_component(int arrIdx, long int compNum) { myVertices[arrIdx].componentNumber = compNum; // since component number is set, respond to your requestors - std::vector::iterator req_iter = myVertices[arrIdx].need_boss_requests.begin(); - while (req_iter != myVertices[arrIdx].need_boss_requests.end()) { - long int requestorID = *req_iter; + std::vector need_boss_queue = myVertices[arrIdx].need_boss_requests; + while (!need_boss_queue.empty()) { + uint64_t requestorID = (need_boss_queue).back(); std::pair requestor_loc = getLocationFromID(requestorID); - if (requestor_loc.first == thisIndex) + if (requestor_loc.first == thisIndex) { set_component(requestor_loc.second, compNum); - else + } else { this->thisProxy[requestor_loc.first].set_component(requestor_loc.second, compNum); + } // done with current requestor, delete from request queue - req_iter = myVertices[arrIdx].need_boss_requests.erase(req_iter); + need_boss_queue.pop_back(); } } +/** + * @brief discards components with number of vertices less than or equal to the + * threshold given and labels them with component number -1 + * + * @param threshold the minimum number of vertices for a component must be + * strictly greater than this number + * @param appReturnCb Callback to be invoked upon completion + */ void UnionFindLib:: prune_components(int threshold, CkCallback appReturnCb) { componentPruneThreshold = threshold; @@ -674,7 +725,19 @@ done_profiling(int total_count) { } } -// library initialization function +/** + * @brief initializes unionFindLib and returns a union find lib proxy + * + * Takes a chare array where vertices are stored and creates a union find chare + * array that is a shadow array of it. Intended so that when accessing vertices + * on the application level, one can easily make a invoke a local function + * on the corresponding union find chare using CkLocal() + * + * @param clientArray chare array that union find proxy will become shadow array + * of + * @param n number of chares in the clientArray + * @return CProxy_UnionFindLib the chare array union find proxy + */ CProxy_UnionFindLib UnionFindLib:: unionFindInit(CkArrayID clientArray, int n) { CkArrayOptions opts(n); diff --git a/unionFindLib.ci b/unionFindLib.ci index 80a76c0..29ba9f9 100644 --- a/unionFindLib.ci +++ b/unionFindLib.ci @@ -15,21 +15,21 @@ module unionFindLib { entry void register_phase_one_cb(CkCallback cb); // functions to build inverted trees #ifndef ANCHOR_ALGO - entry void find_boss1(int arrIdx, long partnerID, long initID); - entry void find_boss2(int arrIdx, long boss1ID, long initID); + entry void find_boss1(int arrIdx, uint64_t partnerID, uint64_t initID); + entry void find_boss2(int arrIdx, uint64_t boss1ID, uint64_t initID); #else - entry void anchor(int w_arrIdx, long v, long path_base_arrIdx); + entry void anchor(int w_arrIdx, uint64_t v, long path_base_arrIdx); #endif // function for grandparent short-circuiting entry [aggregate] void short_circuit_parent(shortCircuitData scd); // function for path compression support - entry void compress_path(int arrIdx, long compressedParent); + entry void compress_path(int arrIdx, uint64_t compressedParent); // functions to perform distributed connected components entry void find_components(CkCallback cb); entry [reductiontarget] void boss_count_prefix_done(int totalCount); - entry void need_boss(int arrIdx, int fromID); + entry void need_boss(int arrIdx, uint64_t fromID); entry void set_component(int arrIdx, int compNum); // functions to prune out small components @@ -40,7 +40,7 @@ module unionFindLib { // TRAM functions entry [aggregate] void insertDataFindBoss(const findBossData & data); - entry [aggregate] void insertDataNeedBoss(const uint64_t & data); + entry [aggregate] void insertDataNeedBoss(const needBossData & data); #ifdef ANCHOR_ALGO entry [aggregate] void insertDataAnchor(const anchorData & data); #endif diff --git a/unionFindLib.h b/unionFindLib.h index 12ccfe3..00e7b1d 100644 --- a/unionFindLib.h +++ b/unionFindLib.h @@ -5,10 +5,10 @@ #include struct unionFindVertex { - long int vertexID; - long int parent; + uint64_t vertexID; + int64_t parent; long int componentNumber = -1; - std::vector need_boss_requests; //request queue for processing need_boss requests + std::vector need_boss_requests; //request queue for processing need_boss requests long int findOrAnchorCount = 0; void pup(PUP::er &p) { @@ -41,7 +41,7 @@ class UnionFindLib : public CBase_UnionFindLib { int numMyVertices; int pathCompressionThreshold = 5; int componentPruneThreshold; - std::pair (*getLocationFromID)(long int vid); + std::pair (*getLocationFromID)(uint64_t vid); int myLocalNumBosses; int totalNumBosses; CkCallback postComponentLabelingCb; @@ -50,22 +50,22 @@ class UnionFindLib : public CBase_UnionFindLib { UnionFindLib() {} UnionFindLib(CkMigrateMessage *m) { } static CProxy_UnionFindLib unionFindInit(CkArrayID clientArray, int n); + void registerGetLocationFromID(std::pair (*gloc)(uint64_t vid)); void register_phase_one_cb(CkCallback cb); void initialize_vertices(unionFindVertex *appVertices, int numVertices); #ifndef ANCHOR_ALGO - void union_request(long int vid1, long int vid2); - void find_boss1(int arrIdx, long int partnerID, long int senderID); - void find_boss2(int arrIdx, long int boss1ID, long int senderID); + void union_request(uint64_t vid1, uint64_t vid2); + void find_boss1(int arrIdx, uint64_t partnerID, uint64_t senderID); + void find_boss2(int arrIdx, uint64_t boss1ID, uint64_t senderID); #else - void union_request(long int v, long int w); - void anchor(int w_arrIdx, long int v, long int path_base_arrIdx); + void union_request(uint64_t v, uint64_t w); + void anchor(int w_arrIdx, uint64_t v, long int path_base_arrIdx); #endif - void local_path_compression(unionFindVertex *src, long int compressedParent); - bool check_same_chares(long int v1, long int v2); + void local_path_compression(unionFindVertex *src, uint64_t compressedParent); + bool check_same_chares(uint64_t v1, uint64_t v2); void short_circuit_parent(shortCircuitData scd); - void compress_path(int arrIdx, long int compressedParent); - unionFindVertex* return_vertices(); - void registerGetLocationFromID(std::pair (*gloc)(long int v)); + void compress_path(int arrIdx, uint64_t compressedParent); + unionFindVertex *return_vertices(); // functions and data structures for finding connected components @@ -73,12 +73,12 @@ class UnionFindLib : public CBase_UnionFindLib { void find_components(CkCallback cb); void boss_count_prefix_done(int totalCount); void start_component_labeling(); - void insertDataNeedBoss(const uint64_t & data); + void insertDataNeedBoss(const needBossData & data); void insertDataFindBoss(const findBossData & data); #ifdef ANCHOR_ALGO void insertDataAnchor(const anchorData & data); #endif - void need_boss(int arrIdx, long int fromID); + void need_boss(int arrIdx, uint64_t fromID); void set_component(int arrIdx, long int compNum); void prune_components(int threshold, CkCallback appReturnCb); void perform_pruning();