Time and benchmarking¶
Level: 201 · for programmers who time things
One line: Before you can trust a benchmark you have to trust three things — the clock, the types it hands you, and the compiler — and this chapter shows each of them failing in a way you would not have noticed.
The chapter follows Matt Godbolt's closing keynote at C++Now 2026, Benchmarking: It's About Time — video ↗, slides ↗, slide source ↗. Every claim taken from it was re-run on libstdc++ and libc++, and three came out differently from the slide:
- The trenchcoat.
high_resolution_clockis "steady_clockin a trenchcoat" in libc++ and in MSVC's library. In libstdc++ — the library whose error message is on the very next slide — it issystem_clock. Three clocks - The shorthand. The slide's
using duration = duration<rep, period>;is fine as pseudocode; compiled, it is an error in GCC 12, a warning in GCC 13 and later, and silent in Clang. What a clock is DoNotOptimize(const T&). The form the slide builds is now deprecated by Google Benchmark — and even the form it recommends protects only a value: Clang still computes a kept result by formula. The optimizer deletes your benchmark
The lessons¶
| Lesson | What it shows | The same in Rust | |
|---|---|---|---|
| 1 | Three clocks, and the one in the trenchcoat | a wall clock, a stopwatch, and an alias that is one of the two — which one is the library's call | Two clocks: Instant and SystemTime ↗ |
| 2 | A clock is four types, a flag and now() |
the talk's first chrono slide, filled in — and a clock a test can drive | — std has no clock trait |
| 3 | A time point knows its clock | the Type safety slide: why system - steady is refused, and why that is right |
An Instant is not a SystemTime ↗ |
| 4 | A duration is a count and a unit | lossless conversions happen by themselves, lossy ones are asked for — and why numeric_limits<duration>::max() is zero |
A Duration cannot be negative ↗ |
| 5 | Timing a block of code | the three-line pattern, the one clock that belongs in it, and what one number cannot tell you | Timing a block ↗ |
| 6 | The optimizer deletes your benchmark | unused results vanish, kept ones get computed by formula, and the barrier has a cost of its own | black_box is a hint ↗ |
Outlined, not yet written¶
The talk goes further than the six pages above. These are stubs — permanent URLs, and the questions each page has to answer:
| Outline | The question | The talk's section |
|---|---|---|
What happens in now() |
what actually runs when you ask the time — down to the vDSO? | What is now()? |
| The cost of asking the time | how long does now() take, and how do you take that out of a measurement? |
What is now()?, When will then be now()? |
| Reading the cycle counter | when is rdtsc worth it, and what does fencing it cost? |
When will then be now()? |
| The branch predictor learns your benchmark | why does repeating one input measure something else? | Confounding factors |
| The cache changes the answer | why does the same code run at different speeds at different sizes? | Caching ruins everything |
| One number is not a measurement | minimum, median or distribution — and which question are you asking? | So, what can we do? |
If you are coming from Rust¶
The Rust library's Time and benchmarking ↗ section is this chapter's twin: two clocks instead of three, one unsigned Duration instead of a type per unit, and std::hint::black_box where C++ has no standard answer at all.