Skip to Content
Course content

235: Profiling Rust Code with perf and flamegraph

Click on the "Edit" button in the top corner of the screen to edit your slide content.

How do I actually get perf to show me Rust functions instead of just hex addresses?

If you've tried running perf record on a Rust binary and then ran perf report only to see a wall of 0x00007f... addresses, you're not alone. It's frustrating. The problem is that by default, cargo build --release strips out the debug symbols to keep the binary small. Perf knows where the CPU is spending time, but it has no map to tell it which function name belongs to that address.

To fix this, you need to tell Cargo to keep the symbol information even in release mode. Add this to your Cargo.toml:

[profile.release]
debug = true

I always do this when I'm in the profiling phase. Don't worry, it doesn't slow down the execution speed of your code—it just makes the binary larger by including a symbol table. Now, when you run perf record ./target/release/my_app and then perf report, you'll actually see names like my_app::calculate_primes instead of random memory locations.

Are flamegraphs actually better than just reading the perf report?

In my experience, yes—by a mile. A perf report is a list; it tells you which functions are expensive, but it doesn't clearly show the call path. A flamegraph turns that data into a visual mountain range. The x-axis isn't time; it's the population of samples. The wider the bar, the more time the CPU spent in that function (or its children).

Instead of wrestling with perf manually, I recommend using cargo-flamegraph. It's a wrapper that handles the recording and the conversion to SVG for you. Install it with cargo install cargo-flamegraph, and then just run:

cargo flamegraph

When you open the resulting flamegraph.svg, look for the "plateaus"—those wide tops of the stacks. If you see a massive block for a function you didn't expect, that's your bottleneck. I've spent hours optimizing a loop only to realize via a flamegraph that the real culprit was actually a hidden allocation inside a library function I was calling.

Does the optimization level change what I see in the profile?

Absolutely. This is a common trap. If you profile a debug build, you're essentially profiling the compiler's lack of optimization. You'll see a massive amount of time spent in stack frame setup and tiny, un-inlined helper functions. It's noise. You'll waste your afternoon "fixing" things that the compiler would have optimized away in a release build anyway.

Always profile your --release build. However, be aware that --release enables inlining. If the compiler decides to inline a small function into its caller, that function effectively disappears from the flamegraph because it no longer has its own stack frame. If you suspect a specific small function is the problem but you can't see it, you can temporarily tell Rust not to inline it for the sake of the profile:

#[inline(never)]
fn expensive_small_task() {
    // ...
}

Just remember to remove that attribute before you push to production, or you'll be leaving performance on the table.




📋 Practical Task

Optimizing a Naive Prime Finder with Flamegraph Analysis

You are given a Rust program that calculates the sum of all prime numbers up to 1,000,000 using a very inefficient "trial division" method. Your goal is to identify the exact bottleneck and optimize it.

The Setup:

  • Create a project with a function is_prime(n: u64) -> bool that checks for primality by iterating from 2 to n - 1.
  • Call this function in a loop from 2 to 1,000,000.
  • Ensure your Cargo.toml has debug = true set for the release profile.

The Challenge:

  1. Run cargo flamegraph and locate the is_prime block. Notice how wide it is compared to the rest of the program.
  2. Optimize the is_prime function (e.g., by checking only up to the square root of n and skipping even numbers).
  3. Run cargo flamegraph again.
  4. Compare the two SVG files. You should see the is_prime block shrink dramatically in width relative to the total execution time.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.