From 6550c3f83347a78cafd794afb2ae45656d274dcc Mon Sep 17 00:00:00 2001 From: Meyer Zinn <6132034+meyerzinn@users.noreply.github.com> Date: Tue, 19 Mar 2024 12:06:46 -0500 Subject: [PATCH 1/3] reduce contention on parallel insertions (#5) --- libgalois/include/galois/LargeVector.h | 8 +++---- .../include/galois/graphs/LS_LC_CSR_Graph.h | 24 ++++++++++++------- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/libgalois/include/galois/LargeVector.h b/libgalois/include/galois/LargeVector.h index d9d4aba22..7b5deb465 100644 --- a/libgalois/include/galois/LargeVector.h +++ b/libgalois/include/galois/LargeVector.h @@ -29,7 +29,7 @@ template class LargeVector : public boost::noncopyable { private: size_t m_capacity; - size_t m_size; + size_t volatile m_size; T* m_data; int m_fd; @@ -68,9 +68,9 @@ class LargeVector : public boost::noncopyable { std::cout << "new_cap = " << new_cap << "\tmmap_size = " << mmap_size << std::endl; - m_data = - static_cast(mmap(nullptr, mmap_size, PROT_READ | PROT_WRITE, - MAP_SHARED | MAP_HUGETLB | MAP_HUGE_2MB, m_fd, 0)); + m_data = static_cast( + mmap(nullptr, mmap_size, PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_HUGETLB | MAP_HUGE_2MB | MAP_POPULATE, m_fd, 0)); if (m_data == MAP_FAILED) throw std::runtime_error(std::string("mmap failed: ") + std::strerror(errno)); diff --git a/libgalois/include/galois/graphs/LS_LC_CSR_Graph.h b/libgalois/include/galois/graphs/LS_LC_CSR_Graph.h index 78e78c62b..c49e504ab 100644 --- a/libgalois/include/galois/graphs/LS_LC_CSR_Graph.h +++ b/libgalois/include/galois/graphs/LS_LC_CSR_Graph.h @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -78,6 +79,8 @@ class LS_LC_CSR_Graph : private boost::noncopyable { // must be held to acquire m_edges_lock. SpinLock m_edges_lock; + std::atomic_uint64_t m_edges_tail = ATOMIC_VAR_INIT(0); + // returns a reference to the metadata for the pointed-to edge inline EdgeMetadata& getEdgeMetadata(EdgeHandle const& handle) { return getEdgeMetadata(handle.buffer, handle.index); @@ -118,18 +121,22 @@ class LS_LC_CSR_Graph : private boost::noncopyable { // Copies the edge list to the end of m_edges[1], prepending // the new edges. - vertex_meta.lock(); + vertex_meta.lock(); // prevents compaction { uint64_t const new_degree = vertex_meta.degree + dsts.size(); - uint64_t new_begin; - m_edges_lock.lock(); - { - new_begin = m_edges[1].size(); - m_edges[1].resize(new_begin + new_degree); - } - m_edges_lock.unlock(); + 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; + if (m_edges[1].size() < new_end) { + m_edges_lock.lock(); + { + if (m_edges[1].size() < new_end) + m_edges[1].resize(std::max(m_edges[1].size() * 2, new_end)); + } + m_edges_lock.unlock(); + } + // insert new edges std::transform(dsts.begin(), dsts.end(), &getEdgeMetadata(1, new_begin), [](VertexTopologyID dst) { @@ -242,6 +249,7 @@ class LS_LC_CSR_Graph : private boost::noncopyable { { m_edges[0].resize(0); swap(m_edges[0], m_edges[1]); + m_edges_tail.store(0, std::memory_order_relaxed); // fine because lock } m_edges_lock.unlock(); From 86f9a711f53f2852f8e68b9c94e71cc03203b08c Mon Sep 17 00:00:00 2001 From: Patrick Kenney Date: Tue, 19 Mar 2024 14:32:59 -0500 Subject: [PATCH 2/3] chore: Add parallel map and pcg generator as dependencies (#3) --- .gitmodules | 6 ++++++ external/parallel-hashmap | 1 + external/pcg-cpp | 1 + 3 files changed, 8 insertions(+) create mode 160000 external/parallel-hashmap create mode 160000 external/pcg-cpp diff --git a/.gitmodules b/.gitmodules index e69de29bb..d66cce84a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "external/pcg-cpp"] + path = external/pcg-cpp + url = https://github.com/imneme/pcg-cpp.git +[submodule "external/parallel-hashmap"] + path = external/parallel-hashmap + url = https://github.com/greg7mdp/parallel-hashmap.git diff --git a/external/parallel-hashmap b/external/parallel-hashmap new file mode 160000 index 000000000..67c24619e --- /dev/null +++ b/external/parallel-hashmap @@ -0,0 +1 @@ +Subproject commit 67c24619e4f5ab2097b74cc397732c17a25d6944 diff --git a/external/pcg-cpp b/external/pcg-cpp new file mode 160000 index 000000000..428802d1a --- /dev/null +++ b/external/pcg-cpp @@ -0,0 +1 @@ +Subproject commit 428802d1a5634f96bcd0705fab379ff0113bcf13 From 700cc9bc981a62c428104e6d7fd4b170017eb675 Mon Sep 17 00:00:00 2001 From: Meyer Zinn <6132034+meyerzinn@users.noreply.github.com> Date: Thu, 21 Mar 2024 11:16:38 -0500 Subject: [PATCH 3/3] reduce vertex metadata size and remove vertex locks (#8) --- .../include/galois/graphs/LS_LC_CSR_Graph.h | 126 ++++++++---------- 1 file changed, 52 insertions(+), 74 deletions(-) diff --git a/libgalois/include/galois/graphs/LS_LC_CSR_Graph.h b/libgalois/include/galois/graphs/LS_LC_CSR_Graph.h index c49e504ab..8de92e2d5 100644 --- a/libgalois/include/galois/graphs/LS_LC_CSR_Graph.h +++ b/libgalois/include/galois/graphs/LS_LC_CSR_Graph.h @@ -72,13 +72,8 @@ class LS_LC_CSR_Graph : private boost::noncopyable { using EdgeRange = boost::iterator_range; std::vector m_vertices; - LargeVector m_edges[2]; - - // To avoid deadlock between updates and compaction, at least one vertex lock - // must be held to acquire m_edges_lock. - SpinLock m_edges_lock; - + SpinLock m_edges_lock; // guards resizing of edges vectors std::atomic_uint64_t m_edges_tail = ATOMIC_VAR_INIT(0); // returns a reference to the metadata for the pointed-to edge @@ -116,46 +111,43 @@ class LS_LC_CSR_Graph : private boost::noncopyable { int addEdgesTopologyOnly(VertexTopologyID src, const std::vector dsts) { - auto& vertex_meta = m_vertices[src]; // Copies the edge list to the end of m_edges[1], prepending // the new edges. - vertex_meta.lock(); // prevents compaction - { - 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; - - if (m_edges[1].size() < new_end) { - m_edges_lock.lock(); - { - if (m_edges[1].size() < new_end) - m_edges[1].resize(std::max(m_edges[1].size() * 2, new_end)); - } - m_edges_lock.unlock(); - } + auto& vertex_meta = m_vertices[src]; - // insert new edges - std::transform(dsts.begin(), dsts.end(), &getEdgeMetadata(1, new_begin), - [](VertexTopologyID dst) { - return EdgeMetadata{.flags = 0, .dst = dst}; - }); - - // copy old, non-tombstoned edges - std::copy_if(&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(); }); - - // update vertex metadata - vertex_meta.buffer = 1; - vertex_meta.begin = new_begin; - vertex_meta.end = new_end; - 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; + + if (m_edges[1].size() < new_end) { + m_edges_lock.lock(); + { + if (m_edges[1].size() < new_end) + m_edges[1].resize(std::max(m_edges[1].size() * 2, new_end)); + } + m_edges_lock.unlock(); } - vertex_meta.unlock(); + + // insert new edges + std::transform(dsts.begin(), dsts.end(), &getEdgeMetadata(1, new_begin), + [](VertexTopologyID dst) { + return EdgeMetadata{.flags = 0, .dst = dst}; + }); + + // copy old, non-tombstoned edges + std::copy_if(&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(); }); + + // update vertex metadata + vertex_meta.buffer = 1; + vertex_meta.begin = new_begin; + vertex_meta.end = new_end; + vertex_meta.degree += dsts.size(); return 0; } @@ -165,32 +157,28 @@ class LS_LC_CSR_Graph : private boost::noncopyable { std::unordered_set edges_set(edges.begin(), edges.end()); auto& vertex_meta = m_vertices[src]; - vertex_meta.lock(); - { - for (auto i = vertex_meta.begin; i < vertex_meta.end; ++i) { - EdgeMetadata& edge_meta = - getEdgeMetadata(EdgeHandle(vertex_meta.buffer, i)); - if (!edge_meta.is_tomb() && - edges_set.find(edge_meta.dst) != edges_set.end()) { - edge_meta.tomb(); - --vertex_meta.degree; - // remove tombstoned edges from the start of the edge list - if (i == vertex_meta.begin) - ++vertex_meta.begin; - } + for (auto i = vertex_meta.begin; i < vertex_meta.end; ++i) { + EdgeMetadata& edge_meta = + getEdgeMetadata(EdgeHandle(vertex_meta.buffer, i)); + if (!edge_meta.is_tomb() && + edges_set.find(edge_meta.dst) != edges_set.end()) { + edge_meta.tomb(); + --vertex_meta.degree; + // 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(EdgeHandle(vertex_meta.buffer, i - 1)).is_tomb()) { - --vertex_meta.end; - --vertex_meta.degree; - } else { - break; - } + // remove tombstoned edges from the end of the edge list + for (auto i = vertex_meta.end; i > vertex_meta.begin; --i) { + if (getEdgeMetadata(EdgeHandle(vertex_meta.buffer, i - 1)).is_tomb()) { + --vertex_meta.end; + --vertex_meta.degree; + } else { + break; } } - vertex_meta.unlock(); return 0; } @@ -202,7 +190,7 @@ class LS_LC_CSR_Graph : private boost::noncopyable { // Performs the compaction algorithm by copying any vertices left in buffer 0 // to buffer 1, then swapping the buffers. // - // Should not be called from within a Galois parallel kernel. + // Not safe to call in parallel with insertions/deletions. void compact() { using std::swap; @@ -210,7 +198,6 @@ class LS_LC_CSR_Graph : private boost::noncopyable { galois::do_all(galois::iterate(vertices().begin(), vertices().end()), [&](VertexTopologyID vertex_id) { VertexMetadata& vertex_meta = m_vertices[vertex_id]; - vertex_meta.lock(); if (vertex_meta.buffer == 0) { uint64_t new_begin; @@ -237,14 +224,9 @@ class LS_LC_CSR_Graph : private boost::noncopyable { // we are about to swap the buffers, so all vertices will // be in buffer 0 vertex_meta.buffer = 0; - - // don't release the vertex lock until after the edge - // arrays are swapped }); // At this point, there are no more live edges in buffer 0. - // We also hold the lock for all vertices, so nobody else can hold - // m_edges_lock. m_edges_lock.lock(); { m_edges[0].resize(0); @@ -252,14 +234,10 @@ class LS_LC_CSR_Graph : private boost::noncopyable { m_edges_tail.store(0, std::memory_order_relaxed); // fine because lock } m_edges_lock.unlock(); - - galois::do_all( - galois::iterate(vertices().begin(), vertices().end()), - [&](VertexTopologyID vertex_id) { m_vertices[vertex_id].unlock(); }); } private: - struct VertexMetadata : public SpinLock { + struct VertexMetadata { uint8_t buffer : 1; uint64_t begin : 48; // inclusive uint64_t end : 48; // exclusive