How to Measure Performance in Rust: Benchmarking for Beginners

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Why Does Rust Require a Special Approach to Benchmarking?



Rust is famous for its speed, but even the fastest language can be slowed down by a poor implementation. Have you ever written an algorithm in Rust, thinking, "This will be lightning fast!", and then ran it and couldn't understand — where did the microseconds go? Sound familiar?



Benchmarking in Rust is not just about starting a timer and measuring time. It's a whole science: you need to account for compiler optimizations, CPU caching, and even how Rust manages memory. In this article, we will break down how to properly benchmark programs in Rust so you get honest numbers, not random results.



We will go from simple measurements with std::time to the professional tool criterion, which is used in real-world projects. Let's go!



1. The Simplest Way: std::time::Instant



If you need to quickly "feel" the performance of a piece of code — without installing crates, just insert the measurement directly into main. But there's a catch: the Rust optimizer may "throw away" your code if it doesn't use the result.



use std::time::Instant;

fn main() { let start = Instant::now();

// Your code to be measured let mut sum = 0u64; for i in 0..1_000_000 { sum += i; }

let duration = start.elapsed(); println!("Execution time: {:?}", duration); // Be sure to use the result! println!("Sum: {}", sum);}


Why Can't You Just Measure a Single Run?

A single run is a lottery. The system might be busy, the CPU might be throttling, the cache might be cold. A proper Rust benchmark should:

  • Warm up the code — several iterations before measurement
  • Run the code many times (thousands of iterations)
  • Average the results


Manual implementation of this is a path to errors. That's why the Rust community created criterion.



2. Professional Benchmarking: The Criterion Crate



Criterion.rs is the de facto standard for benchmarking in Rust. It is used in the rustc compiler itself and in hundreds of crates. Why is it great?

  • Automatic warm-up and statistical processing
  • Charts and comparison with previous runs
  • Regression detection (if your code becomes slower — you'll know)
  • Support for parameterized benchmarks


Installation and Setup

Add to Cargo.toml:

[dev-dependencies]criterion = { version = "0.5", features = ["html_reports"] }

[[bench]]name = "my_benchmark"harness = false


Create a file benches/my_benchmark.rs:



use criterion::{black_box, criterion_group, criterion_main, Criterion};

// The function being benchmarkedfn fibonacci(n: u64) -> u64 { match n { 0 => 0, 1 => 1, _ => fibonacci(n - 1) + fibonacci(n - 2), }}

fn bench_fibonacci(c: &mut Criterion) { c.bench_function("fibonacci 20", |b| { // black_box — prevents the optimizer from discarding the code b.iter(|| fibonacci(black_box(20))) });}

criterion_group!(benches, bench_fibonacci);criterion_main!(benches);


Run: cargo bench. You will see something like:

fibonacci 20             time:   [24.567 µs 24.789 µs 25.012 µs]


The three numbers are the confidence interval (lower, middle, upper bound). If the interval is narrow — the benchmark is stable.



3. The Secret Ingredient: black_box



You might have noticed black_box in the example. This is a magic function from criterion that prevents LLVM from optimizing away arguments or the result. Without it, your benchmark might show 0 ns — because the compiler simply computed the constant at compile time.



Example p

Blogs

Book Recommendations