diff --git a/Makefile b/Makefile index b1b391c9..f463433a 100644 --- a/Makefile +++ b/Makefile @@ -209,8 +209,8 @@ LDFLAGS := $(WARNING_FLAGS) $(foreach d, $(LDIRS), -Xlinker -rpath -Xlinker $(d) ifeq ($(BUILD),graph-rec) # -ftree-vectorize crashes Mnemosyne -CFLAGS+=-DGRAPH_RECOVERY -ftree-vectorize -O3 -DNDEBUG -CXXFLAGS+=-DGRAPH_RECOVERY -ftree-vectorize -O3 -DNDEBUG +CFLAGS+=-DGRAPH_RECOVERY -ftree-vectorize -O0 #-DNDEBUG +CXXFLAGS+=-DGRAPH_RECOVERY -ftree-vectorize -O0 #-DNDEBUG # we can add additional release customization here # e.g. link against different libraries, # define enviroment vars, etc. diff --git a/src/RGraph.hpp b/src/RGraph.hpp index 3115559f..7f27cac4 100644 --- a/src/RGraph.hpp +++ b/src/RGraph.hpp @@ -17,6 +17,8 @@ class RGraph : public Rideable{ */ virtual bool add_edge(int src, int dest, int weight) = 0; + virtual bool add_vertex(int vid) = 0; + virtual bool has_edge(int v1, int v2) = 0; /** @@ -26,15 +28,6 @@ class RGraph : public Rideable{ * @return True if the edge exists */ virtual bool remove_edge(int src, int dest) = 0; - - /** - * @brief Removes any edge from the selected vertex. - * - * @param src The integer id of the source node. - * @return true If an edge can be removed. - * @return false If an edge cannot be removed, i.e. non-allocated vertex - */ - virtual bool remove_any_edge(int src) = 0; /** * @brief Removes vertex from graph, along with the incoming and outgoing edges. @@ -48,33 +41,7 @@ class RGraph : public Rideable{ * @return false Vertex was already removed from the map. */ virtual bool remove_vertex(int vid) = 0; - - /** - * @brief Calls function fn on vertex of each outgoing edge - * - * Calls the function fn on the vertex of each outgoing edge. This is to implement something - * similar to a for-each loop, but in an implementation-defined way. Will run over all outgoing - * edges or until fn returns 'false' indicating no further processing is required. This acquires the - * lock on vid, and so users should be cautious of deadlock. - * - * @param vid Identifier of the vertex - * @param fn Predicate function that performs work on outgoing edge and returns whether or not to continue processing. - */ - virtual void for_each_outgoing(int vid, std::function fn) = 0; - - /** - * @brief Calls function fn on vertex of each outgoing edge - * - * Calls the function fn on the vertex of each outgoing edge. This is to implement something - * similar to a for-each loop, but in an implementation-defined way. Will run over all outgoing - * edges or until fn returns 'false' indicating no further processing is required. This acquires the - * lock on vid, and so users should be cautious of deadlock. - * - * @param vid Identifier of the vertex - * @param fn Predicate function that performs work on outgoing edge and returns whether or not to continue processing. - */ - virtual void for_each_incoming(int vid, std::function fn) = 0; - + /** * @brief Obtains statistics including (|V|, |E|, average degree, vertex degrees, vertex degrees length) * diff --git a/src/main.cpp b/src/main.cpp index fe618c8c..7f5da496 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -87,7 +87,7 @@ int main(int argc, char *argv[]) { GlobalTestConfig gtc; const size_t numVertices = 1024; - const size_t meanEdgesPerVertex = 20; + const size_t meanEdgesPerVertex = 128; const size_t vertexLoad = 50; /* queues */ @@ -144,7 +144,7 @@ int main(int argc, char *argv[]) #ifndef MNEMOSYNE gtc.addTestOption(new RecoverVerifyTest(), "RecoverVerifyTest"); - gtc.addTestOption(new GraphTest(1000000,numVertices, meanEdgesPerVertex), "GraphTest:1m:i33r33l33:c1"); + gtc.addTestOption(new GraphTest(1024,numVertices, meanEdgesPerVertex), "GraphTest:1m:i33r33l33:c1"); // gtc.addTestOption(new GraphRecoveryTest("graph_data/", "orkut-edge-list_", 28610, 5, true), "GraphRecoveryTest:Orkut:verify"); // gtc.addTestOption(new GraphRecoveryTest("graph_data/", "orkut-edge-list_", 28610, 5, false), "GraphRecoveryTest:Orkut:noverify"); gtc.addTestOption(new TGraphConstructionTest("graph_data/", "orkut-edge-list_", 28610, 5), "TGraphConstructionTest:Orkut"); diff --git a/src/rideables/TGraph.hpp b/src/rideables/TGraph.hpp index d86d34d2..af899614 100644 --- a/src/rideables/TGraph.hpp +++ b/src/rideables/TGraph.hpp @@ -36,29 +36,11 @@ class TGraph : public RGraph{ // 'deleter' function to control whether or not it will try to delete the wrapped pointer below // https://stackoverflow.com/a/17853770/4111188 - template - struct maybe_deleter { - bool _delete; - explicit maybe_deleter(bool doIt = true) : _delete(doIt){} - - void operator()(T *p) { - if (_delete) delete p; - } - }; - - template - using set_shared_ptr = std::shared_ptr; - - template - set_shared_ptr make_find_ptr(T *raw) { - return set_shared_ptr(raw, maybe_deleter(false)); - } - class Relation; class Vertex { public: - std::unordered_set> adjacency_list; - std::unordered_set> dest_list; + std::unordered_set adjacency_list; + std::unordered_set dest_list; int id; int lbl; Vertex(int id, int lbl): id(id), lbl(lbl){} @@ -93,6 +75,7 @@ class TGraph : public RGraph{ // Allocates data structures and pre-loads the graph TGraph(GlobalTestConfig* gtc) { + srand(time(NULL)); this->idxToVertex = new Vertex*[numVertices]; this->vertexLocks = new std::atomic[numVertices]; this->vertexSeqs = new uint32_t[numVertices]; @@ -115,16 +98,17 @@ class TGraph : public RGraph{ // Fill to mean edges per vertex for (int i = 0; i < numVertices; i++) { - for (int i = 0; i < meanEdgesPerVertex * 100 / vertexLoad; i++) { - if (idxToVertex[i] == nullptr) continue; - int j = verticesRNG(gen); - while (j == i) { - j = verticesRNG(gen); + if (idxToVertex[i] == nullptr) continue; + for (int j = 0; j < meanEdgesPerVertex * 100 / vertexLoad; j++) { + int k = verticesRNG(gen); + while (k == i) { + k = verticesRNG(gen); } - if (idxToVertex[j] != nullptr) { - auto r = make_shared(i,j,-1); - source(i).insert(r); - destination(j).insert(r); + if (idxToVertex[k] != nullptr) { + Relation *in = new Relation(i, k, -1); + Relation *out = new Relation(i, k, -1); + source(i).insert(in); + destination(k).insert(out); } } } @@ -185,16 +169,17 @@ class TGraph : public RGraph{ lock(dest); } - if (idxToVertex[src] == nullptr) { - idxToVertex[src] = new Vertex(src, src); - } - if (idxToVertex[dest] == nullptr) { - idxToVertex[dest] = new Vertex(dest, dest); - } Relation r(src,dest,weight); auto& srcSet = source(src); auto& destSet = destination(dest); + + // Note: We do not create a vertex if one is not found + // also we do not add an edge even if it is found some of the time + // to enable even constant load factor + if (idxToVertex[src] == nullptr || idxToVertex[dest] == nullptr) { + goto exitEarly; + } if (has_relation(srcSet, &r)) { // Sanity check assert(has_relation(destSet, &r)); @@ -202,9 +187,10 @@ class TGraph : public RGraph{ } { - std::shared_ptr rel = std::make_shared(src, dest, weight); - srcSet.insert(rel); - destSet.insert(rel); + Relation *out = new Relation(src, dest, weight); + Relation *in = new Relation(src, dest, weight); + srcSet.insert(out); + destSet.insert(in); inc_seq(src); inc_seq(dest); retval = true; @@ -272,36 +258,49 @@ class TGraph : public RGraph{ return true; } - bool remove_any_edge(int vid) { - lock(vid); - int src = -1; - int dest = -1; - - if (idxToVertex[vid] != nullptr) { - // Check source first - auto search = source(vid).begin(); - if (search == source(vid).end()) { - // Then destination - search = destination(vid).begin(); - if (search == destination(vid).end()) { - goto failure; - } + bool add_vertex(int vid) { + std::mt19937_64 vertexGen; + std::uniform_int_distribution<> uniformVertex(0,numVertices); + bool retval = true; + // Randomly sample vertices... + std::vector vec(meanEdgesPerVertex); + for (size_t i = 0; i < meanEdgesPerVertex; i++) { + int u = uniformVertex(vertexGen); + while (u == i) { + u = uniformVertex(vertexGen); } - std::shared_ptr r = *search; - src = r->src; - dest = r->dest; + vec.push_back(u); } - - failure: - unlock(vid); - if (src == -1 || dest == -1) { - return false; + vec.push_back(vid); + std::sort(vec.begin(), vec.end()); + vec.erase(std::unique(vec.begin(), vec.end()), vec.end()); + + for (int u : vec) { + lock(u); + } + + if (idxToVertex[vid] == nullptr) { + idxToVertex[vid] = new Vertex(vid, vid); + for (int u : vec) { + if (idxToVertex[u] == nullptr) continue; + if (u == vid) continue; + Relation *in = new Relation(vid, u, -1); + Relation *out = new Relation(vid, u, -1); + source(vid).insert(in); + destination(u).insert(out); + } } else { - return remove_edge(src, dest); + retval = false; } + + std::reverse(vec.begin(), vec.end()); + for (int u : vec) { + if (idxToVertex[vid] != nullptr && idxToVertex[u] != nullptr) inc_seq(u); + unlock(u); + } + return retval; } - bool remove_vertex(int vid) { startOver: { @@ -328,6 +327,16 @@ class TGraph : public RGraph{ unlock(vid); for (int _vid : vertices) { lock(_vid); + if (!(idxToVertex[_vid] != nullptr || get_seq(vid) != seq)) { + for (auto r : source(vid)) { + if (r->dest == _vid) + std::cout << "(" << r->src << "," << r->dest << ")" << std::endl; + } + for (auto r : destination(vid)) { + if (r->src == _vid) + std::cout << "(" << r->src << "," << r->dest << ")" << std::endl; + } + } } // Has vertex been changed? Start over @@ -344,25 +353,20 @@ class TGraph : public RGraph{ // vertices that relate to this vertex for (int other : vertices) { if (other == vid) continue; - std::vector toRemoveList; - - Relation src(other, vid, -1); - Relation dst(vid, other, -1); - remove_relation(source(other), &src); - remove_relation(destination(other), &dst); - // Last relation, delete this vertex - if (source(other).size() == 0 && destination(other).size() == 0) { - destroy(other); - } - } + Relation src(vid, other, -1); + Relation dest(other, vid, -1); + remove_relation(source(other), &dest); + remove_relation(destination(other), &src); + } - // Step 4: Delete edges, clear set of src and dest edges, then delete the vertex itself - source(vid).clear(); - destination(vid).clear(); + std::vector toDelete(source(vid).size() + destination(vid).size()); + for (auto r : source(vid)) toDelete.push_back(r); + for (auto r : destination(vid)) toDelete.push_back(r); destroy(vid); + for (auto r : toDelete) delete r; - // Step 5: Release in reverse order + // Step 4: Release in reverse order std::reverse(vertices.begin(), vertices.end()); for (int _vid : vertices) { inc_seq(_vid); @@ -372,26 +376,6 @@ class TGraph : public RGraph{ return true; } - void for_each_outgoing(int vid, std::function fn) { - lock(vid); - for (auto r : source(vid)) { - if (!fn(r->dest)) { - break; - } - } - unlock(vid); - } - - void for_each_incoming(int vid, std::function fn) { - lock(vid); - for (auto r : destination(vid)) { - if (!fn(r->src)) { - break; - } - } - unlock(vid); - } - private: void lock(size_t idx) { std::atomic& lck = vertexLocks[idx]; @@ -421,24 +405,27 @@ class TGraph : public RGraph{ } // Incoming edges - std::unordered_set>& source(int idx) { + std::unordered_set& source(int idx) { return idxToVertex[idx]->adjacency_list; + } // Outgoing edges - std::unordered_set>& destination(int idx) { + std::unordered_set& destination(int idx) { return idxToVertex[idx]->dest_list; } - bool has_relation(std::unordered_set>& set, Relation *r) { - auto search = set.find(make_find_ptr(r)); + bool has_relation(std::unordered_set& set, Relation *r) { + auto search = set.find(r); return search != set.end(); } - void remove_relation(std::unordered_set>& set, Relation *r) { - auto search = set.find(make_find_ptr(r)); + void remove_relation(std::unordered_set& set, Relation *r) { + auto search = set.find(r); if (search != set.end()) { + Relation *tmp = *search; set.erase(search); + delete tmp; } } }; diff --git a/src/tests/GraphTest.hpp b/src/tests/GraphTest.hpp index f09e9120..68359ce1 100644 --- a/src/tests/GraphTest.hpp +++ b/src/tests/GraphTest.hpp @@ -51,23 +51,6 @@ class GraphTest : public Test { total_ops(numOps), max_verts(max_verts), desiredAvgDegree(desiredAvgDegree) { } - // New ratio is based on delta = averageDegree - desiredAvgDegree - // When delta = 0, it defaults to (33%,33%,33%,1%), but we adjust based on delta such that: - // 1) insertProb (33% + delta) + removalProb (33% - delta) = 66% - // 2) clearProb = min(0, max(1, delta / 33%)) - // 3) lookupProb = 100% - insertProb - removalProb - clearProb - void update_ratio(double averageDegree) { - int sign = averageDegree > desiredAvgDegree ? -1 : averageDegree < desiredAvgDegree ? 1 : 0; - double ratio = min(2.0, max((double) desiredAvgDegree / averageDegree - 1, averageDegree / (double) desiredAvgDegree - 1)); - int delta = 1650 * ratio * sign; // 16.5% is half of 33% - insertionProb = 3300 + delta; - removalProb = 3300 - delta; - clearProb = min(0.0, max(1.0, (double) delta / 3300.0)) * 100; - lookupProb = 10000 - insertionProb - removalProb - clearProb; - std::cout << "sign(" << sign << "), ratio(" << ratio << "), delta(" << delta << ")" << std::endl; - std::cout << "(" << insertionProb / 100.0 << "," << removalProb / 100.0 << "," << lookupProb / 100.0 << "," << clearProb / 100.0 << ")" << std::endl; - } - void init(GlobalTestConfig *gtc) { uint64_t new_ops = total_ops / gtc->task_num; thd_ops = new uint64_t[gtc->task_num]; @@ -88,7 +71,10 @@ class GraphTest : public Test { gtc->interval = numeric_limits::max(); auto stats = g->grab_stats(); std::apply(print_stats, stats); - update_ratio(std::get<2>(stats)); + insertionProb = 3300; + removalProb = 3300; + lookupProb = 1650; + clearProb = 1650; workingThreads = gtc->task_num; threadsDone = 0; } @@ -99,33 +85,17 @@ class GraphTest : public Test { std::mt19937_64 gen_v(ltc->seed + 1); std::uniform_int_distribution<> dist(0,9999); std::uniform_int_distribution<> distv(0,max_verts-1); - for (size_t i = 0; i < thd_ops[ltc->tid]; i++) { - if ((i+1) % 1000 == 0) { - threadsDone++; - while (threadsDone != workingThreads) { - pthread_yield(); - } - if (tid == 0) { - auto stats = g->grab_stats(); - std::apply(print_stats, stats); - update_ratio(std::get<2>(stats)); - threadsDone = 0; - } else { - while (threadsDone == workingThreads) { - pthread_yield(); - } - } - } - int rng = dist(gen_p); + int rng = dist(gen_p); + for (size_t i = 0; i < thd_ops[tid]; i++) { if (rng <= insertionProb) { // std::cout << "rng(" << rng << ") is add_edge <= " << insertionProb << std::endl; g->add_edge(distv(gen_v), distv(gen_v), -1); } else if (rng <= insertionProb + removalProb) { // std::cout << "rng(" << rng << ") is remove_any_edge <= " << insertionProb + removalProb << std::endl; - g->remove_any_edge(distv(gen_v)); + g->remove_edge(distv(gen_v), distv(gen_v)); } else if (rng <= insertionProb + removalProb + lookupProb) { // std::cout << "rng(" << rng << ") is has_edge <= " << insertionProb + removalProb + lookupProb << std::endl; - g->has_edge(distv(gen_v), distv(gen_v)); + g->add_vertex(distv(gen_v)); } else { // std::cout << "rng(" << rng << ") is remove_vertex..."; g->remove_vertex(distv(gen_v)); @@ -135,6 +105,8 @@ class GraphTest : public Test { } void cleanup(GlobalTestConfig *gtc) { + auto stats = g->grab_stats(); + std::apply(print_stats, stats); delete g; }