Question
Is it possible to benchmark programs in Rust? If so, how can you do it?
For example, how can you measure the execution time of a Rust program or a section of code in seconds?
A simple example might look like this:
use std::time::Instant;
fn main() {
let start = Instant::now();
// Code to measure
let mut sum = 0;
for i in 0..1_000_000 {
sum += i;
}
let elapsed = start.elapsed();
println!("sum = {sum}, elapsed = {:.6} seconds", elapsed.as_secs_f64());
}
Short Answer
By the end of this page, you will understand how to measure execution time in Rust, when to use simple timing versus proper benchmarking, and how developers benchmark Rust code in real projects. You will also learn common mistakes that make timing results unreliable.
Concept
Rust supports benchmarking in a few different ways, depending on what you want to measure.
The most basic approach is timing code manually with std::time::Instant. This is useful when you want to know how long a program or one specific block of code takes to run.
For example, you can:
- record a start time with
Instant::now() - run the code
- ask for the elapsed time with
start.elapsed()
This gives you a duration, which you can print in:
- seconds
- milliseconds
- microseconds
- nanoseconds
For more serious performance work, developers usually use benchmarking tools rather than one-off timing. A benchmark tool runs code many times, reduces noise, and helps compare different implementations more fairly.
In Rust, common options are:
std::time::Instantfor simple timingcargo benchfor benchmark targets- the
criterioncrate for more reliable statistical benchmarking
This matters because raw execution time is often misleading. A single run can be affected by:
- CPU scheduling
- memory allocation
- caching effects
- debug vs release build differences
- background processes on your machine
So the real idea is:
- use
Instantwhen you want a quick measurement
Mental Model
Think of benchmarking like timing a runner.
If you use a stopwatch once, you get a rough result. That is like using Instant around your code.
If you want a fair comparison between two runners, you would:
- make them run multiple times
- use the same track
- reduce outside distractions
- average the results
That is what a real benchmark framework does.
So:
Instant= a stopwatch for quick checks- benchmarking framework = a proper timed test with repeated runs and better analysis
Syntax and Examples
Measuring elapsed time with Instant
The simplest way to time Rust code is with std::time::Instant.
use std::time::Instant;
fn main() {
let start = Instant::now();
let mut total = 0;
for i in 0..5_000_000 {
total += i;
}
let duration = start.elapsed();
println!("total = {total}");
println!("Elapsed: {:.6} seconds", duration.as_secs_f64());
}
What this does
Instant::now()stores the current moment- the loop runs
start.elapsed()returns aDurationas_secs_f64()converts that duration into seconds as a decimal number
Measuring milliseconds
Step by Step Execution
Consider this example:
use std::time::Instant;
fn main() {
let start = Instant::now();
let mut value = 0;
for i in 1..=3 {
value += i;
}
let elapsed = start.elapsed();
println!("value = {value}");
println!("elapsed = {:.6} seconds", elapsed.as_secs_f64());
}
Here is what happens step by step:
-
let start = Instant::now();- Rust stores the current instant in time.
-
let mut value = 0;- A mutable variable is created.
-
for i in 1..=3- The loop runs with
i = 1, , and .
- The loop runs with
Real World Use Cases
Benchmarking and timing are used in many practical Rust tasks.
Command-line tools
A CLI program may measure how long it takes to:
- parse files
- search text
- compress data
- process logs
Web services and APIs
Developers often measure:
- request handling time
- JSON serialization speed
- database query processing time
- middleware overhead
Data processing
Rust is often used for fast data pipelines. Timing helps compare:
- parsing strategies
- batch sizes
- algorithms for sorting or grouping
- memory-heavy transformations
Systems programming
In lower-level code, timing helps evaluate:
- file I/O performance
- network packet processing
- caching strategies
- lock contention and synchronization overhead
Algorithm comparison
Suppose you wrote two functions that produce the same result. Benchmarking helps answer:
- which one is faster?
- how much faster?
- does the speed difference matter for realistic inputs?
Real Codebase Usage
In real Rust projects, developers usually do not sprinkle Instant::now() everywhere permanently. Instead, they apply timing and benchmarking in structured ways.
Quick local profiling
During development, a programmer may temporarily wrap a slow section:
let start = std::time::Instant::now();
let result = process_records(&records);
println!("process_records took {} ms", start.elapsed().as_millis());
This is useful for quick investigation.
Guarding important code paths
Developers often measure only expensive operations, such as:
- parsing
- sorting
- API response generation
- large allocations
Comparing implementations
A team may write two versions of the same logic and benchmark both:
- naive implementation
- optimized implementation
Then they keep the version that is both correct and meaningfully faster.
Benchmark suites
In mature projects, benchmark files are added under benches/ so performance can be checked regularly.
This is especially common for:
Common Mistakes
1. Measuring in debug mode
A very common mistake is timing code with:
cargo run
Debug builds are much slower and are not suitable for performance conclusions.
Use:
cargo run --release
or for benchmarks:
cargo bench
2. Timing code only once
One run is noisy. The result may change because of background system activity.
Better approach
- run the code multiple times
- average results
- use a benchmark framework for serious comparison
3. Benchmarking code that gets optimized away
Broken example:
use std::time::Instant;
fn main() {
let start = Instant::now();
let mut sum = 0;
for i in 0..1_000_000 {
sum += i;
}
let _ = start.();
}
Comparisons
Common ways to measure performance in Rust
| Approach | Best for | Pros | Cons |
|---|---|---|---|
std::time::Instant | Quick manual timing | Built into Rust, simple to use | No statistics, easy to misuse |
cargo bench | Organized benchmark targets | Standard Rust workflow | Built-in benchmarking support has changed over time and is less convenient alone |
criterion crate | Reliable performance comparisons | Repeated runs, statistical analysis, reports | Requires extra setup |
Instant vs Criterion
| Feature |
|---|
Cheat Sheet
Quick timing with Instant
use std::time::Instant;
let start = Instant::now();
// code here
let elapsed = start.elapsed();
Convert Duration
elapsed.as_secs_f64(); // fractional seconds
elapsed.as_secs(); // whole seconds
elapsed.as_millis(); // milliseconds
elapsed.as_micros(); // microseconds
elapsed.as_nanos(); // nanoseconds
Time a whole function
let start = Instant::now();
do_work();
println!("{} ms", start.elapsed().as_millis());
Important rules
FAQ
How do I measure execution time in Rust?
Use std::time::Instant. Record the start time with Instant::now(), run your code, then call start.elapsed().
How do I print elapsed time in seconds in Rust?
Use elapsed.as_secs_f64() to get fractional seconds:
println!("{:.6}", elapsed.as_secs_f64());
Should I use cargo run or cargo run --release for timing?
Use cargo run --release. Debug builds are much slower and do not reflect real performance.
What is the difference between timing and benchmarking?
Timing usually means measuring one run. Benchmarking means running code repeatedly and analyzing performance more carefully.
Does Rust have built-in benchmarking?
Rust supports benchmark workflows, but many developers use the criterion crate because it is easier and more reliable for practical benchmarking.
Why are my benchmark results inconsistent?
Small timing differences can be affected by CPU load, caching, memory allocation, and background processes. Run multiple times and use a benchmark tool for better results.
Mini Project
Description
Build a small Rust program that measures how long two different approaches take to compute the sum of numbers from 0 to n. This demonstrates basic timing with Instant and shows how to compare implementations in a practical way.
Goal
Create a Rust program that times two summing strategies and prints their results and elapsed times in milliseconds and seconds.
Requirements
- Write one function that sums numbers using a loop.
- Write another function that sums numbers using an iterator.
- Measure each function separately with
std::time::Instant. - Print both the computed sum and the execution time.
- Run the program in release mode to compare performance realistically.
Keep learning
Related questions
Accessing Cargo Package Metadata in Rust
Learn how to read Cargo package metadata like version, name, and authors in Rust using compile-time environment macros.
Associated Types vs Generic Type Parameters in Rust: When to Use Each
Learn when to use associated types vs generic parameters in Rust traits, with clear rules, examples, and practical API design advice.
Convert an Integer to a String in Rust
Learn the current Rust way to convert integers to strings, why `to_str()` no longer works, and when to use `to_string()` or `format!`.