First release since v4.3.11 in February 2023. Most of what's here is correctness work on the measurement path itself, so some numbers will change — see below before you compare against older results.
Measurements are more accurate
Six defects found while auditing the measurement path. These change reported numbers, which is the point:
doNotOptimizeAwaywas weaker on clang-cl. clang in MSVC-compatibility mode defines_MSC_VER, and the guards only tested for that, so every call went through an opaque function call instead of the inline assembly barrier — slower and less effective at stopping the optimizer. Now picked by what the compiler understands. (#111)epochIterations()was silently ignored whenwarmup()was also set.epochs(5).epochIterations(1000).warmup(100)actually ran 381617, 373323, 1000, 1000, 1000 iterations — epochs of wildly different size mixed into one median.- The
relativecolumn compared raw epoch runtimes while every other column of the row is per unit, so the percentage was wrong by the ratio of the batch sizes whenever the baseline used a differentbatch(). (#131) medianAbsolutePercentErrorreturned NaN for a measurement of 0, poisoningerr%, the CSV/JSON output, and the strict weak orderingstd::sortrequires.- Performance counters were not compensated for multiplexing. When more events are monitored than the hardware can count at once the kernel time-shares them, so cycles/instructions/branches were silently underreported — e.g. when the NMI watchdog holds a counter, or on a VM with a restricted PMU. Now scaled by
time_enabled / time_running, the wayperf statdoes it. calcBestNumIterscould cast a non-finite double touint64_t, which is undefined behavior.
It builds where it didn't
- musl / Alpine. nanobench had not compiled there since 2023: glibc declares
ioctl()'s request parameter asunsigned long, musl asint, andPERF_EVENT_IOC_IDdoesn't fit in anint. There's now an Alpine CI leg so it can't come back. (#92) -fno-rtti. Installing thestd::numpunctfacet used for thousands separators goes through__dynamic_cast, which reads through a null pointer without RTTI — a crash in nanobench's own output formatting. The separators are now produced withoutstd::locale, so output is identical with RTTI on or off, and a-fno-rttiCI leg guards it. (#122)- GCC 14 (
<cstdint>), Intel icx 2025 (-Wfloat-equalviastd::tie) (#127), macOS with recent AppleClang (#126, #129), and a CMake deprecation warning that surfaced in consumers' builds (#137).
New API
All additions — nothing existing changed shape.
Bench::setup()— an untimed step before each epoch, for restoring state the benchmark consumes. Note it runs once per epoch, not once per iteration; the docs are explicit about which cases that does and does not cover. Thanks @l0rinc. (#130, #133, #86)Bench::hideColumn()/showColumn()with theColumnenum — the full table is over 150 characters and wraps in most terminals. (#97)Bench::contextColumn()— show acontext()variable as a table column, for parameterised benchmarks. (#103)elapsedms/elapsedus/elapsednsin render templates —elapsedis in seconds and the template language has no arithmetic to rescale it. (#107)
Documentation
The output table is printed all over the docs and not one column was ever defined — total, for instance, is the wall-clock time the measurement cost, not a property of the code being benchmarked. There's now a column reference, an explicit statement of which columns are Linux-only, and nanobench says so itself when perf_event_open is refused instead of just showing a narrower table. (#114, #123, #106, #117)
Infrastructure
Nothing on GitHub compiled nanobench before this release: Travis, Cirrus and AppVeyor had all stopped running for the repository. CI is now GitHub Actions across gcc/clang × C++11..20, 32-bit, libc++, sanitizers, ARM64, macOS, MSVC, clang-cl, MinGW, musl and -fno-rtti, plus pinned linters, a CMake consumer check, and docs built and deployed on every push.
Thanks
@l0rinc, @pps83, @viordash, @Adda0, @tazio, @erinacio, @melg8, @wsehjk, @foolnotion, @maldag, @janicebeinrot, @mortenfc, @Andersama, @byjtew, @lprc, @0xB10C, @samuelpmish, @Segfault1602, @N9199, @pkp124 and everyone who filed an issue and waited patiently.
Full changelog: v4.3.11...v4.4.0