Skip to content

Commit a90ae6b

Browse files
committed
test(bench): add Blake3Service hash-throughput criterion benchmark
Adds bench_blake3 benchmark group over 3 input sizes (1 KiB / 64 KiB / 1 MiB) x 2 hash modes (quick_hash + full_hash) with Throughput::Bytes annotation so criterion reports MiB/s alongside per-iter time. iter_batched + BatchSize::SmallInput keeps the per-iteration tempfile write out of the measurement window — only the hash call is timed. sample_size = 30 (criterion default = 100; reduced for CI wall-clock budget per spec D-6). Override at invocation: `cargo bench -- --sample-size N`. WHY #![allow(missing_docs)] at the top of the bench file: the workspace lint set denies missing_docs, but `criterion_group!` macro expansion produces an undocumented `pub fn benches()`. Bench targets are not part of the library's public surface (compiled only with --benches), and the macro entry point is third-party. Local 10-sample smoke run completed all 6 benchmarks: 1KiB quick/full ~13 MiB/s, 64KiB ~520 MiB/s, 1MiB ~1.27 GiB/s on this VM (cold-cache dev environment numbers — CI runner numbers will differ; baseline calibration is the follow-up GH issue per spec §10 Q5). Cargo.lock updated with criterion 0.5 transitive deps (anes, cast, ciborium, plotters, etc.) — first build that resolved them.
1 parent 68c12bd commit a90ae6b

2 files changed

Lines changed: 236 additions & 1 deletion

File tree

Cargo.lock

Lines changed: 147 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/hash/benches/blake3.rs

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
//! `Blake3Service` hash-throughput benchmark.
2+
//!
3+
//! Runs `quick_hash` + `full_hash` over 3 input sizes (1 KiB, 64 KiB,
4+
//! 1 MiB). Reports per-iteration time + throughput (MiB/s) per criterion.
5+
//!
6+
//! Slice 4 of T1 test-architecture decomposition. Observability-only —
7+
//! the workflow prints results; no threshold gate.
8+
9+
// WHY allow missing_docs: the workspace lint set denies missing_docs, but
10+
// `criterion_group!` expands to an undocumented `pub fn benches()`. The
11+
// macro is the third-party public-API entry point — annotating its
12+
// expansion with rustdoc is impossible. Bench targets are not part of
13+
// the library's public surface either (compiled only with --benches).
14+
#![allow(missing_docs)]
15+
16+
use std::io::Write;
17+
18+
use criterion::{BatchSize, Criterion, Throughput, criterion_group, criterion_main};
19+
// WHY HashService trait import: Blake3Service's quick_hash/full_hash methods
20+
// are defined inside `impl HashService for Blake3Service` (NOT inherent
21+
// methods). Without `use perima_core::HashService;` in scope, method
22+
// resolution fails at compile time.
23+
use perima_core::HashService;
24+
use perima_hash::Blake3Service;
25+
26+
const SIZES: &[usize] = &[1024, 64 * 1024, 1024 * 1024];
27+
28+
fn bench_blake3(c: &mut Criterion) {
29+
let svc = Blake3Service::new();
30+
31+
for &size in SIZES {
32+
let mut group = c.benchmark_group(format!("blake3/{}", human_size(size)));
33+
// WHY u64 cast: Throughput::Bytes takes u64; usize-to-u64 is
34+
// lossless on 32-bit + 64-bit. clippy::cast_possible_truncation
35+
// would not fire (lossless cast).
36+
group.throughput(Throughput::Bytes(size as u64));
37+
38+
// WHY iter_batched: setup writes a fresh tempfile per batch; the
39+
// measurement is just the hash call. Keeps file I/O setup out of
40+
// the timing window.
41+
group.bench_function("quick_hash", |b| {
42+
b.iter_batched(
43+
|| setup_tempfile(size),
44+
|(_td, path)| {
45+
let _ = svc.quick_hash(&path);
46+
},
47+
BatchSize::SmallInput,
48+
);
49+
});
50+
group.bench_function("full_hash", |b| {
51+
b.iter_batched(
52+
|| setup_tempfile(size),
53+
|(_td, path)| {
54+
let _ = svc.full_hash(&path);
55+
},
56+
BatchSize::SmallInput,
57+
);
58+
});
59+
60+
group.finish();
61+
}
62+
}
63+
64+
/// Build a tempfile of `size` bytes filled with `0xFF`. Returns the
65+
/// `TempDir` (must outlive the path) + the path.
66+
fn setup_tempfile(size: usize) -> (tempfile::TempDir, std::path::PathBuf) {
67+
let td = tempfile::tempdir().expect("tempdir");
68+
let path = td.path().join("bench.bin");
69+
let mut f = std::fs::File::create(&path).expect("create");
70+
f.write_all(&vec![0xFFu8; size]).expect("write");
71+
(td, path)
72+
}
73+
74+
fn human_size(n: usize) -> String {
75+
if n >= 1024 * 1024 {
76+
format!("{}MiB", n / (1024 * 1024))
77+
} else if n >= 1024 {
78+
format!("{}KiB", n / 1024)
79+
} else {
80+
format!("{n}B")
81+
}
82+
}
83+
84+
criterion_group!(
85+
name = benches;
86+
config = Criterion::default().sample_size(30);
87+
targets = bench_blake3
88+
);
89+
criterion_main!(benches);

0 commit comments

Comments
 (0)