Skip to content

Commit

Permalink
🐛 setup batch update experiment
Browse files Browse the repository at this point in the history
Also:
- rename to setDegreeUnsafe()
- rename to addEdgeUnsafe()
- fix get/set/clearBit()
- use GCC13
  • Loading branch information
wolfram77 committed Jan 26, 2025
1 parent 3eaf351 commit 36fdb37
Show file tree
Hide file tree
Showing 5 changed files with 157 additions and 28 deletions.
53 changes: 40 additions & 13 deletions inc/Graph.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

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


Expand All @@ -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
Expand All @@ -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<K, E> *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<K, E> *ptr = edges[u];
ptr[i] = {v, w};
}


/**
* Remove outgoing edges from a vertex in the graph.
* @param u source vertex id
Expand Down Expand Up @@ -797,7 +824,7 @@ class ArenaDiGraph {
* @param ie end iterator of edges to add
* @note [ib, ie) must be sorted and unique.
*/
template <class I, class FL>
template <class I>
inline void addEdges(K u, I ib, I ie) {
if (!hasVertex(u) || ib==ie) return;
auto *eb = edges[u], *ee = edges[u] + degrees[u];
Expand Down Expand Up @@ -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) {
Expand All @@ -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);
}
Expand Down Expand Up @@ -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)
Expand All @@ -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);
}
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
}
Expand Down
6 changes: 3 additions & 3 deletions inc/_vector.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ using vector2d = vector<vector<T>>;
template <class T>
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;
}


Expand All @@ -52,7 +52,7 @@ inline bool getBit(const T *x, size_t i) {
template <class T>
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));
}


Expand All @@ -64,7 +64,7 @@ inline void setBit(T *x, size_t i) {
template <class T>
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

Expand Down
24 changes: 12 additions & 12 deletions inc/duplicate.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -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<S; ++u) {
if (!x.hasVertex(u)) continue;
a.addVertex(u, x.vertexValue(u));
}
auto t2 = timeNow();
// auto t2 = timeNow();
#pragma omp parallel for schedule(dynamic, 2048)
for (K u=0; u<S; ++u) {
if (!x.hasVertex(u)) continue;
a.allocateEdges(u, x.degree(u));
}
auto t3 = timeNow();
// auto t3 = timeNow();
// Populate the edges.
#pragma omp parallel for schedule(dynamic, 1024)
for (K u=0; u<S; ++u) {
if (!x.hasVertex(u)) continue;
x.forEachEdge(u, [&](auto v, auto w) {
a.addEdgeUnchecked(u, v, w);
a.addEdgeUnsafe(u, v, w);
});
}
auto t4 = timeNow();
// auto t4 = timeNow();
a.updateOmp(true, false);
auto t5 = timeNow();
printf("duplicateArenaOmpW: Reserve space = %.3f ms\n", duration(t0, t1));
printf("duplicateArenaOmpW: Add vertices = %.3f ms\n", duration(t1, t2));
printf("duplicateArenaOmpW: Reserve edges = %.3f ms\n", duration(t2, t3));
printf("duplicateArenaOmpW: Add edges = %.3f ms\n", duration(t3, t4));
printf("duplicateArenaOmpW: Update = %.3f ms\n", duration(t4, t5));
// auto t5 = timeNow();
// printf("duplicateArenaOmpW: Reserve space = %.3f ms\n", duration(t0, t1));
// printf("duplicateArenaOmpW: Add vertices = %.3f ms\n", duration(t1, t2));
// printf("duplicateArenaOmpW: Reserve edges = %.3f ms\n", duration(t2, t3));
// printf("duplicateArenaOmpW: Add edges = %.3f ms\n", duration(t3, t4));
// printf("duplicateArenaOmpW: Update = %.3f ms\n", duration(t4, t5));
}
#pragma endregion
101 changes: 101 additions & 0 deletions main.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,104 @@ using namespace std;

#pragma region METHODS
#pragma region PERFORM EXPERIMENT
/**
* Perform the experiment.
* @param x input graph
*/
template <class G>
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<K, V, E> ydel;
ArenaDiGraph<K, V, E> 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<K, V, E> 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<K, V, E> 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<K, V, E> 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<K, V, E> 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;
Expand All @@ -58,6 +156,7 @@ int main(int argc, char **argv) {
});
LOG(""); println(xc);
printf("{%09.1fms} %s\n", tr, "readMtxFormatToCsrOmpW");

// DiGraph<K, V, E> x;
// double ts = measureDuration([&]() {
// duplicateOmpW(x, xc);
Expand All @@ -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;
Expand Down
1 change: 1 addition & 0 deletions main.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 36fdb37

Please sign in to comment.