What is nanobench and what is it used for?

Updated

nanobench is a C++ microbenchmarking library for measuring small code snippets. It is header-only and can be used by direct inclusion or through CMake integration. It is intended for benchmarking code that is fast, slow, or unstable, and it produces results as tables, CSV, JSON, HTML box plots, or pyperf output. Use it when you need a repeatable measurement of a small piece of C++ code and want the result in a form you can read, compare, or feed into another tool.

What problem it solves

Measuring small code snippets with a hand-rolled timer is easy to get wrong: the measured time can be dominated by setup, the compiler can optimize the work away, and a single run says little about variation. nanobench is designed around the experiment rather than just the timer. Its documentation covers design goals, how a round is reduced to one number, estimating and reporting, what was rejected, and serial correlation and why warmup is not the answer. In practice, that means you define the benchmark, let the library run it, and then read a result that includes more than one number.

How you use it

A benchmark is built around ankerl::nanobench::Bench. The main entry point is Bench(), and the workflow is:

  1. Create a Bench object.
  2. Configure it as needed, for example with title(), name(), context(), batch(), unit(), timeUnit(), warmup(), epochs(), minEpochTime(), maxEpochTime(), minEpochIterations(), or epochIterations().
  3. Call run() with the code to measure. doNotOptimizeAway() is available to keep the compiler from removing the work.
  4. Read the results through results(), or render them with render().

For comparisons, compare() and CompareResult provide size(), operator[](), rounds(), comparisons(), isSignificant(), and fastest(), so you can check whether a difference is significant rather than just looking at two numbers.

Output formats

The library documents several output paths, each suited to a different use:

Output How it is produced Typical use
Table Default console output with configurable columns Reading results directly
CSV templates::csv / csv() Spreadsheets and further analysis
JSON templates::json / json() Passing results to other tools
HTML box plot templates::htmlBoxplot / htmlBoxplot() Seeing the distribution of runs
pyperf templates::pyperf / pyperf() The Python pyperf module

Columns can be chosen with showColumn(), hideColumn(), and isColumnVisible(), and context can be shown as columns with contextColumn() and clearContextColumns(). Available columns include relative, complexityN, timePerUnit, unitPerSecond, error, instructions, cycles, ipc, branches, branchMisses, and total.

Reading the output

The documentation has a dedicated section on reading the output and on how precise a comparison is. It also covers asymptotic complexity through complexityN(), complexityBigO(), and the complexityBigO() column, which is useful when you want to see how cost grows with input size rather than only how fast one case runs. For unstable code, the histogram and box plot outputs show the spread that a single average would hide.

Practical notes

  • NANOBENCH_ENDLESS runs a specific test endlessly.
  • NANOBENCH_SUPPRESS_WARNINGS turns off stability warnings.
  • NANOBENCH_CONFIG changes settings without recompiling.
  • The documentation includes comparisons against Google Benchmark, nonius, Picobench, Catch2, moodycamel::microbench, sltbench, Celero, and folly Benchmark, with source code and results for each, which is useful if you are choosing between libraries.

If you only need a quick timing of one function, a simple timer may be enough. If you need to compare alternatives, check whether a difference is significant, or present results as a table, CSV, JSON, or box plot, nanobench is built for that job.