diff --git a/inc/Graph.hxx b/inc/Graph.hxx index d295dd6..badf6be 100644 --- a/inc/Graph.hxx +++ b/inc/Graph.hxx @@ -722,12 +722,12 @@ class ArenaDiGraph { /** - * Set the degree of a vertex in the graph. + * Set the degree of a vertex in the graph, without "any" checks. * @param u vertex id * @param d new degree of the vertex - * @note `update()` must be called after all degrees are set. + * @note Ensure that the vertex exists, and it has at least `d` edges. */ - inline void setDegreeUnchecked(K u, K d) { + inline void setDegreeUnsafe(K u, K d) { degrees[u] = d; } @@ -737,10 +737,12 @@ class ArenaDiGraph { * @param u source vertex id * @param v target vertex id * @param w associated weight of the edge + * @note Ensure that the vertex exists, and enough capacity is reserved. */ - inline void addEdgeUnchecked(K u, K v, E w=E()) { + inline void addEdgeUnsafe(K u, K v, E w=E()) { auto *ptr = edges[u]; - ptr[degrees[u]++] = {v, w}; + K i = degrees[u]++; + ptr[i] = {v, w}; } @@ -749,8 +751,9 @@ class ArenaDiGraph { * @param u source vertex id * @param v target vertex id * @param w associated weight of the edge + * @note Ensure that the vertex exists, and enough capacity is reserved. */ - inline void addEdgeUncheckedOmp(K u, K v, E w=E()) { + inline void addEdgeUnsafeOmp(K u, K v, E w=E()) { auto *ptr = edges[u]; K i = K(); #pragma omp atomic capture @@ -759,6 +762,30 @@ class ArenaDiGraph { } + /** + * Add an outgoing edge to the graph, without uniqueness check. + * @param u source vertex id + * @param v target vertex id + * @param w associated weight of the edge + * @note Ensure that the span of the graph is sufficient. + */ + inline void addEdgeUnchecked(K u, K v, E w=E()) { + if (!getBit(exists, u)) setBit(exists, u); + if (!getBit(exists, v)) setBit(exists, v); + K i = degrees[u]++; + if (i >= capacities[u]) { + K cap = allocationCapacity(degrees[u]); + pair *tmp = allocate(cap); + if (i>0) memcpy(tmp, edges[u], i*EDGE); + if (i>0) deallocate(edges[u], capacities[u]); + edges[u] = tmp; + capacities[u] = cap; + } + pair *ptr = edges[u]; + ptr[i] = {v, w}; + } + + /** * Remove outgoing edges from a vertex in the graph. * @param u source vertex id @@ -797,7 +824,7 @@ class ArenaDiGraph { * @param ie end iterator of edges to add * @note [ib, ie) must be sorted and unique. */ - template + template inline void addEdges(K u, I ib, I ie) { if (!hasVertex(u) || ib==ie) return; auto *eb = edges[u], *ee = edges[u] + degrees[u]; @@ -1590,7 +1617,7 @@ inline void subtractGraphW(H& a, const GX& x, const GY& y) { auto xb = x.beginEdges(u), xe = x.endEdges(u); auto ab = a.beginEdges(u); auto it = copy(xb, xe, ab); - a.setDegreeUnchecked(u, it - ab); + a.setDegreeUnsafe(u, it - ab); }); // Now add edges of vertices that are touched. y.forEachVertexKey([&](auto u) { @@ -1600,7 +1627,7 @@ inline void subtractGraphW(H& a, const GX& x, const GY& y) { auto ab = a.beginEdges(u); auto fl = [](const auto& a, const auto& b) { return a.first < b.first; }; auto it = set_difference(xb, xe, yb, ye, ab, fl); - a.setDegreeUnchecked(u, it - ab); + a.setDegreeUnsafe(u, it - ab); }); a.update(true, true); } @@ -1638,7 +1665,7 @@ inline void subtractGraphOmpW(H& a, const GX& x, const GY& y) { auto xb = x.beginEdges(u), xe = x.endEdges(u); auto ab = a.beginEdges(u); auto it = copy(xb, xe, ab); - a.setDegreeUnchecked(u, it - ab); + a.setDegreeUnsafe(u, it - ab); } // Now add edges of vertices that are touched. #pragma omp parallel for schedule(dynamic, 2048) @@ -1649,7 +1676,7 @@ inline void subtractGraphOmpW(H& a, const GX& x, const GY& y) { auto ab = a.beginEdges(u); auto fl = [](const auto& a, const auto& b) { return a.first < b.first; }; auto it = set_difference(xb, xe, yb, ye, ab, fl); - a.setDegreeUnchecked(u, it - ab); + a.setDegreeUnsafe(u, it - ab); } a.updateOmp(true, true); } @@ -1738,7 +1765,7 @@ inline void addGraphW(H& a, const GX& x, const GY& y) { auto ab = a.beginEdges(u); auto fl = [](const auto& a, const auto& b) { return a.first < b.first; }; auto it = set_union(xb, xe, yb, ye, ab, fl); - a.setDegreeUnchecked(u, it - ab); + a.setDegreeUnsafe(u, it - ab); } a.update(true, true); } @@ -1779,7 +1806,7 @@ inline void addGraphOmpW(H& a, const GX& x, const GY& y) { auto ab = a.beginEdges(u); auto fl = [](const auto& a, const auto& b) { return a.first < b.first; }; auto it = set_union(xb, xe, yb, ye, ab, fl); - a.setDegreeUnchecked(u, it - ab); + a.setDegreeUnsafe(u, it - ab); } a.updateOmp(true, true); } diff --git a/inc/_vector.hxx b/inc/_vector.hxx index 6fd38e6..b2189bc 100644 --- a/inc/_vector.hxx +++ b/inc/_vector.hxx @@ -40,7 +40,7 @@ using vector2d = vector>; template inline bool getBit(const T *x, size_t i) { constexpr size_t WORD = 8 * sizeof(T); - return x[i / WORD] & (1 << (i % WORD)); + return (x[i / WORD] & (T(1) << (i % WORD))) != 0; } @@ -52,7 +52,7 @@ inline bool getBit(const T *x, size_t i) { template inline void setBit(T *x, size_t i) { constexpr size_t WORD = 8 * sizeof(T); - x[i / WORD] |= (1 << (i % WORD)); + x[i / WORD] |= (T(1) << (i % WORD)); } @@ -64,7 +64,7 @@ inline void setBit(T *x, size_t i) { template inline void clearBit(T *x, size_t i) { constexpr size_t WORD = 8 * sizeof(T); - x[i / WORD] &= ~(1 << (i % WORD)); + x[i / WORD] &= ~(T(1) << (i % WORD)); } #pragma endregion diff --git a/inc/duplicate.hxx b/inc/duplicate.hxx index 885fb52..65544c2 100644 --- a/inc/duplicate.hxx +++ b/inc/duplicate.hxx @@ -173,36 +173,36 @@ inline void duplicateArenaOmpW(H& a, const G& x) { // Delete existing data. a.clear(); // Add vertices and reserve space for edges. - auto t0 = timeNow(); + // auto t0 = timeNow(); a.reserveOmp(S); - auto t1 = timeNow(); + // auto t1 = timeNow(); #pragma omp parallel for schedule(static, 2048) for (K u=0; u +inline void runExperiment(const G& x) { + using K = typename G::key_type; + using V = typename G::vertex_value_type; + using E = typename G::edge_value_type; + printf("Running experiment ...\n"); + // Create random number generator. + random_device dev; + default_random_engine rnd(dev()); + // Experiment of various batch fractions. + for (double frac=1e-7; frac<=1e-1; frac*=10) { + printf("Batch fraction: %.1e\n", frac); + // Generate random edge deletions and insertions. + printf("Generating random edge deletions and insertions ...\n"); + auto deletions = generateEdgeDeletions (rnd, x, size_t(frac * x.size()), 0, x.span(), false); + auto insertions = generateEdgeInsertions(rnd, x, size_t(frac * x.size()), 0, x.span(), false, E(1)); + printf("Edge deletions: %zu\n", deletions.size()); + printf("Edge insertions: %zu\n", insertions.size()); + // Create edge deletions and insertions graph. + printf("Creating edge deletions and insertions graph ...\n"); + ArenaDiGraph ydel; + ArenaDiGraph yins; + ydel.setAllocator(x.allocator()); + yins.setAllocator(x.allocator()); + ydel.reserveOmp(x.span()); + yins.reserveOmp(x.span()); + for (auto [u, v, w] : deletions) + ydel.addEdgeUnchecked(u, v, w); + for (auto [u, v, w] : insertions) + yins.addEdgeUnchecked(u, v, w); + printf("Updating edge counts ...\n"); + ydel.updateOmp(); + yins.updateOmp(); + printf("Edge deletions: "); println(ydel); + printf("Edge insertions: "); println(yins); + // Appy edge deletions in-place. + printf("Applying edge deletions in-place ...\n"); + { + ArenaDiGraph y; + y.setAllocator(x.allocator()); + float t0 = measureDuration([&]() { + duplicateArenaOmpW(y, x); + }); + float t1 = measureDuration([&]() { + subtractGraphOmpU(y, ydel); + }); + println(y); + printf("{%09.1fms; %09.1fms duplicate} %s\n", t1, t0, "subtractGraphInplace"); + } + // Apply edge insertions in-place. + printf("Applying edge insertions in-place ...\n"); + { + ArenaDiGraph y; + y.setAllocator(x.allocator()); + float t0 = measureDuration([&]() { + duplicateArenaOmpW(y, x); + }); + float t1 = measureDuration([&]() { + addGraphOmpU(y, yins); + }); + println(y); + printf("{%09.1fms; %09.1fms duplicate} %s\n", t1, t0, "addGraphInplace"); + } + // Apply edge deletions to a new graph. + { + ArenaDiGraph y; + y.setAllocator(x.allocator()); + float t = measureDuration([&]() { + subtractGraphOmpW(y, x, ydel); + }); + println(y); + printf("{%09.1fms; %09.1fms duplicate} %s\n", t, 0.0, "subtractGraphNew"); + } + // Apply edge insertions to a new graph. + { + ArenaDiGraph y; + y.setAllocator(x.allocator()); + float t = measureDuration([&]() { + addGraphOmpW(y, x, yins); + }); + println(y); + printf("{%09.1fms; %09.1fms duplicate} %s\n", t, 0.0, "addGraphNew"); + } + printf("\n"); + } +} + + +/** + * Main function. + * @param argc argument count + * @param argv argument values + * @returns exit code + */ int main(int argc, char **argv) { using K = uint32_t; using V = None; @@ -58,6 +156,7 @@ int main(int argc, char **argv) { }); LOG(""); println(xc); printf("{%09.1fms} %s\n", tr, "readMtxFormatToCsrOmpW"); + // DiGraph x; // double ts = measureDuration([&]() { // duplicateOmpW(x, xc); @@ -71,6 +170,8 @@ int main(int argc, char **argv) { }); LOG(""); println(xa); printf("{%09.1fms} %s\n", ta, "duplicateArenaOmpW"); + + runExperiment(xa); } printf("\n"); return 0; diff --git a/main.sh b/main.sh index e1e456b..68e7e9e 100755 --- a/main.sh +++ b/main.sh @@ -10,6 +10,7 @@ # module load hpcx-2.7.0/hpcx-ompi # module load openmpi/4.1.5 # module load cuda/12.3 +source /opt/rh/gcc-toolset-13/enable src="graph-openmp" out="$HOME/Logs/$src$1.log" ulimit -s unlimited