Rust Performance Engineering

Optimization Levels

The opt-level setting in Cargo.toml (or -C opt-level=<level> passed to rustc) controls which LLVM optimization passes are executed during compilation, trading off compilation time and binary size against runtime execution speed.

Compile Time & Binary Size vs. Runtime Speed:

  `opt-level = 0`   Fastest compile, zero optimization (Debug default)
  `opt-level = 1`   Basic register allocation & dead-code cleanup (Fast compile, ~3-5x speedup)
  `opt-level = 2`   Most speed optimizations without aggressive code bloat
  `opt-level = 3`   Maximum speed: aggressive loop unrolling, inlining, & SIMD (Release default)
  `opt-level = "s"` Optimize for small binary size while keeping reasonable speed
  `opt-level = "z"` Optimize for minimum binary size (disables loop vectorization/unrolling)

Detailed Comparison of All Six opt-level Settings

opt-level Equivalent Clang/GCC Flag Loop Unrolling & Auto-Vectorization Function Inlining Threshold Primary Use Case
0 -O0 Disabled Only #[inline(always)] Instantaneous incremental cargo check / cargo build and interactive LLDB stepping.
1 -O1 Disabled Conservative Fast test compilation when 0 is too slow for compute-heavy unit tests.
2 -O2 Enabled (conservative) Standard Balanced production builds where instruction-cache footprint matters alongside speed.
3 -O3 Aggressive Aggressive Default [profile.release] and [profile.bench] for maximum CPU throughput.
"s" -Os Vectorization only if size-neutral Reduced WebAssembly (.wasm), mobile libraries, and flash-constrained embedded firmware.
"z" -Oz Disabled Minimal Ultra-compact bootloaders, microcontrollers, and size-capped WASM binaries.

Hybrid Per-Package Optimization in Cargo.toml

You do not have to choose a single opt-level for your entire dependency graph! Cargo's profile overrides ([profile.<name>.package.<pkg>]) allow you to compile hot third-party dependencies (such as image, regex, sqlx, or sha2) at opt-level = 2 or 3 even during normal cargo run and cargo test debug builds:

[profile.dev]
# Keep your own workspace crates at opt-level = 0 or 1 for sub-second rebuilds:
opt-level = 0

# Compile all external dependencies at opt-level = 2 (cached once in target/debug/deps!):
[profile.dev.package."*"]
opt-level = 2

# Compile a specific performance-critical workspace crate at opt-level = 3 even in dev:
[profile.dev.package.my_crypto_engine]
opt-level = 3

When opt-level = 2 or "s" Can Outperform opt-level = 3

While opt-level = 3 is fastest for tight numerical kernels and benchmarks that fit inside the CPU's 32 KiB / 64 KiB L1 instruction cache (L1i), aggressive inlining and loop unrolling across a massive 50 MB server binary can occasionally increase L1 instruction cache misses. Benchmarking your full application under both opt-level = 2 and opt-level = 3 verifies whether aggressive unrolling helps or hurts your specific workload.

pub fn polynomial_horner(coeffs: &[f64], x: f64) -> f64 {
    // At opt-level >= 2, this fold lowers to a tight FMA (`fmadd`) register loop:
    coeffs.iter().rev().fold(0.0, |acc, &c| acc.mul_add(x, c))
}

fn main() {
    // Evaluate 2x^2 + 3x + 5 at x = 4.0 -> 2(16) + 12 + 5 = 49.0
    let y = polynomial_horner(&[5.0, 3.0, 2.0], 4.0);
    println!("p(4.0) = {y:.1}");
}
p(4.0) = 49.0