use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main};
use std::hint::black_box;
use yo_common::Rng;
use yo_graph::{Adjacency, Csr, Dir, csr};
const FOLLOWS: u32 = 1;
const NODES: u32 = 500_000;
const DEGREE: u32 = 20;
fn edges(nodes: u32, degree: u32, seed: u64) -> Vec<(u32, u32)> {
let mut rng = Rng::new(seed);
let mut out = Vec::with_capacity(nodes as usize * degree as usize);
for src in 0..nodes {
for _ in 0..degree {
out.push((src, (rng.next_u64() % u64::from(nodes)) as u32));
}
}
out
}
fn bench_read(c: &mut Criterion) {
let mut rng = Rng::new(0xc01d);
let mut group = c.benchmark_group("graph/cold one hop");
for degree in [4u32, 20, 200] {
let cold = Csr::build(
50_000,
&mut edges(50_000, degree, 0x5eed + u64::from(degree)),
);
let mut out = Vec::with_capacity(1024);
group.bench_with_input(BenchmarkId::from_parameter(degree), °ree, |b, _| {
b.iter(|| {
let node = (rng.next_u64() % 50_000) as u32;
black_box(&cold).neighbours_into(node, &mut out);
black_box(out.iter().sum::<u32>())
});
});
}
group.finish();
let mut group = c.benchmark_group("graph/cold degree");
for degree in [4u32, 200] {
let cold = Csr::build(
50_000,
&mut edges(50_000, degree, 0x5eed + u64::from(degree)),
);
group.bench_with_input(BenchmarkId::from_parameter(degree), °ree, |b, _| {
b.iter(|| black_box(&cold).degree((rng.next_u64() % 50_000) as u32));
});
}
group.finish();
let list = edges(NODES, DEGREE, 0x11ee);
let cold = Csr::build(NODES, &mut list.clone());
let mut hot = Adjacency::out_only();
for (s, d) in &list {
hot.link(u64::from(*s), u64::from(*d), FOLLOWS, 0);
}
hot.compact();
let mut group = c.benchmark_group("graph/hop");
let mut out = Vec::with_capacity(1024);
group.bench_function("hot", |b| {
b.iter(|| {
let node = rng.next_u64() % u64::from(NODES);
black_box(
black_box(&hot)
.neighbours(node, FOLLOWS, Dir::Out)
.iter()
.sum::<u64>(),
)
});
});
group.bench_function("cold", |b| {
b.iter(|| {
let node = (rng.next_u64() % u64::from(NODES)) as u32;
black_box(&cold).neighbours_into(node, &mut out);
black_box(out.iter().sum::<u32>())
});
});
group.finish();
let mut group = c.benchmark_group("graph/cold two hops");
let mut hop = Vec::with_capacity(64);
group.bench_function("serial", |b| {
b.iter(|| {
let node = (rng.next_u64() % u64::from(NODES)) as u32;
cold.neighbours_into(node, &mut hop);
let mut n = 0usize;
for next in &hop {
cold.neighbours_into(*next, &mut out);
n += out.len();
}
black_box(n)
});
});
group.bench_function("prefetched", |b| {
b.iter(|| {
let node = (rng.next_u64() % u64::from(NODES)) as u32;
cold.neighbours_into(node, &mut hop);
for next in &hop {
cold.prefetch(*next);
}
let mut n = 0usize;
for next in &hop {
cold.neighbours_into(*next, &mut out);
n += out.len();
}
black_box(n)
});
});
group.finish();
let mut group = c.benchmark_group("graph/cold order");
group.bench_function("degree", |b| {
b.iter(|| {
let to = csr::order_by_degree(NODES, black_box(&list));
black_box(to[0])
});
});
group.finish();
let mut group = c.benchmark_group("graph/cold build");
group.bench_function("ten million edges", |b| {
b.iter_batched_ref(
|| list.clone(),
|l| black_box(Csr::build(NODES, l).edges()),
criterion::BatchSize::LargeInput,
);
});
group.finish();
}
criterion_group!(benches, bench_read);
criterion_main!(benches);