Skip to main content

Crate ssb

Crate ssb 

Source
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 = false

Instrument 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 main function that runs the given benchmark functions.

Structs§

Bench
Builder for a single benchmark.
BenchGroup
Accumulates multiple benchmarks belonging to the same group and prints a single combined table on drop.
BenchReport
A complete benchmark report: per-span statistics across all iterations.
Comparison
Side-by-side comparison of a current report against a baseline.
SpanStats
Aggregated timing statistics for a single named span across all iterations.
WrapFn
Helper type to implement BenchFn for both Fn() -> Any and Fn(&mut Bench).

Traits§

BenchFn