Part 9 · 1 chapters · ~8 min

Performance and Profiling

Release builds and profiles (LTO, codegen-units, panic=abort), measuring with criterion, profiling with perf, cargo-flamegraph and samply, allocation profiling with dhat and heaptrack, choosing allocators (jemalloc, mimalloc), avoiding unnecessary clones and allocations, SIMD, and when Rust is not faster.

13

Measuring Rust

code
# Cargo.toml release profile for services
[profile.release]
lto = "thin"
codegen-units = 1
debug = "line-tables-only"     # keep symbols for profiling

cargo flamegraph --bin ledger         # perf + flame graph (Linux) / dtrace (macOS)
samply record ./target/release/ledger # a profiler that opens in the Firefox Profiler UI
#[global_allocator] static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;   # often faster than the system allocator

Measured here: a release build of a small program was 540 KB. Rust is not automatically faster: a Rust service that clones strings in every handler, allocates per request and holds locks across awaits can be slower than well-written Go or Java. Profile first; the usual wins are fewer allocations, borrowing instead of cloning, and better algorithms.