Skip to content

Commit

Permalink
add data to the LS_CSR
Browse files Browse the repository at this point in the history
  • Loading branch information
meyerzinn committed Mar 21, 2024
1 parent 0db6557 commit 5a526fe
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 60 deletions.
6 changes: 3 additions & 3 deletions libgalois/include/galois/graphs/LS_LC_CSR_64_Graph.h
Original file line number Diff line number Diff line change
Expand Up @@ -293,13 +293,13 @@ class LS_LC_CSR_64_Graph :

template <bool _A1 = HasNoLockable, bool _A2 = HasOutOfLineLockable>
void acquireNode(GraphNode N, MethodFlag mflag,
typename std::enable_if<!_A1&& !_A2>::type* = 0) {
typename std::enable_if<!_A1 && !_A2>::type* = 0) {
galois::runtime::acquire(&nodeData[N], mflag);
}

template <bool _A1 = HasOutOfLineLockable, bool _A2 = HasNoLockable>
void acquireNode(GraphNode N, MethodFlag mflag,
typename std::enable_if<_A1&& !_A2>::type* = 0) {
typename std::enable_if<_A1 && !_A2>::type* = 0) {
this->outOfLineAcquire(getId(N), mflag);
}

Expand Down Expand Up @@ -338,7 +338,7 @@ class LS_LC_CSR_64_Graph :
template <bool _A1 = EdgeData::has_value,
bool _A2 = LargeArray<FileEdgeTy>::has_value>
void constructEdgeValue(FileGraph&, typename FileGraph::edge_iterator nn,
typename std::enable_if<_A1&& !_A2>::type* = 0) {
typename std::enable_if<_A1 && !_A2>::type* = 0) {
edgeData.set(*nn, {});
}

Expand Down
165 changes: 111 additions & 54 deletions libgalois/include/galois/graphs/LS_LC_CSR_Graph.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,17 @@
#include <cstddef>
#include <atomic>
#include <new>
#include <type_traits>
#include <unordered_map>

#include <boost/range/iterator_range_core.hpp>
#include <boost/range/counting_range.hpp>
#include <boost/iterator/iterator_facade.hpp>
#include <boost/functional/hash.hpp>

#include "galois/config.h"
#include "galois/LargeVector.h"
#include "galois/LargeArray.h"

#ifdef __cpp_lib_hardware_interference_size
using std::hardware_destructive_interference_size;
Expand All @@ -44,7 +48,7 @@ namespace galois::graphs {
/**
* Local computation graph.
*/
template <bool concurrent = true>
template <typename VertexData, typename EdgeData, bool concurrent = true>
class LS_LC_CSR_Graph : private boost::noncopyable {
public:
using VertexTopologyID = uint64_t;
Expand All @@ -55,10 +59,10 @@ class LS_LC_CSR_Graph : private boost::noncopyable {
private:
uint8_t buffer : 1;
uint64_t index : 48;
VertexTopologyID src : 48;

EdgeHandle(uint8_t buffer, uint64_t index) : buffer(buffer), index(index) {}

EdgeHandle(uint64_t const& v) : buffer(v >> 63), index(v) {}
EdgeHandle(uint8_t buffer, uint64_t index, uint64_t src)
: buffer(buffer), index(index), src(src) {}

friend class LS_LC_CSR_Graph;

Expand All @@ -69,55 +73,80 @@ class LS_LC_CSR_Graph : private boost::noncopyable {
} __attribute__((packed));

private:
using SpinLock = galois::substrate::PaddedLock<concurrent>;

// forward-declarations
using SpinLock = galois::substrate::PaddedLock<concurrent>;
using HasVertexData = std::negation<std::is_same<VertexData, void>>;
using VertexDataStore =
std::conditional_t<HasVertexData::value,
typename galois::LargeArray<VertexData>,
typename std::tuple<>>;

using HasEdgeData = std::negation<std::is_same<EdgeData, void>>;
using EdgeDataStore = std::conditional_t<
HasEdgeData::value,
typename std::unordered_map<
std::pair<VertexTopologyID, VertexTopologyID>, EdgeData,
boost::hash<std::pair<VertexTopologyID, VertexTopologyID>>>,
typename std::tuple<>>;

// forward-declarations of internal structs
struct VertexMetadata;
struct EdgeMetadata;

class EdgeIterator;
using EdgeRange = boost::iterator_range<EdgeIterator>;

VertexDataStore m_vertex_data;
std::vector<VertexMetadata> m_vertices;

// m_edges[0] is the CSR with gaps, m_edges[1] is the update log.
LargeVector<EdgeMetadata> m_edges[2];
SpinLock m_edges_lock; // guards resizing of edges vectors
EdgeDataStore m_edge_data;

alignas(hardware_destructive_interference_size) std::atomic_uint64_t
m_edges_tail = ATOMIC_VAR_INIT(0);

// m_holes is the number of holes in the log (m_edges[1])
alignas(hardware_destructive_interference_size) std::atomic_uint64_t m_holes =
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);
}

inline EdgeMetadata& getEdgeMetadata(uint8_t buffer, uint64_t index) const {
return m_edges[buffer][index];
}

inline EdgeMetadata& getEdgeMetadata(EdgeHandle handle) const {
return getEdgeMetadata(handle.buffer, handle.index);
}

public:
LS_LC_CSR_Graph(uint64_t num_vertices)
: m_vertices(num_vertices, VertexMetadata()) {}
: m_vertices(num_vertices, VertexMetadata()) {
if constexpr (HasVertexData::value) {
m_vertex_data.allocateBlocked(num_vertices);
}
}

inline uint64_t size() const noexcept { return m_vertices.size(); }

// returns an estimated memory footprint
inline uint64_t getFootprint() {
uint64_t estimate;
m_edges_lock.lock();
{
estimate =
(m_edges[0].size() + m_edges_tail.load(std::memory_order_relaxed)) *
sizeof(EdgeMetadata);
}
m_edges_lock.unlock();
return estimate;
/** Data Manipulations **/

inline void setData(VertexTopologyID vertex,
std::enable_if<HasVertexData::value, VertexData> data) {
m_vertex_data[vertex] = data;
}

// return data associated with a vertex
inline std::enable_if<HasVertexData::value, VertexData>&
getData(VertexTopologyID vertex) {
return m_vertex_data[vertex];
}

inline uint64_t numHoles() const noexcept {
return m_holes.load(std::memory_order_relaxed);
void setEdgeData(EdgeHandle handle,
std::enable_if<HasEdgeData::value, EdgeData> data) {
m_edge_data[make_pair(handle.src, getEdgeMetadata(handle).dst)] = data;
}


inline VertexTopologyID begin() const noexcept {
return static_cast<VertexTopologyID>(0);
Expand All @@ -129,16 +158,25 @@ class LS_LC_CSR_Graph : private boost::noncopyable {

VertexRange vertices() { return VertexRange(begin(), end()); }

VertexTopologyID getEdgeDst(EdgeHandle eh) { return getEdgeMetadata(eh).dst; }

EdgeRange edges(VertexTopologyID node) {
auto& vertex_meta = m_vertices[node];
auto const* ii = &getEdgeMetadata(vertex_meta.buffer, vertex_meta.begin);
auto const* ee = &getEdgeMetadata(vertex_meta.buffer, vertex_meta.end);

return EdgeRange(EdgeIterator(ii, ee), EdgeIterator(ee, ee));
return EdgeRange(EdgeIterator(this, vertex_meta.buffer, vertex_meta.begin,
vertex_meta.end, node),
EdgeIterator(this, vertex_meta.buffer, vertex_meta.end,
vertex_meta.end, node));
}

void
addEdges(VertexTopologyID src, const std::vector<VertexTopologyID> dsts,
std::vector<std::enable_if<HasEdgeData::value, EdgeData>> data) {
this->addEdgesTopologyOnly(src, dsts);
}

int addEdgesTopologyOnly(VertexTopologyID src,
const std::vector<VertexTopologyID> dsts) {
void addEdgesTopologyOnly(VertexTopologyID src,
const std::vector<VertexTopologyID> dsts) {

// Copies the edge list to the end of m_edges[1], prepending
// the new edges.
Expand Down Expand Up @@ -178,19 +216,16 @@ class LS_LC_CSR_Graph : private boost::noncopyable {

m_holes.fetch_add(vertex_meta.degree, std::memory_order_relaxed);
vertex_meta.degree += dsts.size();

return 0;
}

int deleteEdges(VertexTopologyID src,
const std::vector<VertexTopologyID>& edges) {
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(EdgeHandle(vertex_meta.buffer, 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.tomb();
Expand All @@ -204,7 +239,7 @@ class LS_LC_CSR_Graph : private boost::noncopyable {

// 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()) {
if (getEdgeMetadata(vertex_meta.buffer, i - 1).is_tomb()) {
--vertex_meta.end;
--vertex_meta.degree;
} else {
Expand All @@ -213,12 +248,6 @@ class LS_LC_CSR_Graph : private boost::noncopyable {
}

m_holes.fetch_add(holes_added, std::memory_order_relaxed);

return 0;
}

VertexTopologyID getEdgeDst(EdgeHandle edge) {
return getEdgeMetadata(edge).dst;
}

// Performs the compaction algorithm by copying any vertices left in buffer 0
Expand Down Expand Up @@ -272,6 +301,28 @@ class LS_LC_CSR_Graph : private boost::noncopyable {
m_edges_lock.unlock();
}

/*
Compaction policy utilities.
*/

// Returns an estimated memory usage in bytes for the entire data structure.
inline size_t getMemoryUsageBytes() {
size_t estimate = m_vertices.size() * sizeof(VertexMetadata);
m_edges_lock.lock();
{
estimate +=
(m_edges[0].size() + m_edges_tail.load(std::memory_order_relaxed)) *
sizeof(EdgeMetadata);
}
m_edges_lock.unlock();
return estimate;
}

// Returns the number of bytes used for holes in the log.
inline size_t getLogHolesMemoryUsageBytes() {
return m_holes.load(std::memory_order_relaxed) * sizeof(EdgeMetadata);
}

private:
struct VertexMetadata {
uint8_t buffer : 1;
Expand Down Expand Up @@ -303,25 +354,31 @@ class LS_LC_CSR_Graph : private boost::noncopyable {
static_assert(sizeof(EdgeMetadata) <= sizeof(uint64_t));

class EdgeIterator
: public boost::iterator_facade<EdgeIterator, EdgeMetadata const,
: public boost::iterator_facade<EdgeIterator, EdgeHandle const,
boost::forward_traversal_tag,
VertexTopologyID> {
EdgeHandle const> {
private:
EdgeMetadata const* m_ptr;
EdgeMetadata const* const m_end;
LS_LC_CSR_Graph* graph;
uint8_t buffer;
uint64_t index;
uint64_t end;
VertexTopologyID src;

explicit EdgeIterator(EdgeMetadata const* ptr, EdgeMetadata const* end)
: m_ptr(ptr), m_end(end) {}
explicit EdgeIterator(LS_LC_CSR_Graph* graph, uint8_t buffer,
uint64_t index, uint64_t end, VertexTopologyID src)
: graph(graph), buffer(buffer), index(index), end(end), src(src) {}

void increment() {
while (++m_ptr < m_end && m_ptr->is_tomb())
while (++index < end && graph->getEdgeMetadata(buffer, index).is_tomb())
;
};
}

// note: equality fails across generations
bool equal(EdgeIterator const& other) const { return m_ptr == other.m_ptr; }
// updates to the graph will invalidate iterators
bool equal(EdgeIterator const& other) const {
return graph == other.graph && index == other.index;
}

VertexTopologyID dereference() const { return m_ptr->dst; }
EdgeHandle dereference() const { return EdgeHandle(buffer, index, src); }

friend class LS_LC_CSR_Graph;
friend class boost::iterator_core_access;
Expand Down
24 changes: 21 additions & 3 deletions libgalois/test/graph-compile-lscsr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ void check() {
auto print_graph = [&g](std::string_view msg) {
std::cout << "- " << msg << " -" << std::endl;
for (auto src : g.vertices()) {
for (auto dst : g.edges(src)) {
std::cout << src << "->" << dst << std::endl;
for (auto edge : g.edges(src)) {
std::cout << src << "->" << g.getEdgeDst(edge) << std::endl;
}
}
};
Expand All @@ -59,7 +59,25 @@ void check() {

int main() {
galois::SharedMemSys Galois_runtime;
check<galois::graphs::LS_LC_CSR_Graph<>>();
check<galois::graphs::LS_LC_CSR_Graph<void, void>>();
check<galois::graphs::LS_LC_CSR_Graph<float, float>>();

// check that we can access data on nodes/edges
LS_LC_CSR_Graph<uint32_t, uint32_t> g(4);

g.setData(0, 0);
GALOIS_ASSERT(g.getData(0) == 0);
g.setData(1, 1);
GALOIS_ASSERT(g.getData(1) == 1);
g.setData(2, 2);
GALOIS_ASSERT(g.getData(2) == 2);
g.setData(3, 3);
GALOIS_ASSERT(g.getData(3) == 3);

g.addEdges(0, {1, 2, 3}, {1, 2, 3});
for (auto const& handle : g.edges(0)) {
GALOIS_ASSERT(g.getEdgeDst(handle) == g.getEdgeData(handle));
}

return 0;
}

0 comments on commit 5a526fe

Please sign in to comment.