Website profiles · Technology insights · Alternatives

nanobench.ankerl.com

No paid content found
Visit website
Profile views 1 Outbound visits 1 Normal
nanobench Full homepage screenshot

At a glance

Category
Development
Language
English
Paid-content signal
No paid content foundObserved
Registered
2006
Hosting
FastlyInferred
HTTPS certificate trusted
PassedObserved
Last updated

Website review

This review may include AI-generated content and is not individually verified. Domain, certificate and other facts come from the observed data on this page.

What is nanobench?

nanobench is a single-header C++ microbenchmarking library. You include it in your project, write a small harness around the code you want to measure, and it runs that code repeatedly while timing each round and reporting statistics such as time per unit, error, and optionally CPU counters like instructions, cycles, and branch misses.

What it is for

It is aimed at C++ developers who want quick, repeatable measurements of small code paths — a function, an algorithm, a container operation — without pulling in a large framework. The page evidence shows the intended workflow: install by direct inclusion or CMake integration, then use ankerl::nanobench::Bench with run(), title(), name(), and unit() to describe each benchmark, and output() or render() to format results.

Things it does beyond plain timing

  • Comparison mode: compare() and CompareResult let you check whether one implementation is meaningfully faster than another rather than eyeballing two numbers.
  • Stability handling: the API exposes warmup(), epochs(), minEpochTime(), and related knobs, and the documentation discusses unstable results and serial correlation — a sign the library expects you to think about measurement noise, not just call a timer.
  • Complexity annotations: complexityN() and complexityBigO() support fitting results to a growth curve.
  • Output formats: table columns, CSV, JSON, HTML box plots, and pyperf-compatible output, plus Mustache-like templates for custom rendering.
  • A bundled fast PRNG (Rng) for benchmarks that need random input.

Who should reach for it

A concrete case: you are choosing between two implementations of a hot function and want a defensible answer. You write one Bench with two run() calls under the same title, use compare(), and let the reported error tell you whether the difference is real. If you only need a rough one-off number, a stopwatch-style loop is less setup; nanobench pays off when you need statistics, comparisons, or machine-readable output.

As a next step, read the "Designing the experiment" and "Reducing a round to one number" sections before trusting any result — the library gives you the tools, but the measurement discipline is yours. The project lives at nanobench.

How do I integrate nanobench into my project with CMake or direct inclusion?

nanobench is a single-header C++ microbenchmarking library, so you can either drop the header directly into your project or wire it in through CMake. Both paths use the same #include and the same ankerl::nanobench::Bench API; the difference is only in how the header reaches your compiler.

Direct inclusion

Copy nanobench.h (and its companion nanobench.cpp, if your version ships one) into your source tree, then include it by relative path:

#include "nanobench.h"

int main() {
    ankerl::nanobench::Bench b;
    b.title("my benchmark").unit("op");
    b.run("work", [] { /* code under test */ });
    b.render(ankerl::nanobench::templates::markdown());
}

This is the quickest route for a single experiment or a throwaway harness. The trade-off is that the header lives in your repo and you update it by hand.

CMake integration

Add the nanobench directory as a subdirectory and link the target the library exports (the documentation lists CMake Integration as a first-class option):

add_subdirectory(third_party/nanobench)
target_link_libraries(my_bench PRIVATE nanobench)

Then #include <nanobench.h> in your code. Linking the target keeps include paths and any compile definitions consistent across your targets, which matters once more than one executable or test binary uses the library.

Which to choose

  • One benchmark file, one machine: direct inclusion is less setup.
  • Multiple targets, CI, or shared team repo: use the CMake target so version and flags stay in one place.
  • Vendored dependency policy: if you already vendor headers, direct inclusion fits; if you pin dependencies via CMake, prefer the subdirectory route.

A practical next step

Start with direct inclusion to confirm the API compiles and produces output, then convert to add_subdirectory + target_link_libraries when you commit the benchmark. Keep the benchmark in its own executable rather than mixing it into your production binary, so optimization flags and runtime cost stay separate. The official docs at nanobench cover the full Bench reference and the output templates (csv, json, htmlBoxplot, pyperf) you will likely want once the harness runs.

How do I interpret nanobench's output columns and comparison results?

nanobench's output is a table of measurement columns per benchmark, and its comparison results tell you whether the difference between two variants is statistically meaningful. Read the columns first to understand what was measured, then read the comparison to decide whether the difference matters.

Reading the output columns

Each row is one run() (or one entry in a compare()). The core columns:

  • relative — the row's time relative to the fastest row in the same run, so the quickest benchmark is 1.00 and slower ones are multiples of it. This is the fastest way to see the shape of your results.
  • timePerUnit — elapsed time divided by batch() × unit(). If you didn't set a unit, treat it as time per iteration.
  • unitPerSecond — the reciprocal, useful when you think in throughput (items/sec, ops/sec).
  • error — nanobench's estimated uncertainty for that row. A large error means the number is noisy; don't draw conclusions from it until you understand why.
  • instructions, cycles, ipc, branches, branchMisses — hardware counters, only present when you enable performanceCounters(). ipc and branchMisses are the most diagnostic: low IPC suggests stalls (cache misses, dependencies); high branch misses suggest unpredictable control flow.
  • total — total time for the whole run, including untimed setup excluded via setup().

Columns are configurable: hideColumn()/showColumn()/isColumnVisible() control which appear, and contextColumn() turns a context() value into its own column — handy when you sweep a parameter (e.g. input size) and want one table with a column per value.

Reading comparison results

compare() returns a CompareResult with size(), operator[], rounds(), comparisons(), isSignificant() and fastest(). The practical reading:

  1. Check isSignificant() before believing any ordering. If the difference isn't significant, treat the two variants as tied and choose on other grounds (readability, maintainability).
  2. Use fastest() to get the winning entry, but only after step 1.
  3. Look at rounds()/comparisons() to see how much evidence backed the verdict — more rounds with a stable result is stronger.

A concrete scenario

You refactor a hot loop and want to know if it's actually faster. Run both versions with compare(), set epochs() high enough that error is small relative to the effect you expect, and enable performanceCounters() if available. If isSignificant() is false and error is comparable to the gap, the refactor is a wash performance-wise — keep it only if it's clearer. If it's significant, check ipc and branchMisses to see why: a jump in IPC with fewer branch misses confirms the loop got more predictable, which is a more durable win than a lucky measurement.

Decision criteria

  • Noisy error? Increase epochs() or minEpochTime(); check for unstable inputs before trusting anything.
  • Tiny but significant difference? Ask whether it's above your noise floor in real use; sub-percent wins rarely survive integration.
  • Comparing across machines or compilers? Re-run the comparison on each; relative is only meaningful within one run.

For a broader comparison against other microbenchmark harnesses (Google Benchmark, nonius, Catch2's benchmark support), see GitHub repositories for each project's own results.

When should I use nanobench instead of Google Benchmark or Catch2?

Use nanobench when you want a single-header C++ microbenchmarking library that is quick to drop into a project and produces human-readable tables plus exportable results (CSV, JSON, HTML box plots). Its documentation describes a Mustache-like templating system, per-benchmark context columns, comparison of alternatives, and complexity estimation. That makes it a good fit for developers who want low-friction benchmarking inside an existing build, especially for small, focused experiments and quick comparisons.

Where each tool tends to fit

Situation nanobench Google Benchmark Catch2
Adding benchmarks with minimal setup Direct inclusion or CMake; single header emphasized in its docs Typically a library dependency with its own build integration Primarily a test framework; benchmarking is an add-on via its benchmark support
Comparing several implementations side by side Built-in comparison support and comparison output Supports comparisons, often via manual setup or fixtures Less central; you usually structure it yourself
Reporting and export CSV, JSON, HTML box plots, pyperf output JSON and console output; rich ecosystem Test-style output; not benchmark-report oriented
Estimating algorithmic complexity Complexity helpers (complexityN, complexityBigO) Possible but not the primary focus Not a typical use
Using one framework for tests and benchmarks Benchmarks only Benchmarks only Tests and simple benchmarks together

Concrete scenario

Suppose you are choosing between two string-splitting routines in a C++ service. With nanobench you can write one Bench object, run both functions with run(), and use compare() to see which is faster while the table shows time per unit and error. If you also want to see how cost scales with input size, the complexity helpers give you a Big-O estimate. If your team already runs Google Benchmark in CI and consumes its JSON, staying there avoids splitting your measurement tooling. If you mainly need assertions and a rough timing check, Catch2 keeps tests and benchmarks in one place.

Decision criterion

Pick nanobench for lightweight, self-contained microbenchmarks where readable tables and flexible export matter. Pick Google Benchmark when you need a mature benchmarking ecosystem and established CI reporting. Pick Catch2 when benchmarking is secondary to unit testing and you value having both in one framework. A useful next step is to write the same small benchmark in two candidates and compare the output format and setup effort against your team's reporting needs.

See nanobench, Google Benchmark and Catch2 for their official documentation.

How do I export nanobench results to CSV, JSON, or HTML box plots for reporting?

Use nanobench's built-in render() templates to export results: nanobench::templates::csv, nanobench::templates::json, and nanobench::templates::htmlBoxplot turn a Bench object's results into text you can write to a file. CSV and JSON are best for archiving or feeding dashboards; the HTML box plot is best for a report where a reader needs to see spread, not just a single number.

Typical flow

  1. Declare a Bench, run one or more cases with run(), and keep the object alive.
  2. Choose a template and call render() against the results, writing the returned text to a .csv, .json, or .html file.
  3. For comparisons, use compare() so the output carries the comparison outcome (isSignificant(), fastest()) rather than a flat list of timings.

Choosing the format

Format Best for Trade-off
CSV Spreadsheet review, long-term archiving, custom plots Loses the nested structure of comparison and context data
JSON Scripts, CI artifacts, dashboards, downstream analysis Needs a parser; not readable at a glance
HTML box plot Human-facing reports and review meetings Static file; not a substitute for raw data

Practical example

A common setup: a CI job runs a benchmark, writes JSON as the artifact, and a small script extracts timePerUnit and error per case. For a pull-request summary, render the same results as an HTML box plot and attach it. CSV is the pragmatic middle ground when you want something a colleague can open without tooling.

Decision criteria

  • Audience is a person skimming a report? HTML box plot.
  • Audience is a script or dashboard? JSON.
  • Audience is a spreadsheet user or a one-off comparison? CSV.

Two things shape how useful the export is. First, stability: unstable timings produce wide box plots and noisy error columns, so tune epochs and interval settings before exporting. Second, context: use context() and contextColumn() to attach parameters such as input size or compiler flags, otherwise the exported rows lose the information that makes them interpretable later.

How do I configure nanobench's epochs, warmup, and timing parameters for stable benchmarks?

Configure nanobench's timing controls through the ankerl::nanobench::Bench object before you call run(): use warmup(), epochs(), minEpochTime(), maxEpochTime(), minEpochIterations(), epochIterations(), clockResolutionMultiple() and targetIntervalWidth() to shape how each benchmark round is measured. The library's own documentation frames this as "designing the experiment" — the parameters exist so you can trade measurement time against stability, rather than there being one correct preset.

What each knob does in practice

Setting Practical effect When to adjust it
warmup() Runs the benchmark unmeasured before timing starts Code with caches, lazy initialization, or first-call overhead
epochs() Number of measured rounds; more rounds give a tighter estimate Noisy results, or when you need a confidence-worthy comparison
minEpochTime() / maxEpochTime() Bounds how long a single epoch runs Very fast code needs a floor; slow code needs a ceiling
minEpochIterations() / epochIterations() Guarantees a minimum number of inner iterations per epoch Sub-nanosecond operations where the clock resolution dominates
clockResolutionMultiple() Ensures each epoch spans a multiple of the clock's resolution Tiny benchmarks where timer granularity causes jitter
targetIntervalWidth() Target spread of measurements within a round Tightening stability at the cost of runtime

A concrete starting scenario

Suppose you are benchmarking a hash function that takes a few nanoseconds per call. Left at defaults, individual epochs may be too short for the clock to resolve, producing wildly varying numbers. A sensible adjustment is to raise minEpochIterations() so each epoch does enough work to be measurable, set minEpochTime() to something like a few milliseconds, and increase epochs() so the reported statistics have enough samples. Add warmup() if the function touches memory that benefits from cache priming.

Decision criteria

  • Results look unstable (wide error, warning about stability): first increase epochs(), then minEpochTime(). The documentation notes that warmup alone is not the answer to serial correlation between measurements.
  • Benchmark runs too long: lower epochs() or maxEpochTime(), accepting a wider interval.
  • Comparing two implementations: use compare() and check isSignificant() on the CompareResult rather than eyeballing means; significance depends on having enough epochs.
  • Very fast operations: prefer iteration-count controls over time-based ones, since time-based bounds may be shorter than the clock can resolve.

Useful next step

Run the same benchmark twice with different epochs() values and compare the reported error and the significance verdict. If the verdict flips, your settings are too loose for the effect size you are trying to detect. For deeper guidance on experiment design, the project's documentation at nanobench covers the reasoning behind each parameter, and Google Benchmark offers a contrasting API if you want to cross-check methodology.

Related questions

More questions →
How nanobench Measures and Reports Benchmark Results

nanobench measures a benchmark by repeatedly running a timed loop (an epoch) until it has a stable sample, reduces each round to a single number, and reports that number with an error estimate rather than a bare average. It is designed to make microbenchmark results trustworthy enough to compare alternatives, and it reports the statistics, histograms, and significance tests needed to judge that trust. The approach fits C++ microbenchmarks where you want defensible numbers without building your own statistics layer; it is not a profiler and does not attribute time to source lines.

Design goals: one number per round, with an estimate

The documentation frames the core problem directly: a benchmark produces many timings, and the reader needs one number plus a sense of how much to trust it. nanobench's stated design goals center on reducing a round to one number and estimating and reporting the uncertainty around it. Everything else in the output — columns, error, comparisons — serves that goal.

This matters because a raw mean hides instability. If a benchmark's timings swing widely, the mean looks precise while being meaningless. nanobench instead reports the central value alongside an error figure so an unstable result is visibly unstable.

How a measurement round works

The ankerl::nanobench::Bench object is the main entry point. You configure it, then call run() with a name and a callable. The relevant knobs for experiment design are:

  • warmup() — number of warmup iterations before measurement.
  • epochs() — how many measurement epochs to collect.
  • minEpochTime() / maxEpochTime() — bounds on how long a single epoch runs.
  • minEpochIterations() / epochIterations() — bounds on iterations per epoch.
  • clockResolutionMultiple() — ensures each epoch spans enough clock ticks to be measurable.
  • batch() — number of operations per iteration, used to compute per-operation cost.
  • targetIntervalWidth() / maxEpochs() — control how tightly the collected samples must cluster before stopping.

The practical effect: nanobench keeps sampling until the spread of epochs is narrow enough (or a limit is hit), then reports the result. You are not choosing a fixed iteration count and hoping; the library adapts to how long the code actually takes.

Warmup and serial correlation

The documentation is explicit that warmup is not the answer to serial correlation. Warmup helps with one-time costs (caches, lazy initialization), but if consecutive measurements are correlated — each influenced by the previous — warmup does not remove that dependence. The docs discuss serial correlation as a distinct concern and explain what was rejected in designing around it. The takeaway for a reader: treat warmup as handling startup effects, not as a fix for a benchmark whose successive runs drift.

What the output contains

Results are exposed through Result objects and rendered through templates. The column set is configurable: showColumn(), hideColumn(), isColumnVisible(), and contextColumn() let you choose what appears. Documented columns include:

Column Meaning
relative Time relative to a baseline
timePerUnit Time per operation (respects batch() and unit())
unitPerSecond Throughput
error Estimated error of the measurement
instructions, cycles, ipc Performance-counter data when enabled
branches, branchMisses Branch statistics when enabled
total Total elapsed time
_size Size context, if set

Performance counters require performanceCounters() to be enabled and depend on platform support.

Statistics and histograms

Show Benchmark Statistics and Show a Histogram are documented output modes. The statistics view gives the summary numbers; the histogram shows the distribution of samples, which is where instability becomes visible. If the histogram is wide or multi-modal, the single reported number deserves skepticism — that is the point of showing it.

Rendering formats

Output is produced by Mustache-like templates via render(). Built-in templates include:

  • templates::csv — comma-separated values, including a comparison variant
  • templates::json — machine-readable output
  • templates::htmlBoxplot — box plots for visual comparison
  • templates::pyperf — Python pyperf-compatible output

You can also supply your own template. Time units inside templates are configurable through timeUnit().

Comparing alternatives and judging significance

Bench::compare() runs a comparison between implementations and returns a CompareResult. The API exposes size(), operator[], rounds(), comparisons(), isSignificant(), and fastest(). The isSignificant() flag is the key one: it tells you whether the observed difference is distinguishable from noise given the collected samples. A comparison that is not significant should not be reported as "faster."

The documentation also asks "How precise is a comparison?" — a reminder that even a significant result has a resolution limit. Two implementations within the error bars are, for practical purposes, the same.

Estimating asymptotic complexity

nanobench can estimate complexity growth using complexityN() and complexityBigO(). You run the benchmark across a range of input sizes (set via the size context), and the library fits the growth. The complexityN and relative complexity columns report the fit. This is an estimate from measured points, not a proof — it tells you whether your data looks like O(n) or O(n log n), and a poor fit signals that the model does not describe the behavior.

A concrete example of the workflow

A task where this matters: deciding whether a custom hash map beats std::unordered_map for your workload. You would:

  1. Create a Bench, set warmup(), epochs(), and a timeUnit().
  2. Call run() for each implementation with the same input sizes, using context() to record the size.
  3. Call compare() to get a CompareResult.
  4. Check isSignificant() before claiming a winner, and inspect the histogram if the result looks unstable.

If the comparison is not significant, the honest conclusion is that they perform the same at that size — which is itself a useful decision input.

Environment controls

Three environment variables affect runs without recompiling:

  • NANOBENCH_ENDLESS — run a specific test endlessly (useful for profiling).
  • NANOBENCH_SUPPRESS_WARNINGS — suppress stability warnings.
  • NANOBENCH_CONFIG — change settings without recompiling.

The stability warnings are worth heeding: they fire when the collected samples are too spread out to trust, which is exactly the condition under which a reported number should not be used for a decision.

What Output Formats Does nanobench Support?

nanobench supports four built-in output formats—CSV, JSON, HTML box plots, and Python's pyperf format—plus custom rendering through Mustache-like templates. You get these by calling render() on a benchmark result or comparison result and passing a template from the ankerl::nanobench::templates namespace. The default console output is a table whose columns you can control; the template formats are for feeding results into other tools or reports.

Default table output and column control

When you run a benchmark without rendering a template, nanobench prints a table. The documentation lists these columns: relative, complexityN, timePerUnit, unitPerSecond, error, instructions, cycles, ipc, branches, branchMisses, total, and _size.

You can shape this table with hideColumn(), showColumn(), and isColumnVisible(). Context values can also be promoted into their own columns via contextColumn() and clearContextColumns(), which is useful when you sweep a parameter and want each value visible per row.

Built-in templates

All built-in templates live in ankerl::nanobench::templates and are passed to render():

Template Function Typical use
CSV templates::csv / csv() Spreadsheet import, diffing runs
JSON templates::json / json() Storing results, downstream tooling
HTML box plot templates::htmlBoxplot / htmlBoxplot() Visualizing run-to-run spread
pyperf templates::pyperf / pyperf() Consuming results with Python's pyperf module

The documentation also covers CSV specifically for comparisons ("CSV for a comparison") and a section on how precise a comparison is, so if you plan to compare alternatives, read those before trusting small differences.

Rendering a result

The pattern is: run the benchmark, keep the Result, then render it with a template. Conceptually:

ankerl::nanobench::Bench b;
b.title("example").unit("op");
b.run("something fast", [&] { /* work */ });

// Render the most recent result
ankerl::nanobench::render(ankerl::nanobench::templates::json(), b.results());

Bench::results() returns the collected Result objects, and render() accepts a template plus results. For comparisons, CompareResult has its own render() overload, so you render the comparison outcome rather than individual results.

Custom Mustache-like templates

Beyond the built-ins, render() accepts Mustache-like templates, so you can produce any text format—Markdown tables, custom logs, config fragments—by writing a template with placeholders. The documentation's "Rendering" and "Mustache-like Templates" sections describe the syntax, and "Time Units in Templates" explains how time values are formatted inside templates. If you need a unit other than the default in your output, set it with timeUnit() on the bench or handle it in the template.

Practical notes

  • Comparisons need the comparison path. Use compare() and render the resulting CompareResult; isSignificant() and fastest() tell you whether a difference is meaningful before you publish it.
  • CSV and JSON are the safe interchange formats. If another tool will read the output, prefer these over scraping the console table.
  • Box plots need multiple rounds. The HTML box plot template is only informative when the benchmark ran enough epochs to show spread; check epochs() and related settings if the plot looks degenerate.
  • pyperf output targets Python tooling. Use it when your analysis pipeline already uses the pyperf module; otherwise JSON is more general.

For exact template names, placeholder syntax, and the comparison-specific CSV behavior, consult the corresponding sections of the nanobench documentation, since the API reference lists each template function individually.

What is nanobench and what is it used for?

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.

Getting Started with nanobench and Reading Its Output

To start with nanobench, add its single header to your project (direct inclusion or CMake integration), include nanobench.h, create an ankerl::nanobench::Bench, and call run() with a name and a lambda containing the code you want to measure. The default output is a table whose key columns are timePerUnit (time per iteration), unitPerSecond (throughput), error (relative measurement error), and optional hardware counters such as instructions, cycles, ipc, branches, and branchMisses. You can hide, show, or add columns, and attach context values that appear as extra columns.

Installation

nanobench is distributed as a header-only library, so there is no compiled library to link.

Direct inclusion

Copy the nanobench header into your project and include it:

#include "nanobench.h"

CMake integration

If you use CMake, add nanobench as a dependency and link the nanobench target to your benchmark executable. The documentation lists CMake Integration as the supported path; follow the target name it provides rather than inventing include paths.

Basic usage

The main entry point is ankerl::nanobench::Bench. A minimal benchmark looks like this:

#include "nanobench.h"

int main() {
    ankerl::nanobench::Bench b;
    b.title("my benchmarks")
     .unit("op")
     .warmup(100)
     .minEpochIterations(1000);

    b.run("some work", [] {
        // code under test
    });
}

What each part does:

  • title() sets the table heading.
  • unit() names what one iteration produces (for example op or byte), which affects how unitPerSecond is labeled.
  • warmup() runs the code before measuring so caches and branch predictors are primed.
  • minEpochIterations() sets a floor on how many iterations each measurement epoch performs.
  • run(name, lambda) executes the lambda repeatedly, measures it, and appends a row to the results.

run() can be called multiple times on the same Bench to build a table of several benchmarks.

Reading the output

The default table reports one row per run() call. The columns you will use most:

Column Meaning
relative Time relative to the fastest benchmark in the table
timePerUnit Measured time divided by iterations, in the chosen time unit
unitPerSecond Throughput: iterations (or units) completed per second
error Estimated relative error of the measurement
instructions Retired instructions per iteration (if performance counters are enabled)
cycles CPU cycles per iteration
ipc Instructions per cycle
branches Branch instructions per iteration
branchMisses Mispredicted branches per iteration

Two practical points when interpreting these:

  • error is a stability signal, not a correctness guarantee. A large error means the measurement varied a lot between epochs; treat the row as unreliable and investigate (see the documentation's sections on unstable benchmarks and serial correlation).
  • Hardware counters are optional. instructions, cycles, ipc, branches, and branchMisses depend on performanceCounters() being enabled and on platform support; do not assume they are populated.

Choosing, hiding, and showing columns

Use showColumn() and hideColumn() to control which columns appear, and isColumnVisible() to query the current state. This is useful when you only care about timing, or when you want counters without the default set.

Adding context as columns

context() attaches a named value to the current benchmark, and contextColumn() promotes a context key into its own table column. clearContext() and clearContextColumns() reset them. This lets you compare variants side by side, for example a context key recording which implementation or input size a row used.

Comparing alternatives

To compare implementations rather than just list them, use compare() on the Bench. It returns a CompareResult whose isSignificant() tells you whether the difference between the fastest and the others is statistically meaningful, and whose fastest() identifies the winner. Use this instead of eyeballing timePerUnit when the numbers are close.

Practical tips

  • Start with a small minEpochIterations() and let nanobench's adaptive epoch logic settle; the library adjusts epoch length to hit a target measurement interval.
  • Keep the lambda free of setup work you do not intend to measure, or move setup outside run().
  • If a benchmark looks unstable, check the documentation's guidance on warmup and serial correlation before increasing iteration counts blindly.
  • For machine-readable results, nanobench can render CSV, JSON, HTML box plots, and pyperf output through its render() templates, so you can feed results into other tooling instead of parsing the console table.
How does nanobench compare with Google Benchmark, Catch2, and other C++ benchmark libraries?

nanobench is a single-header C++ benchmarking library that integrates by direct inclusion or CMake, runs each benchmark through a warmup and multiple timed epochs, and reports results as a table, CSV, JSON, HTML box plot, or pyperf data. It is a good fit when you want a low-friction, header-only tool with built-in comparison of alternatives and significance testing. Google Benchmark, Catch2, nonius, Picobench, Celero, and others differ mainly in integration cost, output formats, and how they treat measurement noise. The nanobench documentation publishes side-by-side source code and results for these libraries, so the comparison below follows the dimensions that documentation exposes.

Integration footprint

Library Integration model (per nanobench docs) Practical consequence
nanobench Header-only; direct inclusion or CMake Drop one header in, no separate build target
Google Benchmark Separate library with its own source and results shown Build and link a dependency
Catch2 Benchmarking inside a test framework; source and results shown Benchmark code lives alongside tests
nonius Separate library; source and results shown Build and link a dependency
Picobench Separate library; source and results shown Build and link a dependency
Celero Separate library; source and results shown Build and link a dependency
moodycamel::microbench, sltbench, folly Benchmark Each with source and results shown Each carries its own setup

The header-only design is the clearest differentiator: nanobench adds no link step and no external build target. If your constraint is "benchmark this function without restructuring the build," that matters more than any single performance number.

How results are produced and reported

nanobench's model, as documented, is: run a warmup, then run multiple epochs, reduce each round to one number, and report statistics. It warns when results are unstable and lets you suppress those warnings with NANOBENCH_SUPPRESS_WARNINGS. Output is column-based: you choose which columns appear (showColumn, hideColumn, contextColumn), and the same run can be rendered as a table, CSV, JSON, HTML box plot, or pyperf module output via render() and the templates:: helpers (templates::csv, templates::json, templates::htmlBoxplot, templates::pyperf).

The documentation is explicit that warmup is not a fix for serial correlation, and it discusses what was rejected in designing the experiment. That is a useful signal: the library treats measurement methodology as part of its job, not just timing loops.

Comparison and significance

nanobench has a first-class comparison path: Bench::compare() produces a CompareResult with size(), operator[], rounds(), comparisons(), isSignificant(), and fastest(). In other words, "is A faster than B, and is the difference significant?" is answered by the library rather than by eyeballing two tables. The docs also ask "How precise is a comparison?" — a reminder that a comparison result is only as trustworthy as the run's stability. If you mainly need to rank alternatives, this is the feature to evaluate first.

Choosing by measurement need

  • Minimal integration, quick comparisons: nanobench, because of header-only inclusion and built-in compare() with significance.
  • You already build against Google Benchmark: stay there if its existing harness and reporting fit your workflow; the nanobench docs show its source and results so you can check whether the numbers you care about differ.
  • Benchmarks belong with your test suite: Catch2's benchmarking support keeps them in the same framework, at the cost of coupling benchmarks to tests.
  • You need a specific output format: check nanobench's templates (CSV, JSON, HTML box plot, pyperf) against what your tooling consumes before switching; format support is often the deciding factor, not raw timing.

No library is universally better. The documentation's own framing — comparing source code and results across implementations — supports choosing on integration cost, comparison semantics, and output format for your specific task, then verifying with a benchmark of your own workload rather than trusting a cross-library ranking.

Practical check before committing

Write one benchmark of a function you actually care about in nanobench (include the header, define a Bench, call run()), render it as CSV and as an HTML box plot, and run a compare() against a known-slower variant. If the significance result and the output format both fit your workflow, the integration cost is already settled. If you need a feature the docs do not list, that absence is the answer — the reference covers the API surface, and unlisted capabilities should not be assumed.

Website Overview

An established domain and managed infrastructure suggest continuity of operations and may support dependable delivery, although neither guarantees service quality. Several search or sharing settings need attention. Together they may make snippets, preview images or preferred URLs less consistent across platforms.

Domain and Registration

Registered in 2006, this domain has about 19 years of history. That suggests continuity, although ownership and purpose may have changed. Transfer-protection status is present, helping reduce the risk of unauthorized domain transfers. The registrar is NameCheap, Inc., a widely used domain service provider. The domain uses the common .com extension, which is not an independent safety signal.

DNS and Email

Nameservers are provided by Cloudflare, indicating managed DNS hosting. No CNAME was found; the observed records resolve directly to addresses. No MX record was found. A conventional explicit inbound-mail route is not configured. TXT records include verification markers for Google. Such markers may also remain after a service stops being used. DNSSEC signatures were not detected, so this additional DNS authenticity protection is not confirmed.

TLS and Certificates

The public key uses EC with 256 bits. The server supplied a complete certificate chain. No organization name is present in the certificate; the available fields are consistent with domain validation. The certificate was issued within the Google Trust Services cloud or CDN ecosystem. The certificate's total validity is about 90 days, consistent with a short renewal cycle.

HTTP and Browser Security

The response lacks these common security headers: CSP, X-Content-Type-Options, Referrer-Policy, Permissions-Policy, clickjacking protection. CORS permits any origin to read this response. This is common for public resources; sensitive responses need narrower handling. No X-Powered-By header was found, reducing one common source of backend fingerprinting information. The cf-ray, x-cache, x-served-by, via response header indicates a CDN or caching proxy in the delivery path. No obvious internal addresses or debug information were found in the headers.

Technology Stack Analysis

The public page identifies Cloudflare, Fastly without precise versions, leaving fewer clues for version-specific scanning.

Search and Social Sharing

No homepage meta description was detected, leaving snippet selection more dependent on page text. No homepage canonical URL was detected. If duplicate URLs exist, consolidation may be less explicit. No Open Graph metadata was detected, so social previews may depend on platform inference. The title has 36 characters, within a common display range. The observed directives allow indexing and link following.

Hosting and Email

DNSCloudflare
HostingFastly
EmailUnknown
Approximate IP geolocation (inferred; a CDN or proxy may differ from the origin server) Location unknown Inferred 104.21.8.244

Registration details

RDAP / WHOIS

RegistrarNameCheap, Inc.
Registered2006-11-09
Expires2027-11-09
Domain statusclient transfer prohibited
Nameserversdan.ns.cloudflare.com、rita.ns.cloudflare.com
DNSSECunsigned

DNS records

DNS

TypeNameValueTTLPriority
Ananobench.ankerl.com104.21.8.244300—
Ananobench.ankerl.com172.67.130.198300—
AAAAnanobench.ankerl.com2606:4700:3030::ac43:82c6300—
AAAAnanobench.ankerl.com2606:4700:3031::6815:8f4300—
NSankerl.comdan.ns.cloudflare.com86400—
NSankerl.comrita.ns.cloudflare.com86400—
TXTankerl.comgoogle-site-verification=fanP0_gbfKxHbEib0H-EkgwGkH1KGEDZlsudKEXoKP83600—

TLS and certificates

TLS / SSL

AssessmentNormal configuration
Supported protocolsTLSv1.2、TLSv1.3
Negotiated protocolTLSv1.3
Certificate subjectankerl.com
IssuerGoogle Trust Services
Valid until2026-12-06T17:02 · Remaining when checked: 42 days
Verification detailsCertificate trust: Passed · Hostname match: Passed

HTTP response headers

HTTP

HeaderValue
content-typetext/html; charset=utf-8
cache-controlmax-age=86400
servercloudflare
strict-transport-securitymax-age=0; includeSubDomains; preload
access-control-allow-origin*

Identified technologies

CloudflareFastly

Pages, Search and Sharing

Meta descriptionNot detected
Canonical URLNot detected
LanguageEnglish (default)
Twitter CardNot detected

Unknown

No rules found

No sitemaps found

User reviews (0)

  • No reviews yet.