90. black_box — Stop the Compiler From Erasing Your Benchmarks
Your benchmark ran in 0 nanoseconds? Congratulations — the compiler optimised away the code you were trying to measure. std::hint::black_box prevents that by hiding values from the optimiser.
The problem: the optimiser is too smart
The Rust compiler aggressively eliminates dead code. If it can prove a result is never used, it simply removes the computation:
| |
In release mode, the compiler can see through this and may still optimise the loop away — or even compute the answer at compile time. Your benchmark reports near-zero time, and you learn nothing.
Enter black_box
std::hint::black_box takes a value and returns it unchanged, but the compiler treats it as an opaque barrier — it can’t see through it, so it can’t optimise away whatever produced that value:
| |
Two black_box calls do the trick:
- Wrap the input — prevents the compiler from constant-folding the argument
- Wrap the output — prevents dead-code elimination of the computation
Before and after
Without black_box (release mode):
| |
With black_box (release mode):
| |
It works on any type
black_box is generic — it works on integers, strings, structs, references, whatever:
| |
Micro-benchmark recipe
Here’s a minimal pattern for quick-and-dirty micro-benchmarks:
| |
Without black_box, the compiler could hoist the pure function call out of the loop or eliminate it entirely. With it, each iteration does real work.
When to use it
Reach for black_box whenever you’re timing code and the results look suspiciously fast. It’s also the foundation that benchmarking frameworks like criterion and the built-in #[bench] use under the hood.
It’s not a full benchmarking harness — for serious measurement you still want warmup, statistics, and outlier detection. But when you need a quick sanity check, black_box + Instant gets the job done.
Available since Rust 1.66 on stable.