Library / SDK
martinus/nanobench avatar
martinus/nanobench

ankerl::nanobench for fast C++ microbenchmarking

Simple, fast, accurate single-header microbenchmarking functionality for C++11/14/17/20

1,747 stars105 forksC++MIT

At a glance

What is it?
ankerl::nanobench is a single-header C++ microbenchmarking library for C++11, C++14, C++17 and C++20. Its main appeal is a compact API that produces timing, error and hardware-counter results in a short benchmark run.
Who is it for?
Adopt ankerl::nanobench when a C++ project needs a compact, single-header benchmark harness and can keep each benchmark focused on a small operation. Do not adopt it as a replacement for whole-application load testing or assume that its concise output covers every statistical and hardware concern.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 46 days ago.
What is it written in?
Mainly C++, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What ankerl::nanobench measures in C++ code

ankerl::nanobench addresses small, repeated C++ operations where developers need timing data without building a large benchmarking executable. It is intended for code that performs measurable work many times, such as arithmetic, container operations, parsing steps or another narrowly defined function. The library is not presented as a tool for simulating complete applications or measuring production traffic under realistic user loads.

The project is platform independent and supports C++11, C++14, C++17 and C++20. Its distribution model is unusually small: the README describes the functionality as a single-header library. That makes it suitable for adding a temporary benchmark inside an existing C++ source tree without introducing a package dependency or a separate library-linking step.

Each benchmark receives a name and a callable. The callable is invoked repeatedly, while the library records elapsed time and derived performance figures. The documented output includes `ns/op`, `op/s`, `err%`, `ins/op`, `cyc/op`, `IPC`, `bra/op`, `miss%`, total runtime and the benchmark name. These fields help distinguish a quick operation from one that merely appears fast because the benchmark did substantial work per invocation.

The README's sample reports 7.52 nanoseconds per operation, approximately 132,948,239.79 operations per second and 1.1% measurement fluctuation. It also reports 6.65 instructions per operation and 24.07 CPU cycles per operation. Those numbers belong to the sample named `some double ops`. They are example output, not a general performance guarantee for nanobench or for other C++ programs.

The Bench().run() execution and reporting path

The API is centered on `ankerl::nanobench::Bench` and its `run()` method. A program creates a benchmark object, supplies a benchmark label and passes a lambda containing the operation under review. This keeps setup at the call site rather than requiring benchmark definitions in an external registration mechanism, based on the README example.

The callable also controls compiler optimization. The sample passes its working variable to `ankerl::nanobench::doNotOptimizeAway(d)`. Without an optimization barrier, a compiler could remove calculations whose results appear unused, making the measurement unrelated to the intended work. Nanobench therefore places that barrier inside the lambda after the arithmetic and conditional update.

The framework accumulates repeated samples before producing its report. The displayed `err%` value expresses measurement fluctuation, while the other columns convert elapsed time into per-operation values and, where available, present instruction, cycle and branch-related counters. The ratio between instructions and cycles appears as `IPC`.

Nanobench's stated design goals include quick execution, repeatable results and warnings when results are not reliable. The README says that its benchmark output is designed to support sound decisions and that outlier handling is part of the design. It does not expose the statistical settings used by the short README example, however. The project documentation is the appropriate place to check calibration, sample selection, counter collection and configuration behavior before depending on a particular reporting field.

Installing the header and running the first benchmark

The README does not provide a package-manager command, a release download command or a build command for adding nanobench to a project. It points to the project documentation for installation and identifies the library as a single-header component. Based on the repository layout, `src/` contains the project sources and documentation assets, but the README does not identify the exact distribution filename to copy, so a manual installation command would risk inventing an instruction.

The first executable follows this documented pattern. It defines `ANKERL_NANOBENCH_IMPLEMENT` before including `<nanobench.h>`, then creates `Bench().run()` with a named lambda. The implementation macro generates the library definitions in this translation unit, so the README example places it before the header include.

cpp
#define ANKERL_NANOBENCH_IMPLEMENT
#include <nanobench.h>

int main() {
    double d = 1.0;
    ankerl::nanobench::Bench().run("some double ops", [&] {
        d += 1.0 / d;
        if (d > 5.0) {
            d -= 5.0;
        }
        ankerl::nanobench::doNotOptimizeAway(d);
    });
}

Compile this file as part of the normal C++ build for the project. The README does not document a required compiler command, linker flag or C++ standard switch, so none can be confirmed here. When the example is run, the executable should print a Markdown table containing a row for `some double ops`. The documented example runs for roughly 60 milliseconds and reports values such as `ns/op`, `op/s`, `err%`, `ins/op` and `cyc/op`. Hardware-dependent values will not necessarily match the sample.

Compiler optimization barriers and representative workloads

A microbenchmark is only useful when the compiler preserves the code that developers intend to measure. The sample's `doNotOptimizeAway` call is therefore part of the measurement design, not incidental cleanup. It establishes a consumer for `d` and helps prevent the optimizer from discarding the preceding operations. A real benchmark should apply the same principle to every result whose computation must remain in the measured region.

The lambda also provides a natural scope for preparing state. A callable can retain local variables across iterations, which is useful for an operation with internal state. The sample instead updates `d` on every invocation and keeps it outside the lambda. That arrangement makes repeated execution explicit and matches the arithmetic described by the benchmark label.

There is a methodological boundary here. Nanobench can time and characterize the callable, but the project cannot decide whether the callable represents a meaningful production workload. An operation selected for convenience, such as one division and a conditional subtraction, may be dominated by CPU behavior unrelated to the larger application. The README does not document controls for cache warming, CPU affinity, frequency scaling, concurrent load or process-level setup, so this article cannot claim that the library applies those controls automatically.

The library's outlier handling and reliability warning are useful signals, but a short run is still not a substitute for testing representative inputs and build configurations. Developers should compare optimized and unoptimized builds where relevant, and should retain the compiler version and benchmark parameters in their own records if those details are not emitted by the executable.

nanobench versus a Google Benchmark runtime comparison

The README identifies Google Benchmark as the comparison point for nanobench's runtime. It states that nanobench is approximately 80 times faster than Google Benchmark in the linked comparison. That statement is a project claim documented by nanobench's comparison page. The README does not include benchmark hardware, compiler settings, workload details or a reproducible procedure for that number.

The practical difference presented here is integration weight and execution scope. Nanobench uses one header, one implementation macro and a direct `Bench().run()` call. Its sample can define and execute the complete benchmark within a normal C++ program. A separate benchmark framework comparison would require confirming its registration and build mechanisms, which the README does not describe and therefore this article does not assert.

Nanobench is consequently a reasonable candidate for developers who want measurements during development without building a separate benchmark suite. It is the wrong choice when the requirement is application-level load generation, concurrent request testing or production-shaped workload simulation. Its output describes repeated execution of a callable, not an entire service under realistic external load.

The claim of much lower benchmark runtime also deserves careful interpretation. Faster acquisition is useful when developers run benchmarks frequently, but it does not by itself establish that every result is more representative. Target hardware, optimization settings, data size and the benchmark body still determine what the measured numbers mean.

Maintenance, portability and MIT license boundaries

The repository is not archived. Its last push was on 2026-09-07, and the release list includes v4.6.0 on 2026-08-14, v4.5.0 on 2026-08-14 and v4.4.0 on 2026-08-13. These dates indicate recent repository and release activity. They do not establish compatibility with every compiler or platform supported by the project's broader test environment.

Nanobench is distributed under the MIT license. That permits use, modification, distribution and inclusion in larger works under the license's conditions, while preserving the license and copyright notice. This is a statement about the documented license, not legal advice about how a particular product should comply with it.

The single-header design reduces build-system integration effort, but it does not eliminate upgrade work. Updating the header can change results or output between releases, especially because the README reports timing, hardware counters and reliability figures. Projects should treat release notes and documentation changes as relevant when changing nanobench versions.

Portability also needs verification at the benchmark level. The README calls the library platform independent, but its sample output includes instruction, cycle, branch and cache-miss fields. The README does not document which counters are available on every operating system or processor. Those fields should not be treated as portable until their behavior has been checked on each target used for release decisions.

Editorial conclusion

Adopt ankerl::nanobench when a C++ project needs a compact, single-header benchmark harness and can keep each benchmark focused on a small operation. Do not adopt it as a replacement for whole-application load testing or assume that its concise output covers every statistical and hardware concern. Before integration, verify that the reported measurements and hardware-counter fields are available and meaningful on the target machine, because the README does not document cross-platform counter fallbacks.

Frequently asked questions

How do I install ankerl::nanobench?

The README describes nanobench as a single-header library and links to the online tutorial for installation, but it does not give a package-manager or manual copy command. The documented source pattern includes `<nanobench.h>` after defining `ANKERL_NANOBENCH_IMPLEMENT`.

How do I write the first ankerl::nanobench benchmark?

Create an `ankerl::nanobench::Bench`, call `run()` with a benchmark name and a lambda, and use `doNotOptimizeAway()` for values that must remain observable to the compiler. The README uses the label `some double ops` in its example.

What measurements does ankerl::nanobench report?

The documented output includes `ns/op`, `op/s`, `err%`, `ins/op`, `cyc/op`, `IPC`, `bra/op`, `miss%`, total runtime and the benchmark name. The README's sample reports 7.52 ns/op and 1.1% fluctuation for `some double ops`.

Is ankerl::nanobench actively maintained?

The repository is not archived, its last push was on 2026-09-07, and the listed recent releases were published in August 2026. Those facts describe recent repository and release activity, not a guarantee that every target platform remains equally supported.

Official sources

  1. License: MIT
  2. martinus/nanobench on GitHub
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/martinus-nanobench.svg)](https://hysenlabs.com/projects/martinus-nanobench)

Was this useful?