Skip to content

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 Timevideo ↗, 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_clock is "steady_clock in a trenchcoat" in libc++ and in MSVC's library. In libstdc++ — the library whose error message is on the very next slide — it is system_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.