-
Notifications
You must be signed in to change notification settings - Fork 2
Contributing
How to add a new reordering algorithm or benchmark to GraphBrew. Both follow the same pattern: edit a small set of files, register the algorithm/benchmark, rebuild.
A reordering produces a permutation new_ids[old_id] = new_vertex_id.
You implement it as a single function, register it in three places, and
build.
Edit bench/include/external/gapbs/util.h and append to
enum ReorderingAlgo:
enum ReorderingAlgo {
ORIGINAL = 0,
// ... existing entries ...
GoGraphOrder = 16,
MyNewOrder = 17, // your algorithm
};Use camelCase. Reserve only one ID per algorithm even if you support
multiple variants (variants go in the parser tokens, e.g. -o 17:mode).
Add a header under bench/include/graphbrew/reorder/:
// bench/include/graphbrew/reorder/reorder_mynew.h
#pragma once
#include "../../external/gapbs/builder.h"
#include "../../external/gapbs/pvector.h"
template <typename NodeID_, typename DestID_, bool invert>
void GenerateMyNewOrderMapping(
const CSRGraph<NodeID_, DestID_, invert>& g,
pvector<NodeID_>& new_ids) {
NodeID_ N = g.num_nodes();
// Example: sort by descending degree.
std::vector<std::pair<int64_t, NodeID_>> deg(N);
#pragma omp parallel for
for (NodeID_ v = 0; v < N; ++v) {
deg[v] = {g.out_degree(v), v};
}
std::sort(deg.begin(), deg.end(),
std::greater<std::pair<int64_t, NodeID_>>());
#pragma omp parallel for
for (NodeID_ i = 0; i < N; ++i) {
new_ids[deg[i].second] = i;
}
}Conventions:
- Return type is
void; the result is written into the suppliednew_idspvector that the caller already sized tog.num_nodes(). -
new_ids[v]holds the new label for original vertexv. The caller uses the inverse mapping to relabel the CSR. - Parallelise with OpenMP where the work is embarrassingly parallel.
- Reuse existing helpers in
reorder_graphbrew.h(BFS, RCM, scanners) rather than re-implementing.
Edit bench/include/external/gapbs/builder.h and add a case to
GenerateMapping:
case MyNewOrder:
GenerateMyNewOrderMapping(g, new_ids);
break;If your algorithm produces a permutation that needs MAP support
(i.e. you want it loadable from a precomputed file), also add the
case to the MAP-aware path elsewhere in builder.h — search for an
existing entry like case GOrder: for the pattern.
Edit bench/include/graphbrew/reorder/reorder_types.h:
- Add a case to
ReorderingAlgoStr()so logs print"MyNewOrder"instead of"17". - Add a case to
intToReorderingAlgo()so-o 17resolves. - Add an entry to the algorithm-name map at the bottom of the file (used by the Python analysis tools).
If your algorithm has runtime knobs (resolution, threshold, etc.),
extend parseGraphBrewConfig() in reorder_graphbrew.h to recognise
the tokens, e.g. -o 17:fast or -o 17:t0.5. Keep the parser logic
local to your algorithm.
make -j8 all all-sim
./bench/bin/pr -f scripts/test/graphs/tiny/tiny.el -s -o 17 -n 3Compare against -o 0 (ORIGINAL) to confirm your reordering changes
the runtime in a sensible direction.
See bench/include/graphbrew/reorder/reorder_corder.h (algorithm 10)
for a compact reference implementation. For a community-aware
algorithm, see reorder_graphbrew.h — orderHybridLeidenRabbit is
the most heavily commented end-to-end example.
A benchmark is a complete graph algorithm (PageRank, BFS, etc.)
with its own main() under bench/src/. The build system already
knows how to compile any new .cc file in bench/src/ against the
GraphBrew headers.
// bench/src/mynew.cc
#include <iostream>
#include "../include/external/gapbs/benchmark.h"
#include "../include/external/gapbs/builder.h"
#include "../include/external/gapbs/command_line.h"
#include "../include/external/gapbs/graph.h"
template <typename NodeID_, typename DestID_>
pvector<float> MyNewAlgorithm(const CSRGraph<NodeID_, DestID_>& g,
int max_iters) {
NodeID_ N = g.num_nodes();
pvector<float> result(N, 0.0f);
for (int iter = 0; iter < max_iters; ++iter) {
#pragma omp parallel for
for (NodeID_ v = 0; v < N; ++v) {
float sum = 0.0f;
for (NodeID_ u : g.in_neigh(v)) sum += result[u];
result[v] = sum / std::max<int64_t>(1, g.in_degree(v));
}
}
return result;
}
int main(int argc, char* argv[]) {
CLApp cli(argc, argv, "MyNew algorithm");
if (!cli.ParseArgs()) return -1;
Builder<NodeID, NodeID> b(cli);
CSRGraph<NodeID, NodeID> g = b.MakeGraph();
auto BenchKernel = [&cli](const CSRGraph<NodeID, NodeID>& g) {
return MyNewAlgorithm(g, cli.max_iters());
};
BenchmarkKernel(cli, g, BenchKernel);
return 0;
}The BenchmarkKernel() helper drives the trial loop, applies any
reordering selected via -o, prints timing, and writes JSON output
when -q is set.
If you want correctness checks (-v flag), implement a reference
comparison and pass it to BenchmarkKernel:
auto VerifyKernel = [](const CSRGraph<NodeID,NodeID>& g, const pvector<float>& r) {
// recompute slowly and compare; return true on match
};
BenchmarkKernel(cli, g, BenchKernel, VerifyKernel);KERNELS is the list of standard binaries built by make all and
KERNELS_SIM the list built by make all-sim. Add your binary name:
KERNELS = pr pr_spmv bfs bc cc cc_sv sssp tc tc_p mynew
KERNELS_SIM = pr pr_spmv bfs bc cc cc_sv sssp tc mynewmake all and make all-sim will pick it up automatically from
bench/src/mynew.cc (and bench/src_sim/mynew.cc if you add the
sim variant; the sim variant typically wraps the standard binary
in cache-sim instrumentation — see bench/src_sim/pr.cc for the
pattern).
Edit scripts/lib/core/utils.py and add your benchmark to the
KERNELS and (if applicable) BENCHMARKS lists so that
graphbrew_experiment.py knows to run it.
For the VLDB experiment script, also edit
scripts/experiments/vldb/config.py if you want it included in the
paper's benchmark list (it has a separate BENCHMARKS constant —
keep the paper's 7-benchmark default unless you're extending the
study).
make -j8 all all-sim
./bench/bin/mynew -f scripts/test/graphs/tiny/tiny.el -s -n 3Sanity-check that running with and without reordering produces identical results:
./bench/bin/mynew -f scripts/test/graphs/tiny/tiny.el -s -o 0 -n 1 -v
./bench/bin/mynew -f scripts/test/graphs/tiny/tiny.el -s -o 8 -n 1 -vSee bench/src/pr.cc for the canonical iterative-algorithm pattern,
or bench/src/bfs.cc for traversal. Both are well under 200 lines
including verifier.
- Match existing OpenMP patterns:
#pragma omp parallel for schedule(dynamic, 1024)for vertex-parallel loops,schedule(dynamic, 64)for community-level. - Avoid adding new external dependencies. The framework keeps Boost
optional (only used by
-o 8:boost); please don't push it back to required. - Avoid duplicating BFS / RCM / scanner code — reuse the helpers
already in
reorder_graphbrew.h. - Run
make all all-simand ensure both build clean before submitting changes.
Open an issue on https://github.com/UVA-LavaLab/GraphBrew/issues with your proposed algorithm or benchmark and the use case. We're happy to help wire it up.