use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main};
use std::hint::black_box;
use yo_common::Rng;
use yo_graph::{Adjacency, Dir};
const FOLLOWS: u32 = 1;
const NODES: u64 = 500_000;
const DEGREE: u64 = 20;
fn build(nodes: u64, degree: u64, seed: u64) -> Adjacency {
let mut g = Adjacency::out_only();
let mut rng = Rng::new(seed);
for src in 0..nodes {
for _ in 0..degree {
g.link(src, rng.next_u64() % nodes, FOLLOWS, 0);
}
}
g
}
fn two_hops(g: &Adjacency, from: u64, out: &mut Vec<u64>) {
out.clear();
for hop in g.neighbours(from, FOLLOWS, Dir::Out) {
out.extend_from_slice(g.neighbours(*hop, FOLLOWS, Dir::Out));
}
}
fn two_hops_prefetched(g: &Adjacency, from: u64, out: &mut Vec<u64>) {
out.clear();
let first = g.neighbours(from, FOLLOWS, Dir::Out);
for hop in first {
g.prefetch(*hop, FOLLOWS, Dir::Out);
}
for hop in first {
out.extend_from_slice(g.neighbours(*hop, FOLLOWS, Dir::Out));
}
}
fn bench_hops(c: &mut Criterion) {
let g = build(NODES, DEGREE, 0x11ee);
let mut rng = Rng::new(9);
let mut group = c.benchmark_group("graph/one hop");
for degree in [4u64, 20, 200] {
let h = build(50_000, degree, 0x5eed + degree);
group.bench_with_input(BenchmarkId::from_parameter(degree), °ree, |b, _| {
b.iter(|| {
let node = rng.next_u64() % 50_000;
black_box(
black_box(&h)
.neighbours(node, FOLLOWS, Dir::Out)
.iter()
.sum::<u64>(),
)
});
});
}
group.finish();
let mut group = c.benchmark_group("graph/two hops");
let mut out = Vec::with_capacity(1024);
group.bench_function("serial", |b| {
b.iter(|| {
two_hops(black_box(&g), rng.next_u64() % NODES, &mut out);
black_box(out.len())
});
});
group.bench_function("prefetched", |b| {
b.iter(|| {
two_hops_prefetched(black_box(&g), rng.next_u64() % NODES, &mut out);
black_box(out.len())
});
});
group.finish();
}
fn bench_writes(c: &mut Criterion) {
let mut rng = Rng::new(0xfeed);
let mut group = c.benchmark_group("graph/link");
for degree in [4u64, 20] {
group.bench_with_input(BenchmarkId::from_parameter(degree), °ree, |b, degree| {
b.iter_batched_ref(
Adjacency::out_only,
|g| {
for src in 0..2000u64 {
for _ in 0..*degree {
g.link(src, rng.next_u64() % 2000, FOLLOWS, 0);
}
}
},
criterion::BatchSize::LargeInput,
);
});
}
group.finish();
let mut group = c.benchmark_group("graph/unlink");
for degree in [4u64, 20, 200] {
group.bench_with_input(BenchmarkId::from_parameter(degree), °ree, |b, degree| {
b.iter_batched_ref(
|| {
let mut g = Adjacency::new();
for src in 0..1000u64 {
for i in 0..*degree {
g.link(src, i, FOLLOWS, 0);
}
}
g
},
|g| {
for src in 0..1000u64 {
for i in 0..*degree {
black_box(g.unlink(src, i, FOLLOWS));
}
}
},
criterion::BatchSize::LargeInput,
);
});
}
group.finish();
}
criterion_group!(benches, bench_hops, bench_writes);
criterion_main!(benches);