From 2658bd2a1565812e9f4fba1e33b44b85a5bc55f4 Mon Sep 17 00:00:00 2001 From: Meyer Zinn <6132034+meyerzinn@users.noreply.github.com> Date: Thu, 4 Apr 2024 18:27:54 -0500 Subject: [PATCH] remove tombstoning from LS_CSR (#27) --- .../include/galois/graphs/LS_LC_CSR_Graph.h | 193 ++++++------------ libgalois/test/graph-compile-lscsr.cpp | 15 +- 2 files changed, 66 insertions(+), 142 deletions(-) diff --git a/libgalois/include/galois/graphs/LS_LC_CSR_Graph.h b/libgalois/include/galois/graphs/LS_LC_CSR_Graph.h index 451477b2f..93f13df0c 100644 --- a/libgalois/include/galois/graphs/LS_LC_CSR_Graph.h +++ b/libgalois/include/galois/graphs/LS_LC_CSR_Graph.h @@ -78,7 +78,7 @@ class LS_LC_CSR_Graph : private boost::noncopyable { // forward-declarations of internal structs struct VertexMetadata; - struct EdgeMetadata; + using EdgeMetadata = VertexTopologyID; VertexDataStore m_vertex_data; std::vector m_vertices; @@ -98,10 +98,10 @@ class LS_LC_CSR_Graph : private boost::noncopyable { */ std::vector m_pfx_sum_cache; static uint64_t transmute(const VertexMetadata& vertex_meta) { - return vertex_meta.degree; + return vertex_meta.degree(); } static uint64_t scan_op(const VertexMetadata& p, const uint64_t& l) { - return p.degree + l; + return p.degree() + l; } static uint64_t combiner(const uint64_t& f, const uint64_t& s) { return f + s; @@ -192,7 +192,9 @@ class LS_LC_CSR_Graph : private boost::noncopyable { return start; } - inline size_t getDegree(VertexTopologyID id) { return m_vertices[id].degree; } + inline size_t getDegree(VertexTopologyID id) { + return m_vertices[id].degree(); + } inline VertexTopologyID getEdgeDst(EdgeHandle eh) { return eh.second; } @@ -214,38 +216,24 @@ class LS_LC_CSR_Graph : private boost::noncopyable { return num_edges.reduce(); } - EdgeIterator edge_begin(VertexTopologyID vertex) { + inline EdgeIterator edge_begin(VertexTopologyID vertex) { auto const& vertex_meta = m_vertices[vertex]; - EdgeMetadata const* const start = - &getEdgeMetadata(vertex_meta.buffer, vertex_meta.begin); - EdgeMetadata const* const end = - &getEdgeMetadata(vertex_meta.buffer, vertex_meta.end); - return EdgeIterator(vertex, start, end); + return EdgeIterator( + vertex, &getEdgeMetadata(vertex_meta.buffer, vertex_meta.begin)); } - EdgeIterator edge_end(VertexTopologyID vertex) { + inline EdgeIterator edge_end(VertexTopologyID vertex) { auto const& vertex_meta = m_vertices[vertex]; - EdgeMetadata const* const end = - &getEdgeMetadata(vertex_meta.buffer, vertex_meta.end); - return EdgeIterator(vertex, end, end); + return EdgeIterator(vertex, + &getEdgeMetadata(vertex_meta.buffer, vertex_meta.end)); } - EdgeRange edges(VertexTopologyID node) { - auto const& vertex_meta = m_vertices[node]; - - EdgeMetadata const* const start = - &getEdgeMetadata(vertex_meta.buffer, vertex_meta.begin); - - EdgeMetadata const* const end = - &getEdgeMetadata(vertex_meta.buffer, vertex_meta.end); - - return EdgeRange(EdgeIterator(node, start, end), - EdgeIterator(node, end, end)); + inline EdgeRange edges(VertexTopologyID node) { + return EdgeRange(edge_begin(node), edge_end(node)); } /* - * Sort the outgoing edges for the given vertex, pruning tombstoned edges in - * the process. + * Sort the outgoing edges for the given vertex. */ void sortEdges(VertexTopologyID node) { auto& vertex_meta = m_vertices[node]; @@ -256,10 +244,6 @@ class LS_LC_CSR_Graph : private boost::noncopyable { EdgeMetadata* end = &getEdgeMetadata(vertex_meta.buffer, vertex_meta.end); std::sort(start, end); - - // Tombstoned edges will be moved to the end, so we can drop them by moving - // the end pointer: - vertex_meta.end = vertex_meta.begin + vertex_meta.degree; } /* @@ -276,24 +260,31 @@ class LS_LC_CSR_Graph : private boost::noncopyable { return std::binary_search(start, end, EdgeMetadata(dst)); } + template void addEdges(VertexTopologyID src, const std::vector dsts, std::vector data) { GALOIS_ASSERT(data.size() == dsts.size()); - this->addEdgesTopologyOnly(src, dsts); + this->addEdgesTopologyOnly(src, dsts); for (size_t i = 0; i < dsts.size(); ++i) { m_edge_data[std::make_pair(src, dsts[i])] = data[i]; } } + /* + * Adds outgoing edges from the given src to all dsts. If `sorted`, assume + * both `dsts` and the existing edge array is sorted ascending, and maintain + * sorted order. + */ + template void addEdgesTopologyOnly(VertexTopologyID src, const std::vector dsts) { - - // Copies the edge list to the end of m_edges[1], prepending - // the new edges. + // Copies the edge list to the end of m_edges[1] together with the new + // edges. auto& vertex_meta = m_vertices[src]; + m_holes.fetch_add(vertex_meta.degree(), std::memory_order_relaxed); - uint64_t const new_degree = vertex_meta.degree + dsts.size(); + uint64_t const new_degree = vertex_meta.degree() + dsts.size(); uint64_t const new_begin = m_edges_tail.fetch_add(new_degree, std::memory_order_relaxed); uint64_t const new_end = new_begin + new_degree; @@ -307,57 +298,27 @@ class LS_LC_CSR_Graph : private boost::noncopyable { m_edges_lock.unlock(); } - // insert new edges - std::transform(dsts.begin(), dsts.end(), &getEdgeMetadata(1, new_begin), - [](VertexTopologyID dst) { return EdgeMetadata(dst); }); - - // copy old, non-tombstoned edges - std::copy_if(&getEdgeMetadata(vertex_meta.buffer, vertex_meta.begin), + EdgeMetadata* log_dst = &getEdgeMetadata(1, new_begin); + if constexpr (sorted) { + std::merge(dsts.begin(), dsts.end(), + &getEdgeMetadata(vertex_meta.buffer, vertex_meta.begin), &getEdgeMetadata(vertex_meta.buffer, vertex_meta.end), - &getEdgeMetadata(1, new_begin + dsts.size()), - [](EdgeMetadata& edge) { return !edge.is_tomb(); }); + log_dst); + } else { + // copy old edges + log_dst = std::copy( + &getEdgeMetadata(vertex_meta.buffer, vertex_meta.begin), + &getEdgeMetadata(vertex_meta.buffer, vertex_meta.end), log_dst); + + // insert new edges + std::copy(dsts.begin(), dsts.end(), log_dst); + } // update vertex metadata vertex_meta.buffer = 1; vertex_meta.begin = new_begin; vertex_meta.end = new_end; - m_holes.fetch_add(vertex_meta.degree, std::memory_order_relaxed); - vertex_meta.degree += dsts.size(); - - m_prefix_valid.store(false, std::memory_order_release); - } - - void deleteEdges(VertexTopologyID src, - const std::vector& edges) { - std::unordered_set edges_set(edges.begin(), edges.end()); - - auto& vertex_meta = m_vertices[src]; - uint64_t holes_added = 0; - for (auto i = vertex_meta.begin; i < vertex_meta.end; ++i) { - EdgeMetadata& edge_meta = getEdgeMetadata(vertex_meta.buffer, i); - if (!edge_meta.is_tomb() && - edges_set.find(edge_meta.dst) != edges_set.end()) { - edge_meta.set_tomb(); - --vertex_meta.degree; - ++holes_added; - // remove tombstoned edges from the start of the edge list - if (i == vertex_meta.begin) - ++vertex_meta.begin; - } - } - - // remove tombstoned edges from the end of the edge list - for (auto i = vertex_meta.end; i > vertex_meta.begin; --i) { - if (getEdgeMetadata(vertex_meta.buffer, i - 1).is_tomb()) { - --vertex_meta.end; - --vertex_meta.degree; - } else { - break; - } - } - - m_holes.fetch_add(holes_added, std::memory_order_relaxed); m_prefix_valid.store(false, std::memory_order_release); } @@ -374,7 +335,7 @@ class LS_LC_CSR_Graph : private boost::noncopyable { VertexMetadata& vertex_meta = m_vertices[vertex_id]; if (vertex_meta.buffer == 0) { - this->addEdgesTopologyOnly(vertex_id, {}); + this->addEdgesTopologyOnly(vertex_id, {}); } // we are about to swap the buffers, so all vertices will @@ -449,78 +410,44 @@ class LS_LC_CSR_Graph : private boost::noncopyable { uint8_t buffer : 1; uint64_t begin : 48; // inclusive uint64_t end : 48; // exclusive - uint64_t degree; - VertexMetadata() : buffer(0), begin(0), end(0), degree(0) {} + VertexMetadata() : buffer(0), begin(0), end(0) {} VertexMetadata(VertexMetadata const& other) - : buffer(other.buffer), begin(other.begin), end(other.end), - degree(other.degree) {} + : buffer(other.buffer), begin(other.begin), end(other.end) {} VertexMetadata(VertexMetadata&& other) : buffer(std::move(other.buffer)), begin(std::move(other.begin)), - end(std::move(other.end)), degree(std::move(other.degree)) {} - }; - - struct EdgeMetadata { - enum Flags : uint16_t { TOMB = 0x1 }; - - uint16_t flags : 16; - VertexTopologyID dst : 48; - - bool is_tomb() const noexcept { return (flags & TOMB) > 0; } - void set_tomb() { flags |= TOMB; } - - EdgeMetadata() {} - explicit EdgeMetadata(VertexTopologyID dst) : flags(0), dst(dst) {} + end(std::move(other.end)) {} - // Sort edges, with tombstoned coming after non-tombstoned. - friend bool operator<(EdgeMetadata const& lhs, EdgeMetadata const& rhs) { - if (lhs.is_tomb() != rhs.is_tomb()) - // tombstoned edges come last - return lhs.is_tomb() < rhs.is_tomb(); - else - // otherwise, sort by dst - return lhs.dst < rhs.dst; - } - - // Check dst equality only. - friend bool operator==(EdgeMetadata const& lhs, EdgeMetadata const& rhs) { - return (lhs.dst == rhs.dst); - } - - } __attribute__((packed)); - - static_assert(sizeof(EdgeMetadata) <= sizeof(uint64_t)); + inline uint64_t degree() const { return end - begin; } + }; public: class EdgeIterator : public boost::iterator_facade { private: VertexTopologyID const src; - EdgeMetadata const* curr; - EdgeMetadata const* const end; + EdgeMetadata const* ptr; - EdgeIterator(VertexTopologyID src, EdgeMetadata const* start, - EdgeMetadata const* end) - : src(src), curr(start), end(end) {} + EdgeIterator(VertexTopologyID src, EdgeMetadata const* ptr) + : src(src), ptr(ptr) {} - void increment() { - while (++curr < end && curr->is_tomb()) - ; - } + void advance(std::ptrdiff_t n) { ptr += n; } - void decrement() { - while ((--curr)->is_tomb()) - ; + std::ptrdiff_t distance_to(EdgeIterator const& y) const { + return y.ptr - ptr; } - // updates to the graph will invalidate iterators - bool equal(EdgeIterator const& other) const { return curr == other.curr; } + void increment() { ++ptr; } + + void decrement() { --ptr; } + + bool equal(EdgeIterator const& other) const { return ptr == other.ptr; } - EdgeHandle dereference() const { return EdgeHandle(src, curr->dst); } + EdgeHandle dereference() const { return EdgeHandle(src, *ptr); } friend class LS_LC_CSR_Graph; friend class boost::iterator_core_access; diff --git a/libgalois/test/graph-compile-lscsr.cpp b/libgalois/test/graph-compile-lscsr.cpp index 7c8aac7da..2ed2e597e 100644 --- a/libgalois/test/graph-compile-lscsr.cpp +++ b/libgalois/test/graph-compile-lscsr.cpp @@ -46,10 +46,6 @@ void check() { print_graph("added 2->1"); - g.deleteEdges(1, {3}); - - print_graph("deleted 1->3"); - g.compact(); print_graph("compacted"); @@ -98,26 +94,27 @@ int main() { GALOIS_ASSERT(g.getEdgeDst(*(++(++(++g.edge_begin(eight))))) == 0); GALOIS_ASSERT(g.getEdgeDst(*(--g.edge_end(eight))) == 0); - g.deleteEdges(eight, {1}); g.sortEdges(eight); GALOIS_ASSERT(g.getEdgeDst(*g.edge_begin(eight)) == 0); - GALOIS_ASSERT(g.getEdgeDst(*(++g.edge_begin(eight))) == 2); - GALOIS_ASSERT(g.getEdgeDst(*(++(++g.edge_begin(eight)))) == 3); + GALOIS_ASSERT(g.getEdgeDst(*(++g.edge_begin(eight))) == 1); + GALOIS_ASSERT(g.getEdgeDst(*(++(++g.edge_begin(eight)))) == 2); + GALOIS_ASSERT(g.getEdgeDst(*(g.edge_begin(eight) + 2)) == 2); GALOIS_ASSERT(g.getEdgeDst(*(--g.edge_end(eight))) == 3); // check searching GALOIS_ASSERT(g.findEdgeSorted(eight, 0)); - GALOIS_ASSERT(!g.findEdgeSorted(eight, 1)); + GALOIS_ASSERT(g.findEdgeSorted(eight, 1)); GALOIS_ASSERT(g.findEdgeSorted(eight, 2)); GALOIS_ASSERT(g.findEdgeSorted(eight, 3)); + GALOIS_ASSERT(!g.findEdgeSorted(eight, 4)); // check prefix sum GALOIS_ASSERT(g[0] == 3); GALOIS_ASSERT(g[1] == 3); GALOIS_ASSERT(g[2] == 3); // ... - GALOIS_ASSERT(g[8] == 6); + GALOIS_ASSERT(g[8] == 7); return 0; }