Expand description
§ssb
An instrumented benchmarking library for Rust, powered by fastrace.
Unlike traditional benchmark tools that only measure total function time,
ssb collects span-level timing from your fastrace-instrumented code,
letting you see how each named phase performs across hundreds of iterations
and compare results between runs.
§Quick start
Add to Cargo.toml:
[dev-dependencies]
ssb = "*"
fastrace = { version = "0.7", features = ["enable"] }
[[bench]]
name = "my_bench"
harness = falseInstrument your code with fastrace spans:
#[fastrace::trace]
fn phase1(data: &[u8]) -> Vec<u8> {
// ... your work
}
#[fastrace::trace]
fn phase2(data: Vec<u8>) -> Vec<u8> {
// ... your work
}
fn process(input: &[u8]) -> Vec<u8> {
phase2(phase1(input))
}Write benches/my_bench.rs:
use ssb::Bench;
fn bench_process() {
let input = vec![0u8; 1024];
// On the first run: prints stats and saves to
// target/ssb/process/baseline.json
// On subsequent runs: loads that file, prints comparison, updates it.
Bench::new("process")
.iterations(1000)
.warmup(100)
.run(|| process(&input));
}
ssb::bench_main!(bench_process);Run with cargo bench (or cargo run --example ... for examples).
The first run prints stats; every subsequent run compares against the
previous result automatically.
§Grouping benchmarks
Use Bench::group to organise benchmarks into subdirectories (mirrors
criterion’s criterion_group! layout):
use ssb::Bench;
fn bench_parse() {
// stored at target/ssb/parsing/parse/baseline.json
Bench::new("parse").group("parsing").run(|| parse(&data));
}§Manual persistence
Call Bench::no_auto_save to skip the automatic I/O and handle the
BenchReport yourself:
use ssb::{Bench, BenchReport};
let report = Bench::new("process")
.no_auto_save()
.run(|| process(&input));
// Save to a custom path (e.g. a CI artifact directory)
report.save("/tmp/ci-results/process.json").unwrap();
// Compare with a baseline loaded from anywhere
let baseline = BenchReport::load("previous.json").unwrap();
report.compare(&baseline).print();§Span nesting
Span::enter_with_local_parent records the current thread-local span as
the parent, but does not automatically make the new span the
thread-local parent for subsequent calls. For proper nesting either:
- Use
#[fastrace::trace](recommended — handles this automatically). - Or call
span.set_local_parent()manually.
Without one of these, all spans in a call chain appear as siblings under the benchmark root rather than as a nested tree.
§Concurrency
ssb uses a global reporter. Do not run multiple Bench::run calls
concurrently (e.g. from parallel test threads). Use
cargo test -- --test-threads=1 when running benchmarks as tests.
Macros§
- bench_
main - Generate a
mainfunction that runs the given benchmark functions.
Structs§
- Bench
- Builder for a single benchmark.
- Bench
Group - Accumulates multiple benchmarks belonging to the same group and prints a single combined table on drop.
- Bench
Report - A complete benchmark report: per-span statistics across all iterations.
- Comparison
- Side-by-side comparison of a current report against a baseline.
- Span
Stats - Aggregated timing statistics for a single named span across all iterations.
- WrapFn
- Helper type to implement BenchFn for both
Fn() -> AnyandFn(&mut Bench).