Profiling Rust Code in Practice: Finding Bottlenecks

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

Profiling Rust Code: Finding Bottlenecks in Practice

When you write in Rust, performance is often a key advantage. But even the fastest code can have hidden bottlenecks: slow allocations, inefficient algorithms, or caching issues. Profiling is the process of measuring program performance to identify such places. In this article, we will cover practical tools and methods for profiling Rust code, from simple benchmarks to flamegraph visualization.



Why Profile Code in Rust?

Rust guarantees memory safety, but it does not guarantee optimal performance. Bottlenecks can arise from:

  • Inefficient data structures (e.g., using Vec instead of HashMap for frequent lookups).
  • Excessive allocations (cloning strings or vectors where references could be used).
  • Caching issues (incorrect matrix traversal order).
  • Lock contention in multithreaded code (contention on mutexes).

Profiling helps you find exactly which parts to optimize, rather than guessing blindly.



Tools for Profiling Rust

Let's look at three main approaches: built-in benchmarks, system profilers, and visualization.



1. Benchmarks with criterion

The criterion library is the standard for micro-benchmarks in Rust. It allows measuring the execution time of individual functions, accounting for statistical outliers.

Setup in Cargo.toml:

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

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

Example benchmark (file benches/my_benchmark.rs):

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

fn fibonacci(n: u64) -> u64 { match n { 0 => 1, 1 => 1, n => fibonacci(n-1) + fibonacci(n-2), }}

fn benchmark_fib(c: &mut Criterion) { c.bench_function("fib 20", |b| b.iter(|| fibonacci(black_box(20))));}

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

Run: cargo bench. Criterion will show the average time, standard deviation, and generate an HTML report with charts.



2. System Profiler perf (Linux)

perf is a powerful Linux kernel tool for collecting CPU statistics (cycles, cache misses, instructions). It is especially useful for Rust as it shows not only time but also hardware events.

Usage example:

# Collect dataperf record -g -- ./target/release/my_program

# Interactive analysisperf report -g

# View hot functionsperf top -p $(pgrep my_program)

The -g flag enables call graph recording. This allows you to see which functions are calling the slow code.



3. Visualization with flamegraph

Flamegraph is a way to visualize the call stack. The width of a rectangle shows how much time the program spent in that function and its descendants. The wider the block, the more time.

Installing the tool:

cargo install flamegraph

Run:

cargo flamegraph --bin my_program

After execution, you will get a flamegraph.svg file. Open it in a browser — you will see an interactive graph where you can click on blocks for details.



Practical Example: Finding a Bottleneck

Suppose we have a program that processes a large text file and counts word frequency. The code might look like this:

use std::collections::HashMap;use std::fs::File;use std::io::{Buf                

Blogs

Book Recommendations