nanobench v4.4.0
Permanent link:
cppdashboard.dev/r/2026/08/nanobench-v4-4-0Simple, fast, accurate single-header microbenchmarking functionality for C++11/14/17/20
Release notes
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: * **`doNotOptimizeAway` was 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 when `warmup()` 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 `relative` column 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 different `batch()`. (#131) * **`medianAbsolutePercentError` returned NaN** for a measurement of 0, poisoning `err%`, the CSV/JSON output, and the strict weak ordering `std::sort` requires. * **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 way `perf stat` does it. * **`calcBestNumIters` could cast a non-finite double to `uint64_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 as `unsigned long`, musl as `int`, and `PERF_EVENT_IOC_ID` doesn't fit in an `int`. There's now an Alpine CI leg so it can't come back. (#92) * **`-fno-rtti`.** Installing the `std::numpunct` facet 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 without `std::locale`, so **output is identical with RTTI on or off**, and a `-fno-rtti` CI leg guards it. (#122) * **GCC 14** (`<cstdint>`), **Intel icx 2025** (`-Wfloat-equal` via `std::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 the `Column` enum — the full table is over 150 characters and wraps in most terminals. (#97) * **`Bench::contextColumn()`** — show a `context()` variable as a table column, for parameterised benchmarks. (#103) * **`elapsedms` / `elapsedus` / `elapsedns`** in render templates — `elapsed` is 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 ac…
Share this resource