diff --git a/src/RGraph.hpp b/src/RGraph.hpp index e6d3fd0c..ab483c00 100644 --- a/src/RGraph.hpp +++ b/src/RGraph.hpp @@ -65,6 +65,13 @@ class RGraph : public Rideable{ * @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) + * + * @return std::tuple Tuple of |V|, |E|, average degree, and histogram + */ + virtual std::tuple grab_stats() = 0; }; diff --git a/src/main.cpp b/src/main.cpp index f3c680af..91d49021 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include "ConcurrentPrimitives.hpp" @@ -44,9 +45,9 @@ #include "NatarajanTree.hpp" #include "TGraph.hpp" -#include "NVMGraph.hpp" -#include "DLGraph.hpp" -#include "MontageGraph.hpp" +// #include "NVMGraph.hpp" +// #include "DLGraph.hpp" +// #include "MontageGraph.hpp" #endif #ifdef MNEMOSYNE @@ -73,7 +74,7 @@ #include "SyncTest.hpp" #ifndef MNEMOSYNE #include "RecoverVerifyTest.hpp" -#include "GraphRecoveryTest.hpp" +// #include "GraphRecoveryTest.hpp" #include "TGraphConstructionTest.hpp" #include "ToyTest.hpp" #endif /* !MNEMOSYNE */ @@ -81,10 +82,13 @@ using namespace std; + int main(int argc, char *argv[]) { - const size_t numVertices = 1024 * 1024; GlobalTestConfig gtc; + const size_t numVertices = 1024; + const size_t meanEdgesPerVertex = 32; + const size_t vertexLoad = 50; /* queues */ // gtc.addRideableOption(new MSQueueFactory(), "MSQueue");//transient @@ -111,13 +115,13 @@ int main(int argc, char *argv[]) gtc.addRideableOption(new MontageNatarajanTreeFactory(), "MontageNataTree"); /* graphs */ - gtc.addRideableOption(new TGraphFactory(), "TGraph"); - gtc.addRideableOption(new NVMGraphFactory(), "NVMGraph"); + gtc.addRideableOption(new TGraphFactory(), "TGraph"); + // gtc.addRideableOption(new NVMGraphFactory(), "NVMGraph"); // gtc.addRideableOption(new DLGraphFactory(), "DLGraph"); - gtc.addRideableOption(new MontageGraphFactory(), "MontageGraph"); + // gtc.addRideableOption(new MontageGraphFactory(), "MontageGraph"); - gtc.addRideableOption(new MontageGraphFactory<3072627>(), "Orkut"); - gtc.addRideableOption(new TGraphFactory<3076727>(), "TransientOrkut"); + // gtc.addRideableOption(new MontageGraphFactory<3072627>(), "Orkut"); + gtc.addRideableOption(new TGraphFactory<3076727, 0, 100>(), "TransientOrkut"); #endif /* !defined(MNEMOSYNE) and !defined(PRONTO) */ #ifdef MNEMOSYNE gtc.addRideableOption(new MneQueueFactory(), "MneQueue"); @@ -142,7 +146,7 @@ int main(int argc, char *argv[]) gtc.addTestOption(new GraphTest(1000000,numVertices,33,33,33, 1), "GraphTest:1m:i33r33l33:c1"); gtc.addTestOption(new GraphTest(1000000,numVertices,25,25,25,25), "GraphTest:1m:i25r25l25:c25"); - 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, 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"); #endif /* !MNEMOSYNE */ diff --git a/src/rideables/TGraph.hpp b/src/rideables/TGraph.hpp index bdb558ee..44986927 100644 --- a/src/rideables/TGraph.hpp +++ b/src/rideables/TGraph.hpp @@ -1,7 +1,7 @@ /** * Author: Louis Jenkins & Benjamin Valpey * Date: 31 Mar 2020 - * Filename: PGraph.hpp + * Filename: TGraph.hpp * Description: A simple implementation of a Transient Graph */ @@ -18,19 +18,47 @@ #include #include #include "RCUTracker.hpp" +#include +#include + +// #pragma GCC optimize ("O0") /** * SimpleGraph class. Labels are of templated type K. */ -template +template class TGraph : public RGraph{ public: + + // We use smart pointers in the unordered_set, but we can only lookup by a key allocated + // on the stack if and only if it is also wrapped into a smart pointer. We create a custom + // '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){} @@ -45,15 +73,6 @@ class TGraph : public RGraph{ int get_id() { return id; } - - void lock() { - lck.lock(); - } - - void unlock() { - lck.unlock(); - } - }; class Relation { @@ -77,11 +96,59 @@ class TGraph : public RGraph{ idxToVertex = new Vertex*[numVertices]; vertexLocks = new std::atomic[numVertices]; vertexSeqs = new uint32_t[numVertices]; + std::mt19937_64 gen(0xDEADBEEF); + std::uniform_int_distribution<> verticesRNG(0, numVertices - 1); + std::uniform_int_distribution<> coinflipRNG(0, 100); + std::cout << "Allocated core..." << std::endl; + // Fill to vertexLoad + for (int i = 0; i < numVertices; i++) { + if (coinflipRNG(gen) <= vertexLoad) { + idxToVertex[i] = new Vertex(i,i); + } else { + idxToVertex[i] = nullptr; + } + vertexLocks[i] = false; + vertexSeqs = 0; + } + + std::cout << "Filled vertexLoad" << std::endl; - // Initialize... - for (size_t i = 0; i < numVertices; i++) { - idxToVertex[i] = new Vertex(i, -1); + // Fill to mean edges per vertex + for (int i = 0; i < numVertices; i++) { + for (int i = 0; i < meanEdgesPerVertex; i++) { + if (idxToVertex[i] == nullptr) continue; + int j = verticesRNG(gen); + while (j == i) { + j = verticesRNG(gen); + } + if (idxToVertex[j] != nullptr) { + auto r = make_shared(i,j,-1); + source(i).insert(r); + destination(j).insert(r); + } + } } + std::cout << "Filled mean edges per vertex" << std::endl; + } + + // Obtain statistics of graph (|V|, |E|, average degree, vertex degrees) + // Not concurrent safe... + std::tuple grab_stats() { + int numV = 0; + int numE = 0; + int *degrees = new int[numVertices]; + double averageEdgeDegree = 0; + for (auto i = 0; i < numVertices; i++) { + if (idxToVertex[i] != nullptr) { + numV++; + numE += source(i).size(); + degrees[i] = source(i).size() + destination(i).size(); + } else { + degrees[i] = 0; + } + } + averageEdgeDegree = numE / ((double) numV); + return std::make_tuple(numV, numE, averageEdgeDegree, degrees, numVertices); } Vertex** idxToVertex; // Transient set of transient vertices to index map @@ -90,10 +157,10 @@ class TGraph : public RGraph{ // Thread-safe and does not leak edges void clear() { - for (int i = 0; i < numVertices; i++) { + for (auto i = 0; i < numVertices; i++) { lock(i); } - for (int i = 0; i < numVertices; i++) { + for (auto i = 0; i < numVertices; i++) { for (Relation *r : idxToVertex[i]->adjacency_list) { delete r; } @@ -128,7 +195,7 @@ class TGraph : public RGraph{ } { - Relation *rel = new Relation(src, dest, weight); + std::shared_ptr rel = std::make_shared(src, dest, weight); srcSet.insert(rel); destSet.insert(rel); inc_seq(src); @@ -170,7 +237,7 @@ class TGraph : public RGraph{ if (src == dest) return false; if (src > dest) { lock(dest); - lock(src) + lock(src); } else { lock(src); lock(dest); @@ -201,10 +268,10 @@ class TGraph : public RGraph{ std::vector vertices; lock(vid); uint32_t seq = get_seq(vid); - for (Relation *r : source(vid)) { + for (auto r : source(vid)) { vertices.push_back(r->dest); } - for (Relation *r : destination(vid)) { + for (auto r : destination(vid)) { vertices.push_back(r->src); } @@ -246,14 +313,8 @@ class TGraph : public RGraph{ } // Step 4: Delete edges, clear set of src and dest edges, then delete the vertex itself - std::vector garbageList(source(vid).size() + destination(vid).size()); - garbageList.insert(garbageList.begin(), source(vid).begin(), source(vid).end()); - garbageList.insert(garbageList.begin(), destination(vid).begin(), destination(vid).end()); source(vid).clear(); destination(vid).clear(); - for (Relation *r : garbageList) { - delete r; - } destroy(vid); // Step 5: Release in reverse order @@ -267,23 +328,23 @@ class TGraph : public RGraph{ } void for_each_outgoing(int vid, std::function fn) { - lock(v); - for (Relation *r : source(v)) { + lock(vid); + for (auto r : source(vid)) { if (!fn(r->dest)) { break; } } - unlock(v); + unlock(vid); } void for_each_incoming(int vid, std::function fn) { - lock(v); - for (Relation *r : destination(v)) { + lock(vid); + for (auto r : destination(vid)) { if (!fn(r->src)) { break; } } - unlock(v); + unlock(vid); } private: @@ -315,33 +376,35 @@ class TGraph : public RGraph{ } // Incoming edges - std::unordered_set& source(size_t idx) { + std::unordered_set>& source(int idx) { return idxToVertex[idx]->adjacency_list; } // Outgoing edges - std::unordered_set& destination(size_t 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(r); + bool has_relation(std::unordered_set>& set, Relation *r) { + auto search = set.find(make_find_ptr(r)); return search != set.end(); } - void remove_relation(std::unordered_set& set, Relation *r) { - auto search = set.find(r); + void remove_relation(std::unordered_set>& set, Relation *r) { + auto search = set.find(make_find_ptr(r)); if (search != set.end()) { set.erase(search); } } }; -template +template class TGraphFactory : public RideableFactory{ Rideable *build(GlobalTestConfig *gtc){ - return new TGraph(gtc); + return new TGraph(gtc); } }; +// #pragma GCC reset_options #endif + diff --git a/src/tests/GraphTest.hpp b/src/tests/GraphTest.hpp index 9b56a9bc..d3ef1230 100644 --- a/src/tests/GraphTest.hpp +++ b/src/tests/GraphTest.hpp @@ -8,20 +8,28 @@ #include "RGraph.hpp" #include "Recoverable.hpp" #include +#include +#include +#include -// void ErdosRenyi(RGraph *g, int numVertices, double p=0.5) { -// size_t x = numVertices; -// size_t numEdges = (x * x) * p; -// #pragma omp parallel -// { -// std::mt19937_64 gen_p(std::chrono::system_clock::now().time_since_epoch().count() + omp_get_thread_num()); -// Recoverable::init_thread(omp_get_thread_num()); -// #pragma omp for -// for (size_t i = 0; i < numEdges; i++) { -// g->add_edge(gen_p() % numVertices, gen_p() % numVertices, 1); -// } -// } -// } +static void print_stats(int numV, int numE, double averageDegree, int *vertexDegrees, int vertexDegreesLength) { + int maxDegree = 0; + for (auto i = 0; i < vertexDegreesLength; i++) { + maxDegree = max(maxDegree, vertexDegrees[i]); + } + std::cout << "|V|=" << numV << ",|E|=" << numE << ",average degree = " << averageDegree << ",maximum degree = " << maxDegree << std::endl; + using namespace boost::histogram; + + auto h = make_histogram(axis::regular<>(maxDegree, 0, maxDegree)); + for (auto i = 0; i < vertexDegreesLength; i++) { + h(vertexDegrees[i]); + } + for (auto&& x : indexed(h)) { + if (*x == 0) continue; + std::cout << boost::format("bin %i [ %.1f, %.1f ): %i\n") + % x.index() % x.bin().lower() % x.bin().upper() % *x; + } +} class GraphTest : public Test { public: @@ -55,7 +63,7 @@ class GraphTest : public Test { if (new_ops * gtc->task_num != total_ops) { thd_ops[0] += (total_ops - new_ops * gtc->task_num); } - + Rideable* ptr = gtc->allocRideable(); g = dynamic_cast(ptr); if(!g){ @@ -64,29 +72,16 @@ class GraphTest : public Test { /* set interval to inf so this won't be killed by timeout */ gtc->interval = numeric_limits::max(); + auto stats = g->grab_stats(); + std::apply(print_stats, stats); } int execute(GlobalTestConfig *gtc, LocalTestConfig *ltc) { int tid = ltc->tid; std::mt19937_64 gen_p(ltc->seed); - std::mt19937_64 gen_v(ltc->seed + 1); for (size_t i = 0; i < thd_ops[ltc->tid]; i++) { - unsigned p = gen_p()%100; - int src = gen_v() % max_verts; - int dest = gen_v() % max_verts; - if (padd_edge(src, dest, 1); - } else if (pfor_each_edge(src, [&dest](int v) { dest = v; return false; }); - g->remove_edge(src, dest); - } else if (p < prop_lookup) { - //std::cout << "Lookup(" << src << "," << dest << ")" << std::endl; - g->has_edge(src, dest); - } else { - g->clear_vertex(src); - } + // g->has_edge(0, 1, -1); + // if (true) break; } return thd_ops[ltc->tid]; } @@ -97,21 +92,7 @@ class GraphTest : public Test { void parInit(GlobalTestConfig *gtc, LocalTestConfig *ltc) { g->init_thread(gtc, ltc); - size_t x = max_verts; - size_t numEdges = (x * x) * 0.5; - std::random_device rd; - std::mt19937_64 gen_p(std::chrono::system_clock::now().time_since_epoch().count() + ltc->tid); - std::uniform_int_distribution<> distrib(0, max_verts - 1); - std::normal_distribution norm(10,3); - std::default_random_engine generator; - for (uint64_t i = ltc->tid; i < max_verts; i += gtc->task_num) { - int n = max(1, (int) round(norm(generator))); - for (int j = 0; j < n; j++) { - uint64_t k = round(distrib(gen_p)); - while (k == i) k = round(distrib(gen_p)); - g->add_edge(i, k, 1); - } - } + usleep(1 * 1000 * 1000); } }; #endif