Files
clawsync/crates/clawhdf5-onion/benches/merkle_bench.rs
T
osobhandClaude Sonnet 4.6 1c107fe58a Apply rustfmt to entire workspace
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]>
2026-04-04 20:31:33 -05:00

139 lines
6.0 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! 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);