24.8 Benchmarking
Benchmarking measures the execution speed (latency) or throughput of code snippets. It complements testing by tracking performance characteristics and helping to identify regressions or validate optimizations. Systems programming often requires careful performance management, making benchmarking a valuable tool.
Rust historically had unstable, built-in benchmarking support usable only on the nightly compiler toolchain. However, the ecosystem has largely standardized on powerful third-party crates that work on stable Rust. criterion
and divan
are two popular choices.
24.8.1 Built-in Benchmarks (Nightly Rust Only - Deprecated)
The built-in harness (#[bench]
attribute, test::Bencher
) requires the nightly toolchain and the #![feature(test)]
flag. Due to its instability and the maturity of external alternatives, it’s generally not recommended for new projects. We mention it here for historical context but advise using crates like criterion
or divan
.
24.8.2 Benchmarking with criterion
(Stable Rust)
criterion
is a widely used, statistics-driven benchmarking library. It performs multiple runs, analyzes results statistically to mitigate noise, detects performance changes between runs, and can generate detailed HTML reports.
- Add Dependency and Configure Harness:
# Cargo.toml [dev-dependencies] criterion = { version = "0.5", features = ["html_reports"] } # Check for latest version # Tell Cargo to use criterion's test harness for benchmarks, not the default one. # 'main' corresponds to the benchmark file benches/main.rs [[bench]] name = "main" harness = false
- Create Benchmark File: Create a file like
benches/main.rs
.// benches/main.rs use criterion::{black_box, criterion_group, criterion_main, Criterion}; // Assume fibonacci function is in your library crate 'my_crate' use my_crate::fibonacci; // Ensure this function is pub or accessible fn fibonacci_benchmarks(c: &mut Criterion) { // Benchmark fibonacci(20) c.bench_function("fib 20", |b| b.iter(|| fibonacci(black_box(20)))); // Benchmark fibonacci(30) with a different ID c.bench_function("fib 30", |b| b.iter(|| fibonacci(black_box(30)))); } // Group benchmarks together criterion_group!(benches, fibonacci_benchmarks); // Generate the main function required to run criterion benchmarks criterion_main!(benches);
black_box
: A function that prevents the compiler from optimizing away the code being benchmarked, ensuring the work is actually performed.Criterion::bench_function
: Defines a single benchmark.Bencher::iter
: Runs the provided closure multiple times to gather statistics.
- Run: Execute
cargo bench
. Results and reports are saved intarget/criterion/
.
24.8.3 Benchmarking with divan
(Stable Rust >= 1.80)
divan
is a newer benchmarking library (requires Rust 1.80+) focused on simplicity, low overhead, and features like parameterized benchmarking using attributes.
- Add Dependency and Configure Harness:
# Cargo.toml [dev-dependencies] divan = "0.1" # Check for the latest version [[bench]] name = "main" # Corresponds to benches/main.rs harness = false
- Create Benchmark File: Create
benches/main.rs
.// benches/main.rs use my_crate::fibonacci; // Assume function is in your lib crate fn main() { // Run all benchmarks registered in this crate divan::main(); } // Simple benchmark for a fixed input #[divan::bench] fn fib_10() -> u64 { fibonacci(divan::black_box(10)) } // Parameterized benchmark: runs for each value in `args` #[divan::bench(args = [5, 15, 25])] fn fib_param(n: u32) -> u64 { fibonacci(n) // black_box is often implicitly handled by divan }
divan::main()
: Initializes and runs the benchmarks.#[divan::bench]
: Attribute marks a function as a benchmark.args = [...]
: Provides input values for parameterized benchmarks.divan::black_box
is available if needed, butdivan
often applies it automatically.
- Run: Execute
cargo bench
. Results are printed directly to the console.
Choosing between criterion
and divan
depends on project needs; criterion
offers more detailed statistical analysis and reporting, while divan
emphasizes ease of use and lower overhead.