Runs cargo fmt --all; all 573 tests still passing, clippy still clean. No logic changes — formatting only. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
139 lines
6.0 KiB
Rust
139 lines
6.0 KiB
Rust
//! Criterion benchmarks for the revision Merkle tree.
|
||
//!
|
||
//! Groups:
|
||
//! - `merkle_index/build_tree/N` — build tree from N revisions
|
||
//! - `merkle_index/diff_walk/N/D` — diff walk with D differing revisions
|
||
//! - `merkle_index/serialise/N` — serialise N-leaf tree
|
||
//! - `merkle_index/deserialise/N` — deserialise N-leaf tree
|
||
//! - `merkle_index/serialised_size/N` — report bytes vs flat manifest
|
||
//!
|
||
//! Run:
|
||
//! cargo bench -p clawhdf5-onion -- merkle_bench
|
||
|
||
use clawhdf5_onion::merkle::RevisionMerkleTree;
|
||
use criterion::{BenchmarkId, Criterion, black_box, criterion_group, criterion_main};
|
||
|
||
fn rev_hash(r: u64) -> [u8; 32] {
|
||
*blake3::hash(&r.to_le_bytes()).as_bytes()
|
||
}
|
||
|
||
fn make_entries(n: usize) -> Vec<(u64, [u8; 32])> {
|
||
(0..n as u64).map(|r| (r, rev_hash(r))).collect()
|
||
}
|
||
|
||
// ─────────────────────────────────────────────────────────────────────────────
|
||
|
||
fn bench_merkle(c: &mut Criterion) {
|
||
let ns = [100usize, 1_000, 10_000];
|
||
|
||
// ── Build ─────────────────────────────────────────────────────────────────
|
||
let mut group = c.benchmark_group("merkle_index/build_tree");
|
||
for &n in &ns {
|
||
let entries = make_entries(n);
|
||
group.bench_with_input(BenchmarkId::from_parameter(n), &entries, |b, e| {
|
||
b.iter(|| RevisionMerkleTree::build(black_box(e)));
|
||
});
|
||
}
|
||
group.finish();
|
||
|
||
// ── Diff walk: D=0 (all match) ────────────────────────────────────────────
|
||
let mut group = c.benchmark_group("merkle_index/diff_walk_d0");
|
||
for &n in &ns {
|
||
let tree = RevisionMerkleTree::build(&make_entries(n));
|
||
group.bench_with_input(BenchmarkId::from_parameter(n), &tree, |b, t| {
|
||
b.iter(|| t.diff_missing_revisions(black_box(t)));
|
||
});
|
||
}
|
||
group.finish();
|
||
|
||
// ── Diff walk: D=1 ───────────────────────────────────────────────────────
|
||
let mut group = c.benchmark_group("merkle_index/diff_walk_d1");
|
||
for &n in &ns {
|
||
let full = RevisionMerkleTree::build(&make_entries(n));
|
||
let minus1 = RevisionMerkleTree::build(&make_entries(n - 1));
|
||
group.bench_with_input(
|
||
BenchmarkId::from_parameter(n),
|
||
&(full, minus1),
|
||
|b, (f, m)| {
|
||
b.iter(|| f.diff_missing_revisions(black_box(m)));
|
||
},
|
||
);
|
||
}
|
||
group.finish();
|
||
|
||
// ── Diff walk: D=10 ──────────────────────────────────────────────────────
|
||
let mut group = c.benchmark_group("merkle_index/diff_walk_d10");
|
||
for &n in &ns {
|
||
if n < 20 {
|
||
continue;
|
||
}
|
||
let full = RevisionMerkleTree::build(&make_entries(n));
|
||
let base = RevisionMerkleTree::build(&make_entries(n - 10));
|
||
group.bench_with_input(
|
||
BenchmarkId::from_parameter(n),
|
||
&(full, base),
|
||
|b, (f, base)| {
|
||
b.iter(|| f.diff_missing_revisions(black_box(base)));
|
||
},
|
||
);
|
||
}
|
||
group.finish();
|
||
|
||
// ── Diff walk: D=100 ─────────────────────────────────────────────────────
|
||
let mut group = c.benchmark_group("merkle_index/diff_walk_d100");
|
||
for &n in &[1_000usize, 10_000] {
|
||
let full = RevisionMerkleTree::build(&make_entries(n));
|
||
let base = RevisionMerkleTree::build(&make_entries(n - 100));
|
||
group.bench_with_input(
|
||
BenchmarkId::from_parameter(n),
|
||
&(full, base),
|
||
|b, (f, base)| {
|
||
b.iter(|| f.diff_missing_revisions(black_box(base)));
|
||
},
|
||
);
|
||
}
|
||
group.finish();
|
||
|
||
// ── Serialise ─────────────────────────────────────────────────────────────
|
||
let mut group = c.benchmark_group("merkle_index/serialise");
|
||
for &n in &ns {
|
||
let tree = RevisionMerkleTree::build(&make_entries(n));
|
||
group.bench_with_input(BenchmarkId::from_parameter(n), &tree, |b, t| {
|
||
b.iter(|| black_box(t.serialise()));
|
||
});
|
||
}
|
||
group.finish();
|
||
|
||
// ── Deserialise ───────────────────────────────────────────────────────────
|
||
let mut group = c.benchmark_group("merkle_index/deserialise");
|
||
for &n in &ns {
|
||
let bytes = RevisionMerkleTree::build(&make_entries(n)).serialise();
|
||
group.bench_with_input(BenchmarkId::from_parameter(n), &bytes, |b, b_| {
|
||
b.iter(|| RevisionMerkleTree::deserialise(black_box(b_)).unwrap());
|
||
});
|
||
}
|
||
group.finish();
|
||
|
||
// ── Serialised size report (not timed) ───────────────────────────────────
|
||
println!("\n=== Serialised size vs flat manifest (N × 60 B) ===");
|
||
println!(
|
||
"{:>8} {:>12} {:>12} {:>10}",
|
||
"N", "Merkle (B)", "Flat (B)", "ratio"
|
||
);
|
||
for &n in &[10usize, 100, 1_000, 10_000] {
|
||
let tree = RevisionMerkleTree::build(&make_entries(n));
|
||
let merkle_sz = tree.serialise().len();
|
||
let flat_sz = n * 60;
|
||
println!(
|
||
"{:>8} {:>12} {:>12} {:>10.2}x",
|
||
n,
|
||
merkle_sz,
|
||
flat_sz,
|
||
flat_sz as f64 / merkle_sz as f64
|
||
);
|
||
}
|
||
}
|
||
|
||
criterion_group!(benches, bench_merkle);
|
||
criterion_main!(benches);
|