Skip to content

Commit

Permalink
remove tombstoning from LS_CSR (#27)
Browse files Browse the repository at this point in the history
  • Loading branch information
meyerzinn authored Apr 4, 2024
1 parent 8ee55e5 commit 2658bd2
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 142 deletions.
193 changes: 60 additions & 133 deletions libgalois/include/galois/graphs/LS_LC_CSR_Graph.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<VertexMetadata> m_vertices;
Expand All @@ -98,10 +98,10 @@ class LS_LC_CSR_Graph : private boost::noncopyable {
*/
std::vector<uint64_t> 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;
Expand Down Expand Up @@ -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; }

Expand All @@ -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];
Expand All @@ -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;
}

/*
Expand All @@ -276,24 +260,31 @@ class LS_LC_CSR_Graph : private boost::noncopyable {
return std::binary_search(start, end, EdgeMetadata(dst));
}

template <bool sorted = false>
void addEdges(VertexTopologyID src, const std::vector<VertexTopologyID> dsts,
std::vector<EdgeData> data) {
GALOIS_ASSERT(data.size() == dsts.size());
this->addEdgesTopologyOnly(src, dsts);
this->addEdgesTopologyOnly<sorted>(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 <bool sorted = false>
void addEdgesTopologyOnly(VertexTopologyID src,
const std::vector<VertexTopologyID> 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;
Expand All @@ -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<VertexTopologyID>& edges) {
std::unordered_set<VertexTopologyID> 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);
}

Expand All @@ -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<false>(vertex_id, {});
}

// we are about to swap the buffers, so all vertices will
Expand Down Expand Up @@ -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<EdgeIterator, EdgeHandle,
boost::bidirectional_traversal_tag,
boost::random_access_traversal_tag,
EdgeHandle const> {
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;
Expand Down
15 changes: 6 additions & 9 deletions libgalois/test/graph-compile-lscsr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down Expand Up @@ -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;
}

0 comments on commit 2658bd2

Please sign in to comment.