nanobench.h raw

   1  //  __   _ _______ __   _  _____  ______  _______ __   _ _______ _     _
   2  //  | \  | |_____| | \  | |     | |_____] |______ | \  | |       |_____|
   3  //  |  \_| |     | |  \_| |_____| |_____] |______ |  \_| |_____  |     |
   4  //
   5  // Microbenchmark framework for C++11/14/17/20
   6  // https://github.com/martinus/nanobench
   7  //
   8  // Licensed under the MIT License <http://opensource.org/licenses/MIT>.
   9  // SPDX-License-Identifier: MIT
  10  // Copyright (c) 2019-2023 Martin Leitner-Ankerl <martin.ankerl@gmail.com>
  11  //
  12  // Permission is hereby granted, free of charge, to any person obtaining a copy
  13  // of this software and associated documentation files (the "Software"), to deal
  14  // in the Software without restriction, including without limitation the rights
  15  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  16  // copies of the Software, and to permit persons to whom the Software is
  17  // furnished to do so, subject to the following conditions:
  18  //
  19  // The above copyright notice and this permission notice shall be included in all
  20  // copies or substantial portions of the Software.
  21  //
  22  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  23  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  24  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  25  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  26  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  27  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  28  // SOFTWARE.
  29  
  30  #ifndef ANKERL_NANOBENCH_H_INCLUDED
  31  #define ANKERL_NANOBENCH_H_INCLUDED
  32  
  33  // see https://semver.org/
  34  #define ANKERL_NANOBENCH_VERSION_MAJOR 4  // incompatible API changes
  35  #define ANKERL_NANOBENCH_VERSION_MINOR 3  // backwards-compatible changes
  36  #define ANKERL_NANOBENCH_VERSION_PATCH 11 // backwards-compatible bug fixes
  37  
  38  ///////////////////////////////////////////////////////////////////////////////////////////////////
  39  // public facing api - as minimal as possible
  40  ///////////////////////////////////////////////////////////////////////////////////////////////////
  41  
  42  #include <chrono>        // high_resolution_clock
  43  #include <cassert>       // assert
  44  #include <cstdint>
  45  #include <cstring>       // memcpy
  46  // IWYU will only see this header with ANKERL_NANOBENCH_IMPLEMENT defined, which
  47  // makes it suggest removing forward declarations for the Input/output library.
  48  // IWYU pragma: begin_keep
  49  #include <iosfwd>        // for std::ostream* custom output target in Config
  50  // IWYU pragma: end_keep
  51  #include <limits>
  52  #include <ratio>
  53  #include <string>        // all names
  54  #include <type_traits>
  55  #include <unordered_map> // holds context information of results
  56  #include <utility>
  57  #include <vector>        // holds all results
  58  
  59  #define ANKERL_NANOBENCH(x) ANKERL_NANOBENCH_PRIVATE_##x()
  60  
  61  #define ANKERL_NANOBENCH_PRIVATE_CXX() __cplusplus
  62  #define ANKERL_NANOBENCH_PRIVATE_CXX98() 199711L
  63  #define ANKERL_NANOBENCH_PRIVATE_CXX11() 201103L
  64  #define ANKERL_NANOBENCH_PRIVATE_CXX14() 201402L
  65  #define ANKERL_NANOBENCH_PRIVATE_CXX17() 201703L
  66  
  67  #if ANKERL_NANOBENCH(CXX) >= ANKERL_NANOBENCH(CXX17)
  68  #    define ANKERL_NANOBENCH_PRIVATE_NODISCARD() [[nodiscard]]
  69  #else
  70  #    define ANKERL_NANOBENCH_PRIVATE_NODISCARD()
  71  #endif
  72  
  73  #if defined(__clang__)
  74  #    define ANKERL_NANOBENCH_PRIVATE_IGNORE_PADDED_PUSH() \
  75          _Pragma("clang diagnostic push") _Pragma("clang diagnostic ignored \"-Wpadded\"")
  76  #    define ANKERL_NANOBENCH_PRIVATE_IGNORE_PADDED_POP() _Pragma("clang diagnostic pop")
  77  #else
  78  #    define ANKERL_NANOBENCH_PRIVATE_IGNORE_PADDED_PUSH()
  79  #    define ANKERL_NANOBENCH_PRIVATE_IGNORE_PADDED_POP()
  80  #endif
  81  
  82  #if defined(__GNUC__)
  83  #    define ANKERL_NANOBENCH_PRIVATE_IGNORE_EFFCPP_PUSH() _Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Weffc++\"")
  84  #    define ANKERL_NANOBENCH_PRIVATE_IGNORE_EFFCPP_POP() _Pragma("GCC diagnostic pop")
  85  #else
  86  #    define ANKERL_NANOBENCH_PRIVATE_IGNORE_EFFCPP_PUSH()
  87  #    define ANKERL_NANOBENCH_PRIVATE_IGNORE_EFFCPP_POP()
  88  #endif
  89  
  90  #if defined(ANKERL_NANOBENCH_LOG_ENABLED)
  91  #    include <iostream>
  92  #    define ANKERL_NANOBENCH_LOG(x)                                                 \
  93          do {                                                                        \
  94              std::cout << __FUNCTION__ << "@" << __LINE__ << ": " << x << std::endl; \
  95          } while (0)
  96  #else
  97  #    define ANKERL_NANOBENCH_LOG(x) \
  98          do {                        \
  99          } while (0)
 100  #endif
 101  
 102  #define ANKERL_NANOBENCH_PRIVATE_PERF_COUNTERS() 0
 103  #if defined(__linux__) && !defined(ANKERL_NANOBENCH_DISABLE_PERF_COUNTERS)
 104  #    include <linux/version.h>
 105  #    if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0)
 106  // PERF_COUNT_HW_REF_CPU_CYCLES only available since kernel 3.3
 107  // PERF_FLAG_FD_CLOEXEC since kernel 3.14
 108  #        undef ANKERL_NANOBENCH_PRIVATE_PERF_COUNTERS
 109  #        define ANKERL_NANOBENCH_PRIVATE_PERF_COUNTERS() 1
 110  #    endif
 111  #endif
 112  
 113  #if defined(__clang__)
 114  #    define ANKERL_NANOBENCH_NO_SANITIZE(...) __attribute__((no_sanitize(__VA_ARGS__)))
 115  #else
 116  #    define ANKERL_NANOBENCH_NO_SANITIZE(...)
 117  #endif
 118  
 119  #if defined(_MSC_VER)
 120  #    define ANKERL_NANOBENCH_PRIVATE_NOINLINE() __declspec(noinline)
 121  #else
 122  #    define ANKERL_NANOBENCH_PRIVATE_NOINLINE() __attribute__((noinline))
 123  #endif
 124  
 125  // workaround missing "is_trivially_copyable" in g++ < 5.0
 126  // See https://stackoverflow.com/a/31798726/48181
 127  #if defined(__GNUC__) && __GNUC__ < 5
 128  #    define ANKERL_NANOBENCH_IS_TRIVIALLY_COPYABLE(...) __has_trivial_copy(__VA_ARGS__)
 129  #else
 130  #    define ANKERL_NANOBENCH_IS_TRIVIALLY_COPYABLE(...) std::is_trivially_copyable<__VA_ARGS__>::value
 131  #endif
 132  
 133  // noexcept may be missing for std::string.
 134  // See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58265
 135  #define ANKERL_NANOBENCH_PRIVATE_NOEXCEPT_STRING_MOVE() std::is_nothrow_move_assignable<std::string>::value
 136  
 137  // declarations ///////////////////////////////////////////////////////////////////////////////////
 138  
 139  // As definitions follow these declarations within this
 140  // header, IWYU considers some of them redundant and
 141  // suggests removing them. Disable this IWYU behavior.
 142  // IWYU pragma: begin_keep
 143  
 144  namespace ankerl {
 145  namespace nanobench {
 146  
 147  using Clock = std::conditional<std::chrono::high_resolution_clock::is_steady, std::chrono::high_resolution_clock,
 148                                 std::chrono::steady_clock>::type;
 149  class Bench;
 150  struct Config;
 151  class Result;
 152  class Rng;
 153  class BigO;
 154  
 155  namespace detail {
 156  template <typename SetupOp>
 157  class SetupRunner;
 158  } // namespace detail
 159  
 160  /**
 161   * @brief Renders output from a mustache-like template and benchmark results.
 162   *
 163   * The templating facility here is heavily inspired by [mustache - logic-less templates](https://mustache.github.io/).
 164   * It adds a few more features that are necessary to get all of the captured data out of nanobench. Please read the
 165   * excellent [mustache manual](https://mustache.github.io/mustache.5.html) to see what this is all about.
 166   *
 167   * nanobench output has two nested layers, *result* and *measurement*.  Here is a hierarchy of the allowed tags:
 168   *
 169   * * `{{#result}}` Marks the begin of the result layer. Whatever comes after this will be instantiated as often as
 170   *   a benchmark result is available. Within it, you can use these tags:
 171   *
 172   *    * `{{title}}` See Bench::title.
 173   *
 174   *    * `{{name}}` Benchmark name, usually directly provided with Bench::run, but can also be set with Bench::name.
 175   *
 176   *    * `{{unit}}` Unit, e.g. `byte`. Defaults to `op`, see Bench::unit.
 177   *
 178   *    * `{{batch}}` Batch size, see Bench::batch.
 179   *
 180   *    * `{{complexityN}}` Value used for asymptotic complexity calculation. See Bench::complexityN.
 181   *
 182   *    * `{{epochs}}` Number of epochs, see Bench::epochs.
 183   *
 184   *    * `{{clockResolution}}` Accuracy of the clock, i.e. what's the smallest time possible to measure with the clock.
 185   *      For modern systems, this can be around 20 ns. This value is automatically determined by nanobench at the first
 186   *      benchmark that is run, and used as a static variable throughout the application's runtime.
 187   *
 188   *    * `{{clockResolutionMultiple}}` Configuration multiplier for `clockResolution`. See Bench::clockResolutionMultiple.
 189   *      This is the target runtime for each measurement (epoch). That means the more accurate your clock is, the faster
 190   *      will be the benchmark. Basing the measurement's runtime on the clock resolution is the main reason why nanobench is so fast.
 191   *
 192   *    * `{{maxEpochTime}}` Configuration for a maximum time each measurement (epoch) is allowed to take. Note that at least
 193   *      a single iteration will be performed, even when that takes longer than maxEpochTime. See Bench::maxEpochTime.
 194   *
 195   *    * `{{minEpochTime}}` Minimum epoch time, defaults to 1ms. See Bench::minEpochTime.
 196   *
 197   *    * `{{minEpochIterations}}` See Bench::minEpochIterations.
 198   *
 199   *    * `{{epochIterations}}` See Bench::epochIterations.
 200   *
 201   *    * `{{warmup}}` Number of iterations used before measuring starts. See Bench::warmup.
 202   *
 203   *    * `{{relative}}` True or false, depending on the setting you have used. See Bench::relative.
 204   *
 205   *    * `{{context(variableName)}}` See Bench::context.
 206   *
 207   *    Apart from these tags, it is also possible to use some mathematical operations on the measurement data. The operations
 208   *    are of the form `{{command(name)}}`.  Currently `name` can be one of `elapsed`, `iterations`. If performance counters
 209   *    are available (currently only on current Linux systems), you also have `pagefaults`, `cpucycles`,
 210   *    `contextswitches`, `instructions`, `branchinstructions`, and `branchmisses`. All the measures (except `iterations`) are
 211   *    provided for a single iteration (so `elapsed` is the time a single iteration took). The following tags are available:
 212   *
 213   *    * `{{median(<name>)}}` Calculate median of a measurement data set, e.g. `{{median(elapsed)}}`.
 214   *
 215   *    * `{{average(<name>)}}` Average (mean) calculation.
 216   *
 217   *    * `{{medianAbsolutePercentError(<name>)}}` Calculates MdAPE, the Median Absolute Percentage Error. The MdAPE is an excellent
 218   *      metric for the variation of measurements. It is more robust to outliers than the
 219   *      [Mean absolute percentage error (M-APE)](https://en.wikipedia.org/wiki/Mean_absolute_percentage_error).
 220   *      @f[
 221   *       \mathrm{MdAPE}(e) = \mathrm{med}\{| \frac{e_i - \mathrm{med}\{e\}}{e_i}| \}
 222   *      @f]
 223   *      E.g. for *elapsed*: First, @f$ \mathrm{med}\{e\} @f$ calculates the median by sorting and then taking the middle element
 224   *      of all *elapsed* measurements. This is used to calculate the absolute percentage
 225   *      error to this median for each measurement, as in  @f$ | \frac{e_i - \mathrm{med}\{e\}}{e_i}| @f$. All these results
 226   *      are sorted, and the middle value is chosen as the median absolute percent error.
 227   *
 228   *      This measurement is a bit hard to interpret, but it is very robust against outliers. E.g. a value of 5% means that half of the
 229   *      measurements deviate less than 5% from the median, and the other deviate more than 5% from the median.
 230   *
 231   *    * `{{sum(<name>)}}` Sum of all the measurements. E.g. `{{sum(iterations)}}` will give you the total number of iterations
 232  *        measured in this benchmark.
 233   *
 234   *    * `{{minimum(<name>)}}` Minimum of all measurements.
 235   *
 236   *    * `{{maximum(<name>)}}` Maximum of all measurements.
 237   *
 238   *    * `{{sumProduct(<first>, <second>)}}` Calculates the sum of the products of corresponding measures:
 239   *      @f[
 240   *          \mathrm{sumProduct}(a,b) = \sum_{i=1}^{n}a_i\cdot b_i
 241   *      @f]
 242   *      E.g. to calculate total runtime of the benchmark, you multiply iterations with elapsed time for each measurement, and
 243   *      sum these results up:
 244   *      `{{sumProduct(iterations, elapsed)}}`.
 245   *
 246   *    * `{{#measurement}}` To access individual measurement results, open the begin tag for measurements.
 247   *
 248   *       * `{{elapsed}}` Average elapsed wall clock time per iteration, in seconds.
 249   *
 250   *       * `{{iterations}}` Number of iterations in the measurement. The number of iterations will fluctuate due
 251   *         to some applied randomness, to enhance accuracy.
 252   *
 253   *       * `{{pagefaults}}` Average number of pagefaults per iteration.
 254   *
 255   *       * `{{cpucycles}}` Average number of CPU cycles processed per iteration.
 256   *
 257   *       * `{{contextswitches}}` Average number of context switches per iteration.
 258   *
 259   *       * `{{instructions}}` Average number of retired instructions per iteration.
 260   *
 261   *       * `{{branchinstructions}}` Average number of branches executed per iteration.
 262   *
 263   *       * `{{branchmisses}}` Average number of branches that were missed per iteration.
 264   *
 265   *    * `{{/measurement}}` Ends the measurement tag.
 266   *
 267   * * `{{/result}}` Marks the end of the result layer. This is the end marker for the template part that will be instantiated
 268   *   for each benchmark result.
 269   *
 270   *
 271   *  For the layer tags *result* and *measurement* you additionally can use these special markers:
 272   *
 273   *  * ``{{#-first}}`` - Begin marker of a template that will be instantiated *only for the first* entry in the layer. Use is only
 274   *    allowed between the begin and end marker of the layer. So between ``{{#result}}`` and ``{{/result}}``, or between
 275   *    ``{{#measurement}}`` and ``{{/measurement}}``. Finish the template with ``{{/-first}}``.
 276   *
 277   *  * ``{{^-first}}`` - Begin marker of a template that will be instantiated *for each except the first* entry in the layer. This,
 278   *    this is basically the inversion of ``{{#-first}}``. Use is only allowed between the begin and end marker of the layer.
 279   *    So between ``{{#result}}`` and ``{{/result}}``, or between ``{{#measurement}}`` and ``{{/measurement}}``.
 280   *
 281   *  * ``{{/-first}}`` - End marker for either ``{{#-first}}`` or ``{{^-first}}``.
 282   *
 283   *  * ``{{#-last}}`` - Begin marker of a template that will be instantiated *only for the last* entry in the layer. Use is only
 284   *    allowed between the begin and end marker of the layer. So between ``{{#result}}`` and ``{{/result}}``, or between
 285   *    ``{{#measurement}}`` and ``{{/measurement}}``. Finish the template with ``{{/-last}}``.
 286   *
 287   *  * ``{{^-last}}`` - Begin marker of a template that will be instantiated *for each except the last* entry in the layer. This,
 288   *    this is basically the inversion of ``{{#-last}}``. Use is only allowed between the begin and end marker of the layer.
 289   *    So between ``{{#result}}`` and ``{{/result}}``, or between ``{{#measurement}}`` and ``{{/measurement}}``.
 290   *
 291   *  * ``{{/-last}}`` - End marker for either ``{{#-last}}`` or ``{{^-last}}``.
 292   *
 293     @verbatim embed:rst
 294  
 295     For an overview of all the possible data you can get out of nanobench, please see the tutorial at :ref:`tutorial-template-json`.
 296  
 297     The templates that ship with nanobench are:
 298  
 299     * :cpp:func:`templates::csv() <ankerl::nanobench::templates::csv()>`
 300     * :cpp:func:`templates::json() <ankerl::nanobench::templates::json()>`
 301     * :cpp:func:`templates::htmlBoxplot() <ankerl::nanobench::templates::htmlBoxplot()>`
 302     * :cpp:func:`templates::pyperf() <ankerl::nanobench::templates::pyperf()>`
 303  
 304     @endverbatim
 305   *
 306   * @param mustacheTemplate The template.
 307   * @param bench Benchmark, containing all the results.
 308   * @param out Output for the generated output.
 309   */
 310  void render(char const* mustacheTemplate, Bench const& bench, std::ostream& out);
 311  void render(std::string const& mustacheTemplate, Bench const& bench, std::ostream& out);
 312  
 313  /**
 314   * Same as render(char const* mustacheTemplate, Bench const& bench, std::ostream& out), but for when
 315   * you only have results available.
 316   *
 317   * @param mustacheTemplate The template.
 318   * @param results All the results to be used for rendering.
 319   * @param out Output for the generated output.
 320   */
 321  void render(char const* mustacheTemplate, std::vector<Result> const& results, std::ostream& out);
 322  void render(std::string const& mustacheTemplate, std::vector<Result> const& results, std::ostream& out);
 323  
 324  // Contains mustache-like templates
 325  namespace templates {
 326  
 327  /*!
 328    @brief CSV data for the benchmark results.
 329  
 330    Generates a comma-separated values dataset. First line is the header, each following line is a summary of each benchmark run.
 331  
 332    @verbatim embed:rst
 333    See the tutorial at :ref:`tutorial-template-csv` for an example.
 334    @endverbatim
 335   */
 336  char const* csv() noexcept;
 337  
 338  /*!
 339    @brief HTML output that uses plotly to generate an interactive boxplot chart. See the tutorial for an example output.
 340  
 341    The output uses only the elapsed wall clock time, and displays each epoch as a single dot.
 342    @verbatim embed:rst
 343    See the tutorial at :ref:`tutorial-template-html` for an example.
 344    @endverbatim
 345  
 346    @see also ankerl::nanobench::render()
 347   */
 348  char const* htmlBoxplot() noexcept;
 349  
 350  /*!
 351   @brief Output in pyperf compatible JSON format, which can be used for more analyzation.
 352   @verbatim embed:rst
 353   See the tutorial at :ref:`tutorial-template-pyperf` for an example how to further analyze the output.
 354   @endverbatim
 355   */
 356  char const* pyperf() noexcept;
 357  
 358  /*!
 359    @brief Template to generate JSON data.
 360  
 361    The generated JSON data contains *all* data that has been generated. All times are as double values, in seconds. The output can get
 362    quite large.
 363    @verbatim embed:rst
 364    See the tutorial at :ref:`tutorial-template-json` for an example.
 365    @endverbatim
 366   */
 367  char const* json() noexcept;
 368  
 369  } // namespace templates
 370  
 371  namespace detail {
 372  
 373  template <typename T>
 374  struct PerfCountSet;
 375  
 376  class IterationLogic;
 377  class PerformanceCounters;
 378  
 379  #if ANKERL_NANOBENCH(PERF_COUNTERS)
 380  class LinuxPerformanceCounters;
 381  #endif
 382  
 383  } // namespace detail
 384  } // namespace nanobench
 385  } // namespace ankerl
 386  
 387  // IWYU pragma: end_keep
 388  
 389  // definitions ////////////////////////////////////////////////////////////////////////////////////
 390  
 391  namespace ankerl {
 392  namespace nanobench {
 393  namespace detail {
 394  
 395  template <typename T>
 396  struct PerfCountSet {
 397      T pageFaults{};
 398      T cpuCycles{};
 399      T contextSwitches{};
 400      T instructions{};
 401      T branchInstructions{};
 402      T branchMisses{};
 403  };
 404  
 405  } // namespace detail
 406  
 407  ANKERL_NANOBENCH(IGNORE_PADDED_PUSH)
 408  struct Config {
 409      // actual benchmark config
 410      std::string mBenchmarkTitle = "benchmark";                               // NOLINT(misc-non-private-member-variables-in-classes)
 411      std::string mBenchmarkName = "noname";                                   // NOLINT(misc-non-private-member-variables-in-classes)
 412      std::string mUnit = "op";                                                // NOLINT(misc-non-private-member-variables-in-classes)
 413      double mBatch = 1.0;                                                     // NOLINT(misc-non-private-member-variables-in-classes)
 414      double mComplexityN = -1.0;                                              // NOLINT(misc-non-private-member-variables-in-classes)
 415      size_t mNumEpochs = 11;                                                  // NOLINT(misc-non-private-member-variables-in-classes)
 416      size_t mClockResolutionMultiple = static_cast<size_t>(1000);             // NOLINT(misc-non-private-member-variables-in-classes)
 417      std::chrono::nanoseconds mMaxEpochTime = std::chrono::milliseconds(100); // NOLINT(misc-non-private-member-variables-in-classes)
 418      std::chrono::nanoseconds mMinEpochTime = std::chrono::milliseconds(1);   // NOLINT(misc-non-private-member-variables-in-classes)
 419      uint64_t mMinEpochIterations{1};                                         // NOLINT(misc-non-private-member-variables-in-classes)
 420      // If not 0, run *exactly* these number of iterations per epoch.
 421      uint64_t mEpochIterations{0};                                          // NOLINT(misc-non-private-member-variables-in-classes)
 422      uint64_t mWarmup = 0;                                                  // NOLINT(misc-non-private-member-variables-in-classes)
 423      std::ostream* mOut = nullptr;                                          // NOLINT(misc-non-private-member-variables-in-classes)
 424      std::chrono::duration<double> mTimeUnit = std::chrono::nanoseconds{1}; // NOLINT(misc-non-private-member-variables-in-classes)
 425      std::string mTimeUnitName = "ns";                                      // NOLINT(misc-non-private-member-variables-in-classes)
 426      bool mShowPerformanceCounters = true;                                  // NOLINT(misc-non-private-member-variables-in-classes)
 427      bool mIsRelative = false;                                              // NOLINT(misc-non-private-member-variables-in-classes)
 428      std::unordered_map<std::string, std::string> mContext{};               // NOLINT(misc-non-private-member-variables-in-classes)
 429  
 430      Config();
 431      ~Config();
 432      Config& operator=(Config const& other);
 433      Config& operator=(Config&& other) noexcept(ANKERL_NANOBENCH(NOEXCEPT_STRING_MOVE));
 434      Config(Config const& other);
 435      Config(Config&& other) noexcept;
 436  };
 437  ANKERL_NANOBENCH(IGNORE_PADDED_POP)
 438  
 439  // Result returned after a benchmark has finished. Can be used as a baseline for relative().
 440  ANKERL_NANOBENCH(IGNORE_PADDED_PUSH)
 441  class Result {
 442  public:
 443      enum class Measure : size_t {
 444          elapsed,
 445          iterations,
 446          pagefaults,
 447          cpucycles,
 448          contextswitches,
 449          instructions,
 450          branchinstructions,
 451          branchmisses,
 452          _size
 453      };
 454  
 455      explicit Result(Config benchmarkConfig);
 456  
 457      ~Result();
 458      Result& operator=(Result const& other);
 459      Result& operator=(Result&& other) noexcept(ANKERL_NANOBENCH(NOEXCEPT_STRING_MOVE));
 460      Result(Result const& other);
 461      Result(Result&& other) noexcept;
 462  
 463      // adds new measurement results
 464      // all values are scaled by iters (except iters...)
 465      void add(Clock::duration totalElapsed, uint64_t iters, detail::PerformanceCounters const& pc);
 466  
 467      ANKERL_NANOBENCH(NODISCARD) Config const& config() const noexcept;
 468  
 469      ANKERL_NANOBENCH(NODISCARD) double median(Measure m) const;
 470      ANKERL_NANOBENCH(NODISCARD) double medianAbsolutePercentError(Measure m) const;
 471      ANKERL_NANOBENCH(NODISCARD) double average(Measure m) const;
 472      ANKERL_NANOBENCH(NODISCARD) double sum(Measure m) const noexcept;
 473      ANKERL_NANOBENCH(NODISCARD) double sumProduct(Measure m1, Measure m2) const noexcept;
 474      ANKERL_NANOBENCH(NODISCARD) double minimum(Measure m) const noexcept;
 475      ANKERL_NANOBENCH(NODISCARD) double maximum(Measure m) const noexcept;
 476      ANKERL_NANOBENCH(NODISCARD) std::string const& context(char const* variableName) const;
 477      ANKERL_NANOBENCH(NODISCARD) std::string const& context(std::string const& variableName) const;
 478  
 479      ANKERL_NANOBENCH(NODISCARD) bool has(Measure m) const noexcept;
 480      ANKERL_NANOBENCH(NODISCARD) double get(size_t idx, Measure m) const;
 481      ANKERL_NANOBENCH(NODISCARD) bool empty() const noexcept;
 482      ANKERL_NANOBENCH(NODISCARD) size_t size() const noexcept;
 483  
 484      // Finds string, if not found, returns _size.
 485      static Measure fromString(std::string const& str);
 486  
 487  private:
 488      Config mConfig{};
 489      std::vector<std::vector<double>> mNameToMeasurements{};
 490  };
 491  ANKERL_NANOBENCH(IGNORE_PADDED_POP)
 492  
 493  /**
 494   * An extremely fast random generator. Currently, this implements *RomuDuoJr*, developed by Mark Overton. Source:
 495   * http://www.romu-random.org/
 496   *
 497   * RomuDuoJr is extremely fast and provides reasonable good randomness. Not enough for large jobs, but definitely
 498   * good enough for a benchmarking framework.
 499   *
 500   *  * Estimated capacity: @f$ 2^{51} @f$ bytes
 501   *  * Register pressure: 4
 502   *  * State size: 128 bits
 503   *
 504   * This random generator is a drop-in replacement for the generators supplied by ``<random>``. It is not
 505   * cryptographically secure. It's intended purpose is to be very fast so that benchmarks that make use
 506   * of randomness are not distorted too much by the random generator.
 507   *
 508   * Rng also provides a few non-standard helpers, optimized for speed.
 509   */
 510  class Rng final {
 511  public:
 512      /**
 513       * @brief This RNG provides 64bit randomness.
 514       */
 515      using result_type = uint64_t;
 516  
 517      static constexpr uint64_t(min)();
 518      static constexpr uint64_t(max)();
 519  
 520      /**
 521       * As a safety precaution, we don't allow copying. Copying a PRNG would mean you would have two random generators that produce the
 522       * same sequence, which is generally not what one wants. Instead create a new rng with the default constructor Rng(), which is
 523       * automatically seeded from `std::random_device`. If you really need a copy, use `copy()`.
 524       */
 525      Rng(Rng const&) = delete;
 526  
 527      /**
 528       * Same as Rng(Rng const&), we don't allow assignment. If you need a new Rng create one with the default constructor Rng().
 529       */
 530      Rng& operator=(Rng const&) = delete;
 531  
 532      // moving is ok
 533      Rng(Rng&&) noexcept = default;
 534      Rng& operator=(Rng&&) noexcept = default;
 535      ~Rng() noexcept = default;
 536  
 537      /**
 538       * @brief Creates a new Random generator with random seed.
 539       *
 540       * Instead of a default seed (as the random generators from the STD), this properly seeds the random generator from
 541       * `std::random_device`. It guarantees correct seeding. Note that seeding can be relatively slow, depending on the source of
 542       * randomness used. So it is best to create a Rng once and use it for all your randomness purposes.
 543       */
 544      Rng();
 545  
 546      /*!
 547        Creates a new Rng that is seeded with a specific seed. Each Rng created from the same seed will produce the same randomness
 548        sequence. This can be useful for deterministic behavior.
 549  
 550        @verbatim embed:rst
 551        .. note::
 552  
 553           The random algorithm might change between nanobench releases. Whenever a faster and/or better random
 554           generator becomes available, I will switch the implementation.
 555        @endverbatim
 556  
 557        As per the Romu paper, this seeds the Rng with splitMix64 algorithm and performs 10 initial rounds for further mixing up of the
 558        internal state.
 559  
 560        @param seed  The 64bit seed. All values are allowed, even 0.
 561       */
 562      explicit Rng(uint64_t seed) noexcept;
 563      Rng(uint64_t x, uint64_t y) noexcept;
 564      explicit Rng(std::vector<uint64_t> const& data);
 565  
 566      /**
 567       * Creates a copy of the Rng, thus the copy provides exactly the same random sequence as the original.
 568       */
 569      ANKERL_NANOBENCH(NODISCARD) Rng copy() const noexcept;
 570  
 571      /**
 572       * @brief Produces a 64bit random value. This should be very fast, thus it is marked as inline. In my benchmark, this is ~46 times
 573       * faster than `std::default_random_engine` for producing 64bit random values. It seems that the fastest std contender is
 574       * `std::mt19937_64`. Still, this RNG is 2-3 times as fast.
 575       *
 576       * @return uint64_t The next 64 bit random value.
 577       */
 578      inline uint64_t operator()() noexcept;
 579  
 580      // This is slightly biased. See
 581  
 582      /**
 583       * Generates a random number between 0 and range (excluding range).
 584       *
 585       * The algorithm only produces 32bit numbers, and is slightly biased. The effect is quite small unless your range is close to the
 586       * maximum value of an integer. It is possible to correct the bias with rejection sampling (see
 587       * [here](https://lemire.me/blog/2016/06/30/fast-random-shuffling/), but this is most likely irrelevant in practices for the
 588       * purposes of this Rng.
 589       *
 590       * See Daniel Lemire's blog post [A fast alternative to the modulo
 591       * reduction](https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/)
 592       *
 593       * @param range Upper exclusive range. E.g a value of 3 will generate random numbers 0, 1, 2.
 594       * @return uint32_t Generated random values in range [0, range(.
 595       */
 596      inline uint32_t bounded(uint32_t range) noexcept;
 597  
 598      // random double in range [0, 1(
 599      // see http://prng.di.unimi.it/
 600  
 601      /**
 602       * Provides a random uniform double value between 0 and 1. This uses the method described in [Generating uniform doubles in the
 603       * unit interval](http://prng.di.unimi.it/), and is extremely fast.
 604       *
 605       * @return double Uniformly distributed double value in range [0,1(, excluding 1.
 606       */
 607      inline double uniform01() noexcept;
 608  
 609      /**
 610       * Shuffles all entries in the given container. Although this has a slight bias due to the implementation of bounded(), this is
 611       * preferable to `std::shuffle` because it is over 5 times faster. See Daniel Lemire's blog post [Fast random
 612       * shuffling](https://lemire.me/blog/2016/06/30/fast-random-shuffling/).
 613       *
 614       * @param container The whole container will be shuffled.
 615       */
 616      template <typename Container>
 617      void shuffle(Container& container) noexcept;
 618  
 619      /**
 620       * Extracts the full state of the generator, e.g. for serialization. For this RNG this is just 2 values, but to stay API compatible
 621       * with future implementations that potentially use more state, we use a vector.
 622       *
 623       * @return Vector containing the full state:
 624       */
 625      ANKERL_NANOBENCH(NODISCARD) std::vector<uint64_t> state() const;
 626  
 627  private:
 628      static constexpr uint64_t rotl(uint64_t x, unsigned k) noexcept;
 629  
 630      uint64_t mX;
 631      uint64_t mY;
 632  };
 633  
 634  /**
 635   * @brief Main entry point to nanobench's benchmarking facility.
 636   *
 637   * It holds configuration and results from one or more benchmark runs. Usually it is used in a single line, where the object is
 638   * constructed, configured, and then a benchmark is run. E.g. like this:
 639   *
 640   *     ankerl::nanobench::Bench().unit("byte").batch(1000).run("random fluctuations", [&] {
 641   *         // here be the benchmark code
 642   *     });
 643   *
 644   * In that example Bench() constructs the benchmark, it is then configured with unit() and batch(), and after configuration a
 645   * benchmark is executed with run(). Once run() has finished, it prints the result to `std::cout`. It would also store the results
 646   * in the Bench instance, but in this case the object is immediately destroyed so it's not available any more.
 647   */
 648  ANKERL_NANOBENCH(IGNORE_PADDED_PUSH)
 649  class Bench {
 650  public:
 651      /**
 652       * @brief Creates a new benchmark for configuration and running of benchmarks.
 653       */
 654      Bench();
 655  
 656      Bench(Bench&& other) noexcept;
 657      Bench& operator=(Bench&& other) noexcept(ANKERL_NANOBENCH(NOEXCEPT_STRING_MOVE));
 658      Bench(Bench const& other);
 659      Bench& operator=(Bench const& other);
 660      ~Bench() noexcept;
 661  
 662      /*!
 663        @brief Repeatedly calls `op()` based on the configuration, and performs measurements.
 664  
 665        This call is marked with `noinline` to prevent the compiler to optimize beyond different benchmarks. This can have quite a big
 666        effect on benchmark accuracy.
 667  
 668        @verbatim embed:rst
 669        .. note::
 670  
 671          Each call to your lambda must have a side effect that the compiler can't possibly optimize it away. E.g. add a result to an
 672          externally defined number (like `x` in the above example), and finally call `doNotOptimizeAway` on the variables the compiler
 673          must not remove. You can also use :cpp:func:`ankerl::nanobench::doNotOptimizeAway` directly in the lambda, but be aware that
 674          this has a small overhead.
 675  
 676        @endverbatim
 677  
 678        @tparam Op The code to benchmark.
 679       */
 680      template <typename Op>
 681      ANKERL_NANOBENCH(NOINLINE)
 682      Bench& run(char const* benchmarkName, Op&& op);
 683  
 684      template <typename Op>
 685      ANKERL_NANOBENCH(NOINLINE)
 686      Bench& run(std::string const& benchmarkName, Op&& op);
 687  
 688      /**
 689       * @brief Same as run(char const* benchmarkName, Op op), but instead uses the previously set name.
 690       * @tparam Op The code to benchmark.
 691       */
 692      template <typename Op>
 693      ANKERL_NANOBENCH(NOINLINE)
 694      Bench& run(Op&& op);
 695  
 696      /**
 697       * @brief Title of the benchmark, will be shown in the table header. Changing the title will start a new markdown table.
 698       *
 699       * @param benchmarkTitle The title of the benchmark.
 700       */
 701      Bench& title(char const* benchmarkTitle);
 702      Bench& title(std::string const& benchmarkTitle);
 703  
 704      /**
 705       * @brief Gets the title of the benchmark
 706       */
 707      ANKERL_NANOBENCH(NODISCARD) std::string const& title() const noexcept;
 708  
 709      /// Name of the benchmark, will be shown in the table row.
 710      Bench& name(char const* benchmarkName);
 711      Bench& name(std::string const& benchmarkName);
 712      ANKERL_NANOBENCH(NODISCARD) std::string const& name() const noexcept;
 713  
 714      /**
 715       * @brief Set context information.
 716       *
 717       * The information can be accessed using custom render templates via `{{context(variableName)}}`.
 718       * Trying to render a variable that hasn't been set before raises an exception.
 719       * Not included in (default) markdown table.
 720       *
 721       * @see clearContext, render
 722       *
 723       * @param variableName The name of the context variable.
 724       * @param variableValue The value of the context variable.
 725       */
 726      Bench& context(char const* variableName, char const* variableValue);
 727      Bench& context(std::string const& variableName, std::string const& variableValue);
 728  
 729      /**
 730       * @brief Reset context information.
 731       *
 732       * This may improve efficiency when using many context entries,
 733       * or improve robustness by removing spurious context entries.
 734       *
 735       * @see context
 736       */
 737      Bench& clearContext();
 738  
 739      /**
 740       * @brief Sets the batch size.
 741       *
 742       * E.g. number of processed byte, or some other metric for the size of the processed data in each iteration. If you benchmark
 743       * hashing of a 1000 byte long string and want byte/sec as a result, you can specify 1000 as the batch size.
 744       *
 745       * @tparam T Any input type is internally cast to `double`.
 746       * @param b batch size
 747       */
 748      template <typename T>
 749      Bench& batch(T b) noexcept;
 750      ANKERL_NANOBENCH(NODISCARD) double batch() const noexcept;
 751  
 752      /**
 753       * @brief Sets the operation unit.
 754       *
 755       * Defaults to "op". Could be e.g. "byte" for string processing. This is used for the table header, e.g. to show `ns/byte`. Use
 756       * singular (*byte*, not *bytes*). A change clears the currently collected results.
 757       *
 758       * @param unit The unit name.
 759       */
 760      Bench& unit(char const* unit);
 761      Bench& unit(std::string const& unit);
 762      ANKERL_NANOBENCH(NODISCARD) std::string const& unit() const noexcept;
 763  
 764      /**
 765       * @brief Sets the time unit to be used for the default output.
 766       *
 767       * Nanobench defaults to using ns (nanoseconds) as output in the markdown. For some benchmarks this is too coarse, so it is
 768       * possible to configure this. E.g. use `timeUnit(1ms, "ms")` to show `ms/op` instead of `ns/op`.
 769       *
 770       * @param tu Time unit to display the results in, default is 1ns.
 771       * @param tuName Name for the time unit, default is "ns"
 772       */
 773      Bench& timeUnit(std::chrono::duration<double> const& tu, std::string const& tuName);
 774      ANKERL_NANOBENCH(NODISCARD) std::string const& timeUnitName() const noexcept;
 775      ANKERL_NANOBENCH(NODISCARD) std::chrono::duration<double> const& timeUnit() const noexcept;
 776  
 777      /**
 778       * @brief Set the output stream where the resulting markdown table will be printed to.
 779       *
 780       * The default is `&std::cout`. You can disable all output by setting `nullptr`.
 781       *
 782       * @param outstream Pointer to output stream, can be `nullptr`.
 783       */
 784      Bench& output(std::ostream* outstream) noexcept;
 785      ANKERL_NANOBENCH(NODISCARD) std::ostream* output() const noexcept;
 786  
 787      /**
 788       * Modern processors have a very accurate clock, being able to measure as low as 20 nanoseconds. This is the main trick nanobech to
 789       * be so fast: we find out how accurate the clock is, then run the benchmark only so often that the clock's accuracy is good enough
 790       * for accurate measurements.
 791       *
 792       * The default is to run one epoch for 1000 times the clock resolution. So for 20ns resolution and 11 epochs, this gives a total
 793       * runtime of
 794       *
 795       * @f[
 796       * 20ns * 1000 * 11 \approx 0.2ms
 797       * @f]
 798       *
 799       * To be precise, nanobench adds a 0-20% random noise to each evaluation. This is to prevent any aliasing effects, and further
 800       * improves accuracy.
 801       *
 802       * Total runtime will be higher though: Some initial time is needed to find out the target number of iterations for each epoch, and
 803       * there is some overhead involved to start & stop timers and calculate resulting statistics and writing the output.
 804       *
 805       * @param multiple Target number of times of clock resolution. Usually 1000 is a good compromise between runtime and accuracy.
 806       */
 807      Bench& clockResolutionMultiple(size_t multiple) noexcept;
 808      ANKERL_NANOBENCH(NODISCARD) size_t clockResolutionMultiple() const noexcept;
 809  
 810      /**
 811       * @brief Controls number of epochs, the number of measurements to perform.
 812       *
 813       * The reported result will be the median of evaluation of each epoch. The higher you choose this, the more
 814       * deterministic the result be and outliers will be more easily removed. Also the `err%` will be more accurate the higher this
 815       * number is. Note that the `err%` will not necessarily decrease when number of epochs is increased. But it will be a more accurate
 816       * representation of the benchmarked code's runtime stability.
 817       *
 818       * Choose the value wisely. In practice, 11 has been shown to be a reasonable choice between runtime performance and accuracy.
 819       * This setting goes hand in hand with minEpochIterations() (or minEpochTime()). If you are more interested in *median* runtime,
 820       * you might want to increase epochs(). If you are more interested in *mean* runtime, you might want to increase
 821       * minEpochIterations() instead.
 822       *
 823       * @param numEpochs Number of epochs.
 824       */
 825      Bench& epochs(size_t numEpochs) noexcept;
 826      ANKERL_NANOBENCH(NODISCARD) size_t epochs() const noexcept;
 827  
 828      /**
 829       * @brief Upper limit for the runtime of each epoch.
 830       *
 831       * As a safety precaution if the clock is not very accurate, we can set an upper limit for the maximum evaluation time per
 832       * epoch. Default is 100ms. At least a single evaluation of the benchmark is performed.
 833       *
 834       * @see minEpochTime, minEpochIterations
 835       *
 836       * @param t Maximum target runtime for a single epoch.
 837       */
 838      Bench& maxEpochTime(std::chrono::nanoseconds t) noexcept;
 839      ANKERL_NANOBENCH(NODISCARD) std::chrono::nanoseconds maxEpochTime() const noexcept;
 840  
 841      /**
 842       * @brief Minimum time each epoch should take.
 843       *
 844       * Default is 1ms, so we are mostly relying on clockResolutionMultiple(). In most cases this is exactly what you want. If you see
 845       * that the evaluation is unreliable with a high `err%`, you can increase either minEpochTime() or minEpochIterations().
 846       *
 847       * @see maxEpochTime, minEpochIterations
 848       *
 849       * @param t Minimum time each epoch should take.
 850       */
 851      Bench& minEpochTime(std::chrono::nanoseconds t) noexcept;
 852      ANKERL_NANOBENCH(NODISCARD) std::chrono::nanoseconds minEpochTime() const noexcept;
 853  
 854      /**
 855       * @brief Sets the minimum number of iterations each epoch should take.
 856       *
 857       * Default is 1, and we rely on clockResolutionMultiple(). If the `err%` is high and you want a more smooth result, you might want
 858       * to increase the minimum number of iterations, or increase the minEpochTime().
 859       *
 860       * @see minEpochTime, maxEpochTime, minEpochIterations
 861       *
 862       * @param numIters Minimum number of iterations per epoch.
 863       */
 864      Bench& minEpochIterations(uint64_t numIters) noexcept;
 865      ANKERL_NANOBENCH(NODISCARD) uint64_t minEpochIterations() const noexcept;
 866  
 867      /**
 868       * Sets exactly the number of iterations for each epoch. Ignores all other epoch limits. This forces nanobench to use exactly
 869       * the given number of iterations for each epoch, not more and not less. Default is 0 (disabled).
 870       *
 871       * @param numIters Exact number of iterations to use. Set to 0 to disable.
 872       */
 873      Bench& epochIterations(uint64_t numIters) noexcept;
 874      ANKERL_NANOBENCH(NODISCARD) uint64_t epochIterations() const noexcept;
 875  
 876      /**
 877       * @brief Sets a number of iterations that are initially performed without any measurements.
 878       *
 879       * Some benchmarks need a few evaluations to warm up caches / database / whatever access. Normally this should not be needed, since
 880       * we show the median result so initial outliers will be filtered away automatically. If the warmup effect is large though, you
 881       * might want to set it. Default is 0.
 882       *
 883       * @param numWarmupIters Number of warmup iterations.
 884       */
 885      Bench& warmup(uint64_t numWarmupIters) noexcept;
 886      ANKERL_NANOBENCH(NODISCARD) uint64_t warmup() const noexcept;
 887  
 888      /**
 889       * @brief Marks the next run as the baseline.
 890       *
 891       * Call `relative(true)` to mark the run as the baseline. Successive runs will be compared to this run. It is calculated by
 892       *
 893       * @f[
 894       * 100\% * \frac{baseline}{runtime}
 895       * @f]
 896       *
 897       *  * 100% means it is exactly as fast as the baseline
 898       *  * >100% means it is faster than the baseline. E.g. 200% means the current run is twice as fast as the baseline.
 899       *  * <100% means it is slower than the baseline. E.g. 50% means it is twice as slow as the baseline.
 900       *
 901       * See the tutorial section "Comparing Results" for example usage.
 902       *
 903       * @param isRelativeEnabled True to enable processing
 904       */
 905      Bench& relative(bool isRelativeEnabled) noexcept;
 906      ANKERL_NANOBENCH(NODISCARD) bool relative() const noexcept;
 907  
 908      /**
 909       * @brief Enables/disables performance counters.
 910       *
 911       * On Linux nanobench has a powerful feature to use performance counters. This enables counting of retired instructions, count
 912       * number of branches, missed branches, etc. On default this is enabled, but you can disable it if you don't need that feature.
 913       *
 914       * @param showPerformanceCounters True to enable, false to disable.
 915       */
 916      Bench& performanceCounters(bool showPerformanceCounters) noexcept;
 917      ANKERL_NANOBENCH(NODISCARD) bool performanceCounters() const noexcept;
 918  
 919      /**
 920       * @brief Retrieves all benchmark results collected by the bench object so far.
 921       *
 922       * Each call to run() generates a Result that is stored within the Bench instance. This is mostly for advanced users who want to
 923       * see all the nitty gritty details.
 924       *
 925       * @return All results collected so far.
 926       */
 927      ANKERL_NANOBENCH(NODISCARD) std::vector<Result> const& results() const noexcept;
 928  
 929      /*!
 930        @verbatim embed:rst
 931  
 932        Convenience shortcut to :cpp:func:`ankerl::nanobench::doNotOptimizeAway`.
 933  
 934        @endverbatim
 935       */
 936      template <typename Arg>
 937      Bench& doNotOptimizeAway(Arg&& arg);
 938  
 939      /*!
 940        @verbatim embed:rst
 941  
 942        Sets N for asymptotic complexity calculation, so it becomes possible to calculate `Big O
 943        <https://en.wikipedia.org/wiki/Big_O_notation>`_ from multiple benchmark evaluations.
 944  
 945        Use :cpp:func:`ankerl::nanobench::Bench::complexityBigO` when the evaluation has finished. See the tutorial
 946        :ref:`asymptotic-complexity` for details.
 947  
 948        @endverbatim
 949  
 950        @tparam T Any type is cast to `double`.
 951        @param n Length of N for the next benchmark run, so it is possible to calculate `bigO`.
 952       */
 953      template <typename T>
 954      Bench& complexityN(T n) noexcept;
 955      ANKERL_NANOBENCH(NODISCARD) double complexityN() const noexcept;
 956  
 957      /*!
 958        Calculates [Big O](https://en.wikipedia.org/wiki/Big_O_notation>) of the results with all preconfigured complexity functions.
 959        Currently these complexity functions are fitted into the benchmark results:
 960  
 961         @f$ \mathcal{O}(1) @f$,
 962         @f$ \mathcal{O}(n) @f$,
 963         @f$ \mathcal{O}(\log{}n) @f$,
 964         @f$ \mathcal{O}(n\log{}n) @f$,
 965         @f$ \mathcal{O}(n^2) @f$,
 966         @f$ \mathcal{O}(n^3) @f$.
 967  
 968        If we e.g. evaluate the complexity of `std::sort`, this is the result of `std::cout << bench.complexityBigO()`:
 969  
 970        ```
 971        |   coefficient |   err% | complexity
 972        |--------------:|-------:|------------
 973        |   5.08935e-09 |   2.6% | O(n log n)
 974        |   6.10608e-08 |   8.0% | O(n)
 975        |   1.29307e-11 |  47.2% | O(n^2)
 976        |   2.48677e-15 |  69.6% | O(n^3)
 977        |   9.88133e-06 | 132.3% | O(log n)
 978        |   5.98793e-05 | 162.5% | O(1)
 979        ```
 980  
 981        So in this case @f$ \mathcal{O}(n\log{}n) @f$ provides the best approximation.
 982  
 983        @verbatim embed:rst
 984        See the tutorial :ref:`asymptotic-complexity` for details.
 985        @endverbatim
 986        @return Evaluation results, which can be printed or otherwise inspected.
 987       */
 988      std::vector<BigO> complexityBigO() const;
 989  
 990      /**
 991       * @brief Calculates bigO for a custom function.
 992       *
 993       * E.g. to calculate the mean squared error for @f$ \mathcal{O}(\log{}\log{}n) @f$, which is not part of the default set of
 994       * complexityBigO(), you can do this:
 995       *
 996       * ```
 997       * auto logLogN = bench.complexityBigO("O(log log n)", [](double n) {
 998       *     return std::log2(std::log2(n));
 999       * });
1000       * ```
1001       *
1002       * The resulting mean squared error can be printed with `std::cout << logLogN`. E.g. it prints something like this:
1003       *
1004       * ```text
1005       * 2.46985e-05 * O(log log n), rms=1.48121
1006       * ```
1007       *
1008       * @tparam Op Type of mapping operation.
1009       * @param name Name for the function, e.g. "O(log log n)"
1010       * @param op Op's operator() maps a `double` with the desired complexity function, e.g. `log2(log2(n))`.
1011       * @return BigO Error calculation, which is streamable to std::cout.
1012       */
1013      template <typename Op>
1014      BigO complexityBigO(char const* name, Op op) const;
1015  
1016      template <typename Op>
1017      BigO complexityBigO(std::string const& name, Op op) const;
1018  
1019      /*!
1020        @verbatim embed:rst
1021  
1022        Convenience shortcut to :cpp:func:`ankerl::nanobench::render`.
1023  
1024        @endverbatim
1025       */
1026      Bench& render(char const* templateContent, std::ostream& os);
1027      Bench& render(std::string const& templateContent, std::ostream& os);
1028  
1029      Bench& config(Config const& benchmarkConfig);
1030      ANKERL_NANOBENCH(NODISCARD) Config const& config() const noexcept;
1031  
1032      /**
1033       * @brief Configure an untimed setup step per epoch (forces single-iteration epochs).
1034       *
1035       * Example: `bench.setup(...).run(...);`
1036       */
1037      template <typename SetupOp>
1038      detail::SetupRunner<SetupOp> setup(SetupOp setupOp);
1039  
1040  private:
1041      template <typename SetupOp, typename Op>
1042      Bench& runImpl(SetupOp& setupOp, Op&& op);
1043  
1044      template <typename SetupOp>
1045      friend class detail::SetupRunner;
1046  
1047      Config mConfig{};
1048      std::vector<Result> mResults{};
1049  };
1050  ANKERL_NANOBENCH(IGNORE_PADDED_POP)
1051  
1052  /**
1053   * @brief Makes sure none of the given arguments are optimized away by the compiler.
1054   *
1055   * @tparam Arg Type of the argument that shouldn't be optimized away.
1056   * @param arg The input that we mark as being used, even though we don't do anything with it.
1057   */
1058  template <typename Arg>
1059  void doNotOptimizeAway(Arg&& arg);
1060  
1061  namespace detail {
1062  
1063  #if defined(_MSC_VER)
1064  void doNotOptimizeAwaySink(void const*);
1065  
1066  template <typename T>
1067  void doNotOptimizeAway(T const& val);
1068  
1069  #else
1070  
1071  // These assembly magic is directly from what Google Benchmark is doing. I have previously used what facebook's folly was doing, but
1072  // this seemed to have compilation problems in some cases. Google Benchmark seemed to be the most well tested anyways.
1073  // see https://github.com/google/benchmark/blob/v1.7.1/include/benchmark/benchmark.h#L443-L446
1074  template <typename T>
1075  void doNotOptimizeAway(T const& val) {
1076      // NOLINTNEXTLINE(hicpp-no-assembler)
1077      asm volatile("" : : "r,m"(val) : "memory");
1078  }
1079  
1080  template <typename T>
1081  void doNotOptimizeAway(T& val) {
1082  #    if defined(__clang__)
1083      // NOLINTNEXTLINE(hicpp-no-assembler)
1084      asm volatile("" : "+r,m"(val) : : "memory");
1085  #    else
1086      // NOLINTNEXTLINE(hicpp-no-assembler)
1087      asm volatile("" : "+m,r"(val) : : "memory");
1088  #    endif
1089  }
1090  #endif
1091  
1092  // internally used, but visible because run() is templated.
1093  // Not movable/copy-able, so we simply use a pointer instead of unique_ptr. This saves us from
1094  // having to include <memory>, and the template instantiation overhead of unique_ptr which is unfortunately quite significant.
1095  ANKERL_NANOBENCH(IGNORE_EFFCPP_PUSH)
1096  class IterationLogic {
1097  public:
1098      explicit IterationLogic(Bench const& bench);
1099      IterationLogic(IterationLogic&&) = delete;
1100      IterationLogic& operator=(IterationLogic&&) = delete;
1101      IterationLogic(IterationLogic const&) = delete;
1102      IterationLogic& operator=(IterationLogic const&) = delete;
1103      ~IterationLogic();
1104  
1105      ANKERL_NANOBENCH(NODISCARD) uint64_t numIters() const noexcept;
1106      void add(std::chrono::nanoseconds elapsed, PerformanceCounters const& pc) noexcept;
1107      void moveResultTo(std::vector<Result>& results) noexcept;
1108  
1109  private:
1110      struct Impl;
1111      Impl* mPimpl;
1112  };
1113  ANKERL_NANOBENCH(IGNORE_EFFCPP_POP)
1114  
1115  ANKERL_NANOBENCH(IGNORE_PADDED_PUSH)
1116  class PerformanceCounters {
1117  public:
1118      PerformanceCounters(PerformanceCounters const&) = delete;
1119      PerformanceCounters(PerformanceCounters&&) = delete;
1120      PerformanceCounters& operator=(PerformanceCounters const&) = delete;
1121      PerformanceCounters& operator=(PerformanceCounters&&) = delete;
1122  
1123      PerformanceCounters();
1124      ~PerformanceCounters();
1125  
1126      void beginMeasure();
1127      void endMeasure();
1128      void updateResults(uint64_t numIters);
1129  
1130      ANKERL_NANOBENCH(NODISCARD) PerfCountSet<uint64_t> const& val() const noexcept;
1131      ANKERL_NANOBENCH(NODISCARD) PerfCountSet<bool> const& has() const noexcept;
1132  
1133  private:
1134  #if ANKERL_NANOBENCH(PERF_COUNTERS)
1135      LinuxPerformanceCounters* mPc = nullptr;
1136  #endif
1137      PerfCountSet<uint64_t> mVal{};
1138      PerfCountSet<bool> mHas{};
1139  };
1140  ANKERL_NANOBENCH(IGNORE_PADDED_POP)
1141  
1142  // Gets the singleton
1143  PerformanceCounters& performanceCounters();
1144  
1145  } // namespace detail
1146  
1147  class BigO {
1148  public:
1149      using RangeMeasure = std::vector<std::pair<double, double>>;
1150  
1151      template <typename Op>
1152      static RangeMeasure mapRangeMeasure(RangeMeasure data, Op op) {
1153          for (auto& rangeMeasure : data) {
1154              rangeMeasure.first = op(rangeMeasure.first);
1155          }
1156          return data;
1157      }
1158  
1159      static RangeMeasure collectRangeMeasure(std::vector<Result> const& results);
1160  
1161      template <typename Op>
1162      BigO(char const* bigOName, RangeMeasure const& rangeMeasure, Op rangeToN)
1163          : BigO(bigOName, mapRangeMeasure(rangeMeasure, rangeToN)) {}
1164  
1165      template <typename Op>
1166      BigO(std::string bigOName, RangeMeasure const& rangeMeasure, Op rangeToN)
1167          : BigO(std::move(bigOName), mapRangeMeasure(rangeMeasure, rangeToN)) {}
1168  
1169      BigO(char const* bigOName, RangeMeasure const& scaledRangeMeasure);
1170      BigO(std::string bigOName, RangeMeasure const& scaledRangeMeasure);
1171      ANKERL_NANOBENCH(NODISCARD) std::string const& name() const noexcept;
1172      ANKERL_NANOBENCH(NODISCARD) double constant() const noexcept;
1173      ANKERL_NANOBENCH(NODISCARD) double normalizedRootMeanSquare() const noexcept;
1174      ANKERL_NANOBENCH(NODISCARD) bool operator<(BigO const& other) const noexcept;
1175  
1176  private:
1177      std::string mName{};
1178      double mConstant{};
1179      double mNormalizedRootMeanSquare{};
1180  };
1181  std::ostream& operator<<(std::ostream& os, BigO const& bigO);
1182  std::ostream& operator<<(std::ostream& os, std::vector<ankerl::nanobench::BigO> const& bigOs);
1183  
1184  } // namespace nanobench
1185  } // namespace ankerl
1186  
1187  // implementation /////////////////////////////////////////////////////////////////////////////////
1188  
1189  namespace ankerl {
1190  namespace nanobench {
1191  
1192  constexpr uint64_t(Rng::min)() {
1193      return 0;
1194  }
1195  
1196  constexpr uint64_t(Rng::max)() {
1197      return (std::numeric_limits<uint64_t>::max)();
1198  }
1199  
1200  ANKERL_NANOBENCH_NO_SANITIZE("integer", "undefined")
1201  uint64_t Rng::operator()() noexcept {
1202      auto x = mX;
1203  
1204      mX = UINT64_C(15241094284759029579) * mY;
1205      mY = rotl(mY - x, 27);
1206  
1207      return x;
1208  }
1209  
1210  ANKERL_NANOBENCH_NO_SANITIZE("integer", "undefined")
1211  uint32_t Rng::bounded(uint32_t range) noexcept {
1212      uint64_t const r32 = static_cast<uint32_t>(operator()());
1213      auto multiresult = r32 * range;
1214      return static_cast<uint32_t>(multiresult >> 32U);
1215  }
1216  
1217  double Rng::uniform01() noexcept {
1218      auto i = (UINT64_C(0x3ff) << 52U) | (operator()() >> 12U);
1219      // can't use union in c++ here for type puning, it's undefined behavior.
1220      // std::memcpy is optimized anyways.
1221      double d{};
1222      std::memcpy(&d, &i, sizeof(double));
1223      return d - 1.0;
1224  }
1225  
1226  template <typename Container>
1227  void Rng::shuffle(Container& container) noexcept {
1228      auto i = container.size();
1229      while (i > 1U) {
1230          using std::swap;
1231          auto n = operator()();
1232          // using decltype(i) instead of size_t to be compatible to containers with 32bit index (see #80)
1233          auto b1 = static_cast<decltype(i)>((static_cast<uint32_t>(n) * static_cast<uint64_t>(i)) >> 32U);
1234          swap(container[--i], container[b1]);
1235  
1236          auto b2 = static_cast<decltype(i)>(((n >> 32U) * static_cast<uint64_t>(i)) >> 32U);
1237          swap(container[--i], container[b2]);
1238      }
1239  }
1240  
1241  ANKERL_NANOBENCH_NO_SANITIZE("integer", "undefined")
1242  constexpr uint64_t Rng::rotl(uint64_t x, unsigned k) noexcept {
1243      return (x << k) | (x >> (64U - k));
1244  }
1245  
1246  namespace detail {
1247  
1248  template <typename SetupOp>
1249  class SetupRunner {
1250  public:
1251      explicit SetupRunner(SetupOp setupOp, Bench& bench)
1252          : mSetupOp(std::move(setupOp))
1253          , mBench(bench) {}
1254  
1255      template <typename Op>
1256      ANKERL_NANOBENCH_NO_SANITIZE("integer")
1257      Bench& run(Op&& op) {
1258          assert((mBench.epochIterations() <= 1) &&
1259                 "setup() runs once per epoch, not once per iteration; it requires epochIterations(1)");
1260          mBench.epochIterations(1);
1261          return mBench.runImpl(mSetupOp, std::forward<Op>(op));
1262      }
1263  
1264  private:
1265      SetupOp mSetupOp;
1266      Bench& mBench;
1267  };
1268  } // namespace detail
1269  
1270  template <typename Op>
1271  ANKERL_NANOBENCH_NO_SANITIZE("integer")
1272  Bench& Bench::run(Op&& op) {
1273      auto setupOp = [] {};
1274      return runImpl(setupOp, std::forward<Op>(op));
1275  }
1276  
1277  template <typename SetupOp, typename Op>
1278  ANKERL_NANOBENCH_NO_SANITIZE("integer")
1279  Bench& Bench::runImpl(SetupOp& setupOp, Op&& op) {
1280      // It is important that this method is kept short so the compiler can do better optimizations/ inlining of op()
1281      detail::IterationLogic iterationLogic(*this);
1282      auto& pc = detail::performanceCounters();
1283  
1284      while (auto n = iterationLogic.numIters()) {
1285          setupOp();
1286  
1287          pc.beginMeasure();
1288          Clock::time_point const before = Clock::now();
1289          while (n-- > 0) {
1290              op();
1291          }
1292          Clock::time_point const after = Clock::now();
1293          pc.endMeasure();
1294          pc.updateResults(iterationLogic.numIters());
1295          iterationLogic.add(after - before, pc);
1296      }
1297      iterationLogic.moveResultTo(mResults);
1298      return *this;
1299  }
1300  
1301  template <typename SetupOp>
1302  detail::SetupRunner<SetupOp> Bench::setup(SetupOp setupOp) {
1303      return detail::SetupRunner<SetupOp>(std::move(setupOp), *this);
1304  }
1305  
1306  // Performs all evaluations.
1307  template <typename Op>
1308  Bench& Bench::run(char const* benchmarkName, Op&& op) {
1309      name(benchmarkName);
1310      return run(std::forward<Op>(op));
1311  }
1312  
1313  template <typename Op>
1314  Bench& Bench::run(std::string const& benchmarkName, Op&& op) {
1315      name(benchmarkName);
1316      return run(std::forward<Op>(op));
1317  }
1318  
1319  template <typename Op>
1320  BigO Bench::complexityBigO(char const* benchmarkName, Op op) const {
1321      return BigO(benchmarkName, BigO::collectRangeMeasure(mResults), op);
1322  }
1323  
1324  template <typename Op>
1325  BigO Bench::complexityBigO(std::string const& benchmarkName, Op op) const {
1326      return BigO(benchmarkName, BigO::collectRangeMeasure(mResults), op);
1327  }
1328  
1329  // Set the batch size, e.g. number of processed bytes, or some other metric for the size of the processed data in each iteration.
1330  // Any argument is cast to double.
1331  template <typename T>
1332  Bench& Bench::batch(T b) noexcept {
1333      mConfig.mBatch = static_cast<double>(b);
1334      return *this;
1335  }
1336  
1337  // Sets the computation complexity of the next run. Any argument is cast to double.
1338  template <typename T>
1339  Bench& Bench::complexityN(T n) noexcept {
1340      mConfig.mComplexityN = static_cast<double>(n);
1341      return *this;
1342  }
1343  
1344  // Convenience: makes sure none of the given arguments are optimized away by the compiler.
1345  template <typename Arg>
1346  Bench& Bench::doNotOptimizeAway(Arg&& arg) {
1347      detail::doNotOptimizeAway(std::forward<Arg>(arg));
1348      return *this;
1349  }
1350  
1351  // Makes sure none of the given arguments are optimized away by the compiler.
1352  template <typename Arg>
1353  void doNotOptimizeAway(Arg&& arg) {
1354      detail::doNotOptimizeAway(std::forward<Arg>(arg));
1355  }
1356  
1357  namespace detail {
1358  
1359  #if defined(_MSC_VER)
1360  template <typename T>
1361  void doNotOptimizeAway(T const& val) {
1362      doNotOptimizeAwaySink(&val);
1363  }
1364  
1365  #endif
1366  
1367  } // namespace detail
1368  } // namespace nanobench
1369  } // namespace ankerl
1370  
1371  #if defined(ANKERL_NANOBENCH_IMPLEMENT)
1372  
1373  ///////////////////////////////////////////////////////////////////////////////////////////////////
1374  // implementation part - only visible in .cpp
1375  ///////////////////////////////////////////////////////////////////////////////////////////////////
1376  
1377  #    include <algorithm> // sort, reverse
1378  #    include <cmath>
1379  #    include <compare>
1380  #    include <cstdlib>   // getenv
1381  #    include <fstream>   // ifstream to parse proc files
1382  #    include <functional>
1383  #    include <iomanip>   // setw, setprecision
1384  #    include <iostream>  // cout
1385  #    include <iterator>
1386  #    include <locale>
1387  #    include <numeric>   // accumulate
1388  #    include <random>    // random_device
1389  #    include <sstream>   // to_s in Number
1390  #    include <stdexcept> // throw for rendering templates
1391  #    include <tuple>     // std::tie
1392  #    if defined(__linux__)
1393  #        include <unistd.h> //sysconf
1394  #    endif
1395  #    if ANKERL_NANOBENCH(PERF_COUNTERS)
1396  #        include <map> // map
1397  
1398  #        include <linux/perf_event.h>
1399  #        include <sys/ioctl.h>
1400  #        include <sys/syscall.h>
1401  #        include <sys/types.h>
1402  #    endif
1403  
1404  // declarations ///////////////////////////////////////////////////////////////////////////////////
1405  
1406  // As definitions follow these declarations within this
1407  // header, IWYU considers some of them redundant and
1408  // suggests removing them. Disable this IWYU behavior.
1409  // IWYU pragma: begin_keep
1410  
1411  namespace ankerl {
1412  namespace nanobench {
1413  
1414  // helper stuff that is only intended to be used internally
1415  namespace detail {
1416  
1417  // formatting utilities
1418  namespace fmt {
1419  
1420  class NumSep;
1421  class StreamStateRestorer;
1422  class Number;
1423  class MarkDownColumn;
1424  class MarkDownCode;
1425  
1426  } // namespace fmt
1427  } // namespace detail
1428  } // namespace nanobench
1429  } // namespace ankerl
1430  
1431  // IWYU pragma: end_keep
1432  
1433  // definitions ////////////////////////////////////////////////////////////////////////////////////
1434  
1435  namespace ankerl {
1436  namespace nanobench {
1437  
1438  uint64_t splitMix64(uint64_t& state) noexcept;
1439  
1440  namespace detail {
1441  
1442  // helpers to get double values
1443  template <typename T>
1444  inline double d(T t) noexcept {
1445      return static_cast<double>(t);
1446  }
1447  inline double d(Clock::duration duration) noexcept {
1448      return std::chrono::duration_cast<std::chrono::duration<double>>(duration).count();
1449  }
1450  
1451  // Calculates clock resolution once, and remembers the result
1452  inline Clock::duration clockResolution() noexcept;
1453  
1454  } // namespace detail
1455  
1456  namespace templates {
1457  
1458  char const* csv() noexcept {
1459      return R"DELIM("title";"name";"unit";"batch";"elapsed";"error %";"instructions";"branches";"branch misses";"total"
1460  {{#result}}"{{title}}";"{{name}}";"{{unit}}";{{batch}};{{median(elapsed)}};{{medianAbsolutePercentError(elapsed)}};{{median(instructions)}};{{median(branchinstructions)}};{{median(branchmisses)}};{{sumProduct(iterations, elapsed)}}
1461  {{/result}})DELIM";
1462  }
1463  
1464  char const* htmlBoxplot() noexcept {
1465      return R"DELIM(<html>
1466  
1467  <head>
1468      <script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
1469  </head>
1470  
1471  <body>
1472      <div id="myDiv"></div>
1473      <script>
1474          var data = [
1475              {{#result}}{
1476                  name: '{{name}}',
1477                  y: [{{#measurement}}{{elapsed}}{{^-last}}, {{/last}}{{/measurement}}],
1478              },
1479              {{/result}}
1480          ];
1481          var title = '{{title}}';
1482  
1483          data = data.map(a => Object.assign(a, { boxpoints: 'all', pointpos: 0, type: 'box' }));
1484          var layout = { title: { text: title }, showlegend: false, yaxis: { title: 'time per unit', rangemode: 'tozero', autorange: true } }; Plotly.newPlot('myDiv', data, layout, {responsive: true});
1485      </script>
1486  </body>
1487  
1488  </html>)DELIM";
1489  }
1490  
1491  char const* pyperf() noexcept {
1492      return R"DELIM({
1493      "benchmarks": [
1494          {
1495              "runs": [
1496                  {
1497                      "values": [
1498  {{#measurement}}                        {{elapsed}}{{^-last}},
1499  {{/last}}{{/measurement}}
1500                      ]
1501                  }
1502              ]
1503          }
1504      ],
1505      "metadata": {
1506          "loops": {{sum(iterations)}},
1507          "inner_loops": {{batch}},
1508          "name": "{{title}}",
1509          "unit": "second"
1510      },
1511      "version": "1.0"
1512  })DELIM";
1513  }
1514  
1515  char const* json() noexcept {
1516      return R"DELIM({
1517      "results": [
1518  {{#result}}        {
1519              "title": "{{title}}",
1520              "name": "{{name}}",
1521              "unit": "{{unit}}",
1522              "batch": {{batch}},
1523              "complexityN": {{complexityN}},
1524              "epochs": {{epochs}},
1525              "clockResolution": {{clockResolution}},
1526              "clockResolutionMultiple": {{clockResolutionMultiple}},
1527              "maxEpochTime": {{maxEpochTime}},
1528              "minEpochTime": {{minEpochTime}},
1529              "minEpochIterations": {{minEpochIterations}},
1530              "epochIterations": {{epochIterations}},
1531              "warmup": {{warmup}},
1532              "relative": {{relative}},
1533              "median(elapsed)": {{median(elapsed)}},
1534              "medianAbsolutePercentError(elapsed)": {{medianAbsolutePercentError(elapsed)}},
1535              "median(instructions)": {{median(instructions)}},
1536              "medianAbsolutePercentError(instructions)": {{medianAbsolutePercentError(instructions)}},
1537              "median(cpucycles)": {{median(cpucycles)}},
1538              "median(contextswitches)": {{median(contextswitches)}},
1539              "median(pagefaults)": {{median(pagefaults)}},
1540              "median(branchinstructions)": {{median(branchinstructions)}},
1541              "median(branchmisses)": {{median(branchmisses)}},
1542              "totalTime": {{sumProduct(iterations, elapsed)}},
1543              "measurements": [
1544  {{#measurement}}                {
1545                      "iterations": {{iterations}},
1546                      "elapsed": {{elapsed}},
1547                      "pagefaults": {{pagefaults}},
1548                      "cpucycles": {{cpucycles}},
1549                      "contextswitches": {{contextswitches}},
1550                      "instructions": {{instructions}},
1551                      "branchinstructions": {{branchinstructions}},
1552                      "branchmisses": {{branchmisses}}
1553                  }{{^-last}},{{/-last}}
1554  {{/measurement}}            ]
1555          }{{^-last}},{{/-last}}
1556  {{/result}}    ]
1557  })DELIM";
1558  }
1559  
1560  ANKERL_NANOBENCH(IGNORE_PADDED_PUSH)
1561  struct Node {
1562      enum class Type { tag, content, section, inverted_section };
1563  
1564      char const* begin;
1565      char const* end;
1566      std::vector<Node> children;
1567      Type type;
1568  
1569      template <size_t N>
1570      // NOLINTNEXTLINE(hicpp-avoid-c-arrays,modernize-avoid-c-arrays,cppcoreguidelines-avoid-c-arrays)
1571      bool operator==(char const (&str)[N]) const noexcept {
1572          // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
1573          return static_cast<size_t>(std::distance(begin, end) + 1) == N && 0 == strncmp(str, begin, N - 1);
1574      }
1575  };
1576  ANKERL_NANOBENCH(IGNORE_PADDED_POP)
1577  
1578  // NOLINTNEXTLINE(misc-no-recursion)
1579  static std::vector<Node> parseMustacheTemplate(char const** tpl) {
1580      std::vector<Node> nodes;
1581  
1582      while (true) {
1583          auto const* begin = std::strstr(*tpl, "{{");
1584          auto const* end = begin;
1585          if (begin != nullptr) {
1586              // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
1587              begin += 2;
1588              end = std::strstr(begin, "}}");
1589          }
1590  
1591          if (begin == nullptr || end == nullptr) {
1592              // nothing found, finish node
1593              // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
1594              nodes.emplace_back(Node{*tpl, *tpl + std::strlen(*tpl), std::vector<Node>{}, Node::Type::content});
1595              return nodes;
1596          }
1597  
1598          // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
1599          nodes.emplace_back(Node{*tpl, begin - 2, std::vector<Node>{}, Node::Type::content});
1600  
1601          // we found a tag
1602          // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
1603          *tpl = end + 2;
1604          switch (*begin) {
1605          case '/':
1606              // finished! bail out
1607              return nodes;
1608  
1609          case '#':
1610              // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
1611              nodes.emplace_back(Node{begin + 1, end, parseMustacheTemplate(tpl), Node::Type::section});
1612              break;
1613  
1614          case '^':
1615              // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
1616              nodes.emplace_back(Node{begin + 1, end, parseMustacheTemplate(tpl), Node::Type::inverted_section});
1617              break;
1618  
1619          default:
1620              nodes.emplace_back(Node{begin, end, std::vector<Node>{}, Node::Type::tag});
1621              break;
1622          }
1623      }
1624  }
1625  
1626  static bool generateFirstLast(Node const& n, size_t idx, size_t size, std::ostream& out) {
1627      ANKERL_NANOBENCH_LOG("n.type=" << static_cast<int>(n.type));
1628      bool const matchFirst = n == "-first";
1629      bool const matchLast = n == "-last";
1630      if (!matchFirst && !matchLast) {
1631          return false;
1632      }
1633  
1634      bool doWrite = false;
1635      if (n.type == Node::Type::section) {
1636          doWrite = (matchFirst && idx == 0) || (matchLast && idx == size - 1);
1637      } else if (n.type == Node::Type::inverted_section) {
1638          doWrite = (matchFirst && idx != 0) || (matchLast && idx != size - 1);
1639      }
1640  
1641      if (doWrite) {
1642          for (auto const& child : n.children) {
1643              if (child.type == Node::Type::content) {
1644                  out.write(child.begin, std::distance(child.begin, child.end));
1645              }
1646          }
1647      }
1648      return true;
1649  }
1650  
1651  static bool matchCmdArgs(std::string const& str, std::vector<std::string>& matchResult) {
1652      matchResult.clear();
1653      auto idxOpen = str.find('(');
1654      auto idxClose = str.find(')', idxOpen);
1655      if (idxClose == std::string::npos) {
1656          return false;
1657      }
1658  
1659      matchResult.emplace_back(str.substr(0, idxOpen));
1660  
1661      // split by comma
1662      matchResult.emplace_back();
1663      for (size_t i = idxOpen + 1; i != idxClose; ++i) {
1664          if (str[i] == ' ' || str[i] == '\t') {
1665              // skip whitespace
1666              continue;
1667          }
1668          if (str[i] == ',') {
1669              // got a comma => new string
1670              matchResult.emplace_back();
1671              continue;
1672          }
1673          // no whitespace no comma, append
1674          matchResult.back() += str[i];
1675      }
1676      return true;
1677  }
1678  
1679  static bool generateConfigTag(Node const& n, Config const& config, std::ostream& out) {
1680      using detail::d;
1681  
1682      if (n == "title") {
1683          out << config.mBenchmarkTitle;
1684          return true;
1685      }
1686      if (n == "name") {
1687          out << config.mBenchmarkName;
1688          return true;
1689      }
1690      if (n == "unit") {
1691          out << config.mUnit;
1692          return true;
1693      }
1694      if (n == "batch") {
1695          out << config.mBatch;
1696          return true;
1697      }
1698      if (n == "complexityN") {
1699          out << config.mComplexityN;
1700          return true;
1701      }
1702      if (n == "epochs") {
1703          out << config.mNumEpochs;
1704          return true;
1705      }
1706      if (n == "clockResolution") {
1707          out << d(detail::clockResolution());
1708          return true;
1709      }
1710      if (n == "clockResolutionMultiple") {
1711          out << config.mClockResolutionMultiple;
1712          return true;
1713      }
1714      if (n == "maxEpochTime") {
1715          out << d(config.mMaxEpochTime);
1716          return true;
1717      }
1718      if (n == "minEpochTime") {
1719          out << d(config.mMinEpochTime);
1720          return true;
1721      }
1722      if (n == "minEpochIterations") {
1723          out << config.mMinEpochIterations;
1724          return true;
1725      }
1726      if (n == "epochIterations") {
1727          out << config.mEpochIterations;
1728          return true;
1729      }
1730      if (n == "warmup") {
1731          out << config.mWarmup;
1732          return true;
1733      }
1734      if (n == "relative") {
1735          out << config.mIsRelative;
1736          return true;
1737      }
1738      return false;
1739  }
1740  
1741  // NOLINTNEXTLINE(readability-function-cognitive-complexity)
1742  static std::ostream& generateResultTag(Node const& n, Result const& r, std::ostream& out) {
1743      if (generateConfigTag(n, r.config(), out)) {
1744          return out;
1745      }
1746      // match e.g. "median(elapsed)"
1747      // g++ 4.8 doesn't implement std::regex :(
1748      // static std::regex const regOpArg1("^([a-zA-Z]+)\\(([a-zA-Z]*)\\)$");
1749      // std::cmatch matchResult;
1750      // if (std::regex_match(n.begin, n.end, matchResult, regOpArg1)) {
1751      std::vector<std::string> matchResult;
1752      if (matchCmdArgs(std::string(n.begin, n.end), matchResult)) {
1753          if (matchResult.size() == 2) {
1754              if (matchResult[0] == "context") {
1755                  return out << r.context(matchResult[1]);
1756              }
1757  
1758              auto m = Result::fromString(matchResult[1]);
1759              if (m == Result::Measure::_size) {
1760                  return out << 0.0;
1761              }
1762  
1763              if (matchResult[0] == "median") {
1764                  return out << r.median(m);
1765              }
1766              if (matchResult[0] == "average") {
1767                  return out << r.average(m);
1768              }
1769              if (matchResult[0] == "medianAbsolutePercentError") {
1770                  return out << r.medianAbsolutePercentError(m);
1771              }
1772              if (matchResult[0] == "sum") {
1773                  return out << r.sum(m);
1774              }
1775              if (matchResult[0] == "minimum") {
1776                  return out << r.minimum(m);
1777              }
1778              if (matchResult[0] == "maximum") {
1779                  return out << r.maximum(m);
1780              }
1781          } else if (matchResult.size() == 3) {
1782              auto m1 = Result::fromString(matchResult[1]);
1783              auto m2 = Result::fromString(matchResult[2]);
1784              if (m1 == Result::Measure::_size || m2 == Result::Measure::_size) {
1785                  return out << 0.0;
1786              }
1787  
1788              if (matchResult[0] == "sumProduct") {
1789                  return out << r.sumProduct(m1, m2);
1790              }
1791          }
1792      }
1793  
1794      // match e.g. "sumProduct(elapsed, iterations)"
1795      // static std::regex const regOpArg2("^([a-zA-Z]+)\\(([a-zA-Z]*)\\s*,\\s+([a-zA-Z]*)\\)$");
1796  
1797      // nothing matches :(
1798      throw std::runtime_error("command '" + std::string(n.begin, n.end) + "' not understood");
1799  }
1800  
1801  static void generateResultMeasurement(std::vector<Node> const& nodes, size_t idx, Result const& r, std::ostream& out) {
1802      for (auto const& n : nodes) {
1803          if (!generateFirstLast(n, idx, r.size(), out)) {
1804              ANKERL_NANOBENCH_LOG("n.type=" << static_cast<int>(n.type));
1805              switch (n.type) {
1806              case Node::Type::content:
1807                  out.write(n.begin, std::distance(n.begin, n.end));
1808                  break;
1809  
1810              case Node::Type::inverted_section:
1811                  throw std::runtime_error("got a inverted section inside measurement");
1812  
1813              case Node::Type::section:
1814                  throw std::runtime_error("got a section inside measurement");
1815  
1816              case Node::Type::tag: {
1817                  auto m = Result::fromString(std::string(n.begin, n.end));
1818                  if (m == Result::Measure::_size || !r.has(m)) {
1819                      out << 0.0;
1820                  } else {
1821                      out << r.get(idx, m);
1822                  }
1823                  break;
1824              }
1825              }
1826          }
1827      }
1828  }
1829  
1830  static void generateResult(std::vector<Node> const& nodes, size_t idx, std::vector<Result> const& results, std::ostream& out) {
1831      auto const& r = results[idx];
1832      for (auto const& n : nodes) {
1833          if (!generateFirstLast(n, idx, results.size(), out)) {
1834              ANKERL_NANOBENCH_LOG("n.type=" << static_cast<int>(n.type));
1835              switch (n.type) {
1836              case Node::Type::content:
1837                  out.write(n.begin, std::distance(n.begin, n.end));
1838                  break;
1839  
1840              case Node::Type::inverted_section:
1841                  throw std::runtime_error("got a inverted section inside result");
1842  
1843              case Node::Type::section:
1844                  if (n == "measurement") {
1845                      for (size_t i = 0; i < r.size(); ++i) {
1846                          generateResultMeasurement(n.children, i, r, out);
1847                      }
1848                  } else {
1849                      throw std::runtime_error("got a section inside result");
1850                  }
1851                  break;
1852  
1853              case Node::Type::tag:
1854                  generateResultTag(n, r, out);
1855                  break;
1856              }
1857          }
1858      }
1859  }
1860  
1861  } // namespace templates
1862  
1863  // helper stuff that only intended to be used internally
1864  namespace detail {
1865  
1866  char const* getEnv(char const* name);
1867  bool isEndlessRunning(std::string const& name);
1868  bool isWarningsEnabled();
1869  
1870  template <typename T>
1871  T parseFile(std::string const& filename, bool* fail);
1872  
1873  void gatherStabilityInformation(std::vector<std::string>& warnings, std::vector<std::string>& recommendations);
1874  void printStabilityInformationOnce(std::ostream* outStream);
1875  
1876  // remembers the last table settings used. When it changes, a new table header is automatically written for the new entry.
1877  uint64_t& singletonHeaderHash() noexcept;
1878  
1879  // determines resolution of the given clock. This is done by measuring multiple times and returning the minimum time difference.
1880  Clock::duration calcClockResolution(size_t numEvaluations) noexcept;
1881  
1882  // formatting utilities
1883  namespace fmt {
1884  
1885  // adds thousands separator to numbers
1886  ANKERL_NANOBENCH(IGNORE_PADDED_PUSH)
1887  class NumSep : public std::numpunct<char> {
1888  public:
1889      explicit NumSep(char sep);
1890      char do_thousands_sep() const override;
1891      std::string do_grouping() const override;
1892  
1893  private:
1894      char mSep;
1895  };
1896  ANKERL_NANOBENCH(IGNORE_PADDED_POP)
1897  
1898  // RAII to save & restore a stream's state
1899  ANKERL_NANOBENCH(IGNORE_PADDED_PUSH)
1900  class StreamStateRestorer {
1901  public:
1902      explicit StreamStateRestorer(std::ostream& s);
1903      ~StreamStateRestorer();
1904  
1905      // sets back all stream info that we remembered at construction
1906      void restore();
1907  
1908      // don't allow copying / moving
1909      StreamStateRestorer(StreamStateRestorer const&) = delete;
1910      StreamStateRestorer& operator=(StreamStateRestorer const&) = delete;
1911      StreamStateRestorer(StreamStateRestorer&&) = delete;
1912      StreamStateRestorer& operator=(StreamStateRestorer&&) = delete;
1913  
1914  private:
1915      std::ostream& mStream;
1916      std::locale mLocale;
1917      std::streamsize const mPrecision;
1918      std::streamsize const mWidth;
1919      std::ostream::char_type const mFill;
1920      std::ostream::fmtflags const mFmtFlags;
1921  };
1922  ANKERL_NANOBENCH(IGNORE_PADDED_POP)
1923  
1924  // Number formatter
1925  class Number {
1926  public:
1927      Number(int width, int precision, double value);
1928      Number(int width, int precision, int64_t value);
1929      ANKERL_NANOBENCH(NODISCARD) std::string to_s() const;
1930  
1931  private:
1932      friend std::ostream& operator<<(std::ostream& os, Number const& n);
1933      std::ostream& write(std::ostream& os) const;
1934  
1935      int mWidth;
1936      int mPrecision;
1937      double mValue;
1938  };
1939  
1940  // helper replacement for std::to_string of signed/unsigned numbers so we are locale independent
1941  std::string to_s(uint64_t n);
1942  
1943  std::ostream& operator<<(std::ostream& os, Number const& n);
1944  
1945  class MarkDownColumn {
1946  public:
1947      MarkDownColumn(int w, int prec, std::string tit, std::string suff, double val) noexcept;
1948      ANKERL_NANOBENCH(NODISCARD) std::string title() const;
1949      ANKERL_NANOBENCH(NODISCARD) std::string separator() const;
1950      ANKERL_NANOBENCH(NODISCARD) std::string invalid() const;
1951      ANKERL_NANOBENCH(NODISCARD) std::string value() const;
1952  
1953  private:
1954      int mWidth;
1955      int mPrecision;
1956      std::string mTitle;
1957      std::string mSuffix;
1958      double mValue;
1959  };
1960  
1961  // Formats any text as markdown code, escaping backticks.
1962  class MarkDownCode {
1963  public:
1964      explicit MarkDownCode(std::string const& what);
1965  
1966  private:
1967      friend std::ostream& operator<<(std::ostream& os, MarkDownCode const& mdCode);
1968      std::ostream& write(std::ostream& os) const;
1969  
1970      std::string mWhat{};
1971  };
1972  
1973  std::ostream& operator<<(std::ostream& os, MarkDownCode const& mdCode);
1974  
1975  } // namespace fmt
1976  } // namespace detail
1977  } // namespace nanobench
1978  } // namespace ankerl
1979  
1980  // implementation /////////////////////////////////////////////////////////////////////////////////
1981  
1982  namespace ankerl {
1983  namespace nanobench {
1984  
1985  // NOLINTNEXTLINE(readability-function-cognitive-complexity)
1986  void render(char const* mustacheTemplate, std::vector<Result> const& results, std::ostream& out) {
1987      detail::fmt::StreamStateRestorer const restorer(out);
1988  
1989      out.precision(std::numeric_limits<double>::digits10);
1990      auto nodes = templates::parseMustacheTemplate(&mustacheTemplate);
1991  
1992      for (auto const& n : nodes) {
1993          ANKERL_NANOBENCH_LOG("n.type=" << static_cast<int>(n.type));
1994          switch (n.type) {
1995          case templates::Node::Type::content:
1996              out.write(n.begin, std::distance(n.begin, n.end));
1997              break;
1998  
1999          case templates::Node::Type::inverted_section:
2000              throw std::runtime_error("unknown list '" + std::string(n.begin, n.end) + "'");
2001  
2002          case templates::Node::Type::section:
2003              if (n == "result") {
2004                  const size_t nbResults = results.size();
2005                  for (size_t i = 0; i < nbResults; ++i) {
2006                      generateResult(n.children, i, results, out);
2007                  }
2008              } else if (n == "measurement") {
2009                  if (results.size() != 1) {
2010                      throw std::runtime_error(
2011                          "render: can only use section 'measurement' here if there is a single result, but there are " +
2012                          detail::fmt::to_s(results.size()));
2013                  }
2014                  // when we only have a single result, we can immediately go into its measurement.
2015                  auto const& r = results.front();
2016                  for (size_t i = 0; i < r.size(); ++i) {
2017                      generateResultMeasurement(n.children, i, r, out);
2018                  }
2019              } else {
2020                  throw std::runtime_error("render: unknown section '" + std::string(n.begin, n.end) + "'");
2021              }
2022              break;
2023  
2024          case templates::Node::Type::tag:
2025              if (results.size() == 1) {
2026                  // result & config are both supported there
2027                  generateResultTag(n, results.front(), out);
2028              } else {
2029                  // This just uses the last result's config.
2030                  if (!generateConfigTag(n, results.back().config(), out)) {
2031                      throw std::runtime_error("unknown tag '" + std::string(n.begin, n.end) + "'");
2032                  }
2033              }
2034              break;
2035          }
2036      }
2037  }
2038  
2039  void render(std::string const& mustacheTemplate, std::vector<Result> const& results, std::ostream& out) {
2040      render(mustacheTemplate.c_str(), results, out);
2041  }
2042  
2043  void render(char const* mustacheTemplate, const Bench& bench, std::ostream& out) {
2044      render(mustacheTemplate, bench.results(), out);
2045  }
2046  
2047  void render(std::string const& mustacheTemplate, const Bench& bench, std::ostream& out) {
2048      render(mustacheTemplate.c_str(), bench.results(), out);
2049  }
2050  
2051  namespace detail {
2052  
2053  PerformanceCounters& performanceCounters() {
2054  #    if defined(__clang__)
2055  #        pragma clang diagnostic push
2056  #        pragma clang diagnostic ignored "-Wexit-time-destructors"
2057  #    endif
2058      static PerformanceCounters pc;
2059  #    if defined(__clang__)
2060  #        pragma clang diagnostic pop
2061  #    endif
2062      return pc;
2063  }
2064  
2065  // Windows version of doNotOptimizeAway
2066  // see https://github.com/google/benchmark/blob/v1.7.1/include/benchmark/benchmark.h#L514
2067  // see https://github.com/facebook/folly/blob/v2023.01.30.00/folly/lang/Hint-inl.h#L54-L58
2068  // see https://learn.microsoft.com/en-us/cpp/preprocessor/optimize
2069  #    if defined(_MSC_VER)
2070  #        pragma optimize("", off)
2071  void doNotOptimizeAwaySink(void const*) {}
2072  #        pragma optimize("", on)
2073  #    endif
2074  
2075  template <typename T>
2076  T parseFile(std::string const& filename, bool* fail) {
2077      std::ifstream fin(filename); // NOLINT(misc-const-correctness)
2078      T num{};
2079      fin >> num;
2080      if (fail != nullptr) {
2081          *fail = fin.fail();
2082      }
2083      return num;
2084  }
2085  
2086  char const* getEnv(char const* name) {
2087  #    if defined(_MSC_VER)
2088  #        pragma warning(push)
2089  #        pragma warning(disable : 4996) // getenv': This function or variable may be unsafe.
2090  #    endif
2091      return std::getenv(name); // NOLINT(concurrency-mt-unsafe)
2092  #    if defined(_MSC_VER)
2093  #        pragma warning(pop)
2094  #    endif
2095  }
2096  
2097  bool isEndlessRunning(std::string const& name) {
2098      auto const* const endless = getEnv("NANOBENCH_ENDLESS");
2099      return nullptr != endless && endless == name;
2100  }
2101  
2102  // True when environment variable NANOBENCH_SUPPRESS_WARNINGS is either not set at all, or set to "0"
2103  bool isWarningsEnabled() {
2104      auto const* const suppression = getEnv("NANOBENCH_SUPPRESS_WARNINGS");
2105      return nullptr == suppression || suppression == std::string("0");
2106  }
2107  
2108  void gatherStabilityInformation(std::vector<std::string>& warnings, std::vector<std::string>& recommendations) {
2109      warnings.clear();
2110      recommendations.clear();
2111  
2112  #    if defined(DEBUG)
2113      warnings.emplace_back("DEBUG defined");
2114      bool const recommendCheckFlags = true;
2115  #    else
2116      bool const recommendCheckFlags = false;
2117  #    endif
2118  
2119      bool recommendPyPerf = false;
2120  #    if defined(__linux__)
2121      auto nprocs = sysconf(_SC_NPROCESSORS_CONF);
2122      if (nprocs <= 0) {
2123          warnings.emplace_back("couldn't figure out number of processors - no governor, turbo check possible");
2124      } else {
2125          // check frequency scaling
2126          for (long id = 0; id < nprocs; ++id) {
2127              auto idStr = detail::fmt::to_s(static_cast<uint64_t>(id));
2128              auto sysCpu = "/sys/devices/system/cpu/cpu" + idStr;
2129              auto minFreq = parseFile<int64_t>(sysCpu + "/cpufreq/scaling_min_freq", nullptr);
2130              auto maxFreq = parseFile<int64_t>(sysCpu + "/cpufreq/scaling_max_freq", nullptr);
2131              if (minFreq != maxFreq) {
2132                  auto minMHz = d(minFreq) / 1000.0;
2133                  auto maxMHz = d(maxFreq) / 1000.0;
2134                  warnings.emplace_back("CPU frequency scaling enabled: CPU " + idStr + " between " +
2135                                        detail::fmt::Number(1, 1, minMHz).to_s() + " and " + detail::fmt::Number(1, 1, maxMHz).to_s() +
2136                                        " MHz");
2137                  recommendPyPerf = true;
2138                  break;
2139              }
2140          }
2141  
2142          auto fail = false;
2143          auto currentGovernor = parseFile<std::string>("/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor", &fail);
2144          if (!fail && "performance" != currentGovernor) {
2145              warnings.emplace_back("CPU governor is '" + currentGovernor + "' but should be 'performance'");
2146              recommendPyPerf = true;
2147          }
2148  
2149          auto noTurbo = parseFile<int>("/sys/devices/system/cpu/intel_pstate/no_turbo", &fail);
2150          if (!fail && noTurbo == 0) {
2151              warnings.emplace_back("Turbo is enabled, CPU frequency will fluctuate");
2152              recommendPyPerf = true;
2153          }
2154      }
2155  #    endif
2156  
2157      if (recommendCheckFlags) {
2158          recommendations.emplace_back("Make sure you compile for Release");
2159      }
2160      if (recommendPyPerf) {
2161          recommendations.emplace_back("Use 'pyperf system tune' before benchmarking. See https://github.com/psf/pyperf");
2162      }
2163  }
2164  
2165  void printStabilityInformationOnce(std::ostream* outStream) {
2166      static bool shouldPrint = true;
2167      if (shouldPrint && (nullptr != outStream) && isWarningsEnabled()) {
2168          auto& os = *outStream;
2169          shouldPrint = false;
2170          std::vector<std::string> warnings;
2171          std::vector<std::string> recommendations;
2172          gatherStabilityInformation(warnings, recommendations);
2173          if (warnings.empty()) {
2174              return;
2175          }
2176  
2177          os << "Warning, results might be unstable:" << std::endl;
2178          for (auto const& w : warnings) {
2179              os << "* " << w << std::endl;
2180          }
2181  
2182          os << std::endl << "Recommendations" << std::endl;
2183          for (auto const& r : recommendations) {
2184              os << "* " << r << std::endl;
2185          }
2186      }
2187  }
2188  
2189  // remembers the last table settings used. When it changes, a new table header is automatically written for the new entry.
2190  uint64_t& singletonHeaderHash() noexcept {
2191      static uint64_t sHeaderHash{};
2192      return sHeaderHash;
2193  }
2194  
2195  ANKERL_NANOBENCH_NO_SANITIZE("integer", "undefined")
2196  inline uint64_t hash_combine(uint64_t seed, uint64_t val) {
2197      return seed ^ (val + UINT64_C(0x9e3779b9) + (seed << 6U) + (seed >> 2U));
2198  }
2199  
2200  // determines resolution of the given clock. This is done by measuring multiple times and returning the minimum time difference.
2201  Clock::duration calcClockResolution(size_t numEvaluations) noexcept {
2202      auto bestDuration = Clock::duration::max();
2203      Clock::time_point tBegin;
2204      Clock::time_point tEnd;
2205      for (size_t i = 0; i < numEvaluations; ++i) {
2206          tBegin = Clock::now();
2207          do {
2208              tEnd = Clock::now();
2209          } while (tBegin == tEnd);
2210          bestDuration = (std::min)(bestDuration, tEnd - tBegin);
2211      }
2212      return bestDuration;
2213  }
2214  
2215  // Calculates clock resolution once, and remembers the result
2216  Clock::duration clockResolution() noexcept {
2217      static Clock::duration const sResolution = calcClockResolution(20);
2218      return sResolution;
2219  }
2220  
2221  ANKERL_NANOBENCH(IGNORE_PADDED_PUSH)
2222  struct IterationLogic::Impl {
2223      enum class State { warmup, upscaling_runtime, measuring, endless };
2224  
2225      explicit Impl(Bench const& bench)
2226          : mBench(bench)
2227          , mResult(bench.config()) {
2228          printStabilityInformationOnce(mBench.output());
2229  
2230          // determine target runtime per epoch
2231          mTargetRuntimePerEpoch = detail::clockResolution() * mBench.clockResolutionMultiple();
2232          if (mTargetRuntimePerEpoch > mBench.maxEpochTime()) {
2233              mTargetRuntimePerEpoch = mBench.maxEpochTime();
2234          }
2235          if (mTargetRuntimePerEpoch < mBench.minEpochTime()) {
2236              mTargetRuntimePerEpoch = mBench.minEpochTime();
2237          }
2238  
2239          if (isEndlessRunning(mBench.name())) {
2240              std::cerr << "NANOBENCH_ENDLESS set: running '" << mBench.name() << "' endlessly" << std::endl;
2241              mNumIters = (std::numeric_limits<uint64_t>::max)();
2242              mState = State::endless;
2243          } else if (0 != mBench.warmup()) {
2244              mNumIters = mBench.warmup();
2245              mState = State::warmup;
2246          } else if (0 != mBench.epochIterations()) {
2247              // exact number of iterations
2248              mNumIters = mBench.epochIterations();
2249              mState = State::measuring;
2250          } else {
2251              mNumIters = mBench.minEpochIterations();
2252              mState = State::upscaling_runtime;
2253          }
2254      }
2255  
2256      // directly calculates new iters based on elapsed&iters, and adds a 10% noise. Makes sure we don't underflow.
2257      ANKERL_NANOBENCH(NODISCARD) uint64_t calcBestNumIters(std::chrono::nanoseconds elapsed, uint64_t iters) noexcept {
2258          auto doubleElapsed = d(elapsed);
2259          auto doubleTargetRuntimePerEpoch = d(mTargetRuntimePerEpoch);
2260          auto doubleNewIters = doubleTargetRuntimePerEpoch / doubleElapsed * d(iters);
2261  
2262          auto doubleMinEpochIters = d(mBench.minEpochIterations());
2263          if (doubleNewIters < doubleMinEpochIters) {
2264              doubleNewIters = doubleMinEpochIters;
2265          }
2266          doubleNewIters *= 1.0 + 0.2 * mRng.uniform01();
2267  
2268          // +0.5 for correct rounding when casting
2269          // NOLINTNEXTLINE(bugprone-incorrect-roundings)
2270          return static_cast<uint64_t>(doubleNewIters + 0.5);
2271      }
2272  
2273      ANKERL_NANOBENCH_NO_SANITIZE("integer", "undefined") void upscale(std::chrono::nanoseconds elapsed) {
2274          if (elapsed * 10 < mTargetRuntimePerEpoch) {
2275              // we are far below the target runtime. Multiply iterations by 10 (with overflow check)
2276              if (mNumIters * 10 < mNumIters) {
2277                  // overflow :-(
2278                  showResult("iterations overflow. Maybe your code got optimized away?");
2279                  mNumIters = 0;
2280                  return;
2281              }
2282              mNumIters *= 10;
2283          } else {
2284              mNumIters = calcBestNumIters(elapsed, mNumIters);
2285          }
2286      }
2287  
2288      void add(std::chrono::nanoseconds elapsed, PerformanceCounters const& pc) noexcept {
2289  #    if defined(ANKERL_NANOBENCH_LOG_ENABLED)
2290          auto oldIters = mNumIters;
2291  #    endif
2292  
2293          switch (mState) {
2294          case State::warmup:
2295              if (isCloseEnoughForMeasurements(elapsed)) {
2296                  // if elapsed is close enough, we can skip upscaling and go right to measurements
2297                  // still, we don't add the result to the measurements.
2298                  mState = State::measuring;
2299                  mNumIters = calcBestNumIters(elapsed, mNumIters);
2300              } else {
2301                  // not close enough: switch to upscaling
2302                  mState = State::upscaling_runtime;
2303                  upscale(elapsed);
2304              }
2305              break;
2306  
2307          case State::upscaling_runtime:
2308              if (isCloseEnoughForMeasurements(elapsed)) {
2309                  // if we are close enough, add measurement and switch to always measuring
2310                  mState = State::measuring;
2311                  mTotalElapsed += elapsed;
2312                  mTotalNumIters += mNumIters;
2313                  mResult.add(elapsed, mNumIters, pc);
2314                  mNumIters = calcBestNumIters(mTotalElapsed, mTotalNumIters);
2315              } else {
2316                  upscale(elapsed);
2317              }
2318              break;
2319  
2320          case State::measuring:
2321              // just add measurements - no questions asked. Even when runtime is low. But we can't ignore
2322              // that fluctuation, or else we would bias the result
2323              mTotalElapsed += elapsed;
2324              mTotalNumIters += mNumIters;
2325              mResult.add(elapsed, mNumIters, pc);
2326              if (0 != mBench.epochIterations()) {
2327                  mNumIters = mBench.epochIterations();
2328              } else {
2329                  mNumIters = calcBestNumIters(mTotalElapsed, mTotalNumIters);
2330              }
2331              break;
2332  
2333          case State::endless:
2334              mNumIters = (std::numeric_limits<uint64_t>::max)();
2335              break;
2336          }
2337  
2338          if (static_cast<uint64_t>(mResult.size()) == mBench.epochs()) {
2339              // we got all the results that we need, finish it
2340              showResult("");
2341              mNumIters = 0;
2342          }
2343  
2344          ANKERL_NANOBENCH_LOG(mBench.name() << ": " << detail::fmt::Number(20, 3, d(elapsed.count())) << " elapsed, "
2345                                             << detail::fmt::Number(20, 3, d(mTargetRuntimePerEpoch.count())) << " target. oldIters="
2346                                             << oldIters << ", mNumIters=" << mNumIters << ", mState=" << static_cast<int>(mState));
2347      }
2348  
2349      // NOLINTNEXTLINE(readability-function-cognitive-complexity)
2350      void showResult(std::string const& errorMessage) const {
2351          ANKERL_NANOBENCH_LOG(errorMessage);
2352  
2353          if (mBench.output() != nullptr) {
2354              // prepare column data ///////
2355              std::vector<fmt::MarkDownColumn> columns;
2356  
2357              auto rMedian = mResult.median(Result::Measure::elapsed);
2358  
2359              if (mBench.relative()) {
2360                  double d = 100.0;
2361                  if (!mBench.results().empty()) {
2362                      d = rMedian <= 0.0 ? 0.0 : mBench.results().front().median(Result::Measure::elapsed) / rMedian * 100.0;
2363                  }
2364                  columns.emplace_back(11, 1, "relative", "%", d);
2365              }
2366  
2367              if (mBench.complexityN() > 0) {
2368                  columns.emplace_back(14, 0, "complexityN", "", mBench.complexityN());
2369              }
2370  
2371              columns.emplace_back(22, 2, mBench.timeUnitName() + "/" + mBench.unit(), "",
2372                                   rMedian / (mBench.timeUnit().count() * mBench.batch()));
2373              columns.emplace_back(22, 2, mBench.unit() + "/s", "", rMedian <= 0.0 ? 0.0 : mBench.batch() / rMedian);
2374  
2375              double const rErrorMedian = mResult.medianAbsolutePercentError(Result::Measure::elapsed);
2376              columns.emplace_back(10, 1, "err%", "%", rErrorMedian * 100.0);
2377  
2378              double rInsMedian = -1.0;
2379              if (mBench.performanceCounters() && mResult.has(Result::Measure::instructions)) {
2380                  rInsMedian = mResult.median(Result::Measure::instructions);
2381                  columns.emplace_back(18, 2, "ins/" + mBench.unit(), "", rInsMedian / mBench.batch());
2382              }
2383  
2384              double rCycMedian = -1.0;
2385              if (mBench.performanceCounters() && mResult.has(Result::Measure::cpucycles)) {
2386                  rCycMedian = mResult.median(Result::Measure::cpucycles);
2387                  columns.emplace_back(18, 2, "cyc/" + mBench.unit(), "", rCycMedian / mBench.batch());
2388              }
2389              if (rInsMedian > 0.0 && rCycMedian > 0.0) {
2390                  columns.emplace_back(9, 3, "IPC", "", rCycMedian <= 0.0 ? 0.0 : rInsMedian / rCycMedian);
2391              }
2392              if (mBench.performanceCounters() && mResult.has(Result::Measure::branchinstructions)) {
2393                  double const rBraMedian = mResult.median(Result::Measure::branchinstructions);
2394                  columns.emplace_back(17, 2, "bra/" + mBench.unit(), "", rBraMedian / mBench.batch());
2395                  if (mResult.has(Result::Measure::branchmisses)) {
2396                      double p = 0.0;
2397                      if (rBraMedian >= 1e-9) {
2398                          p = 100.0 * mResult.median(Result::Measure::branchmisses) / rBraMedian;
2399                      }
2400                      columns.emplace_back(10, 1, "miss%", "%", p);
2401                  }
2402              }
2403  
2404              columns.emplace_back(12, 2, "total", "", mResult.sumProduct(Result::Measure::iterations, Result::Measure::elapsed));
2405  
2406              // write everything
2407              auto& os = *mBench.output();
2408  
2409              // combine all elements that are relevant for printing the header
2410              uint64_t hash = 0;
2411              hash = hash_combine(std::hash<std::string>{}(mBench.unit()), hash);
2412              hash = hash_combine(std::hash<std::string>{}(mBench.title()), hash);
2413              hash = hash_combine(std::hash<std::string>{}(mBench.timeUnitName()), hash);
2414              hash = hash_combine(std::hash<double>{}(mBench.timeUnit().count()), hash);
2415              hash = hash_combine(std::hash<bool>{}(mBench.relative()), hash);
2416              hash = hash_combine(std::hash<bool>{}(mBench.performanceCounters()), hash);
2417  
2418              if (hash != singletonHeaderHash()) {
2419                  singletonHeaderHash() = hash;
2420  
2421                  // no result yet, print header
2422                  os << std::endl;
2423                  for (auto const& col : columns) {
2424                      os << col.title();
2425                  }
2426                  os << "| " << mBench.title() << std::endl;
2427  
2428                  for (auto const& col : columns) {
2429                      os << col.separator();
2430                  }
2431                  os << "|:" << std::string(mBench.title().size() + 1U, '-') << std::endl;
2432              }
2433  
2434              if (!errorMessage.empty()) {
2435                  for (auto const& col : columns) {
2436                      os << col.invalid();
2437                  }
2438                  os << "| :boom: " << fmt::MarkDownCode(mBench.name()) << " (" << errorMessage << ')' << std::endl;
2439              } else {
2440                  for (auto const& col : columns) {
2441                      os << col.value();
2442                  }
2443                  os << "| ";
2444                  auto showUnstable = isWarningsEnabled() && rErrorMedian >= 0.05;
2445                  if (showUnstable) {
2446                      os << ":wavy_dash: ";
2447                  }
2448                  os << fmt::MarkDownCode(mBench.name());
2449                  if (showUnstable) {
2450                      auto avgIters = d(mTotalNumIters) / d(mBench.epochs());
2451                      // NOLINTNEXTLINE(bugprone-incorrect-roundings)
2452                      auto suggestedIters = static_cast<uint64_t>(avgIters * 10 + 0.5);
2453  
2454                      os << " (Unstable with ~" << detail::fmt::Number(1, 1, avgIters)
2455                         << " iters. Increase `minEpochIterations` to e.g. " << suggestedIters << ")";
2456                  }
2457                  os << std::endl;
2458              }
2459          }
2460      }
2461  
2462      ANKERL_NANOBENCH(NODISCARD) bool isCloseEnoughForMeasurements(std::chrono::nanoseconds elapsed) const noexcept {
2463          return elapsed * 3 >= mTargetRuntimePerEpoch * 2;
2464      }
2465  
2466      uint64_t mNumIters = 1;                            // NOLINT(misc-non-private-member-variables-in-classes)
2467      Bench const& mBench;                               // NOLINT(misc-non-private-member-variables-in-classes)
2468      std::chrono::nanoseconds mTargetRuntimePerEpoch{}; // NOLINT(misc-non-private-member-variables-in-classes)
2469      Result mResult;                                    // NOLINT(misc-non-private-member-variables-in-classes)
2470      Rng mRng{123};                                     // NOLINT(misc-non-private-member-variables-in-classes)
2471      std::chrono::nanoseconds mTotalElapsed{};          // NOLINT(misc-non-private-member-variables-in-classes)
2472      uint64_t mTotalNumIters = 0;                       // NOLINT(misc-non-private-member-variables-in-classes)
2473      State mState = State::upscaling_runtime;           // NOLINT(misc-non-private-member-variables-in-classes)
2474  };
2475  ANKERL_NANOBENCH(IGNORE_PADDED_POP)
2476  
2477  IterationLogic::IterationLogic(Bench const& bench)
2478      : mPimpl(new Impl(bench)) {}
2479  
2480  IterationLogic::~IterationLogic() {
2481      delete mPimpl;
2482  }
2483  
2484  uint64_t IterationLogic::numIters() const noexcept {
2485      ANKERL_NANOBENCH_LOG(mPimpl->mBench.name() << ": mNumIters=" << mPimpl->mNumIters);
2486      return mPimpl->mNumIters;
2487  }
2488  
2489  void IterationLogic::add(std::chrono::nanoseconds elapsed, PerformanceCounters const& pc) noexcept {
2490      mPimpl->add(elapsed, pc);
2491  }
2492  
2493  void IterationLogic::moveResultTo(std::vector<Result>& results) noexcept {
2494      results.emplace_back(std::move(mPimpl->mResult));
2495  }
2496  
2497  #    if ANKERL_NANOBENCH(PERF_COUNTERS)
2498  
2499  ANKERL_NANOBENCH(IGNORE_PADDED_PUSH)
2500  class LinuxPerformanceCounters {
2501  public:
2502      struct Target {
2503          Target(uint64_t* targetValue_, bool correctMeasuringOverhead_, bool correctLoopOverhead_)
2504              : targetValue(targetValue_)
2505              , correctMeasuringOverhead(correctMeasuringOverhead_)
2506              , correctLoopOverhead(correctLoopOverhead_) {}
2507  
2508          uint64_t* targetValue{};         // NOLINT(misc-non-private-member-variables-in-classes)
2509          bool correctMeasuringOverhead{}; // NOLINT(misc-non-private-member-variables-in-classes)
2510          bool correctLoopOverhead{};      // NOLINT(misc-non-private-member-variables-in-classes)
2511      };
2512  
2513      LinuxPerformanceCounters() = default;
2514      LinuxPerformanceCounters(LinuxPerformanceCounters const&) = delete;
2515      LinuxPerformanceCounters(LinuxPerformanceCounters&&) = delete;
2516      LinuxPerformanceCounters& operator=(LinuxPerformanceCounters const&) = delete;
2517      LinuxPerformanceCounters& operator=(LinuxPerformanceCounters&&) = delete;
2518      ~LinuxPerformanceCounters();
2519  
2520      // quick operation
2521      inline void start() {}
2522  
2523      inline void stop() {}
2524  
2525      bool monitor(perf_sw_ids swId, Target target);
2526      bool monitor(perf_hw_id hwId, Target target);
2527  
2528      ANKERL_NANOBENCH(NODISCARD) bool hasError() const noexcept {
2529          return mHasError;
2530      }
2531  
2532      // Just reading data is faster than enable & disabling.
2533      // we subtract data ourselves.
2534      inline void beginMeasure() {
2535          if (mHasError) {
2536              return;
2537          }
2538  
2539          // NOLINTNEXTLINE(hicpp-signed-bitwise,cppcoreguidelines-pro-type-vararg)
2540          mHasError = -1 == ioctl(mFd, PERF_EVENT_IOC_RESET, PERF_IOC_FLAG_GROUP);
2541          if (mHasError) {
2542              return;
2543          }
2544  
2545          // NOLINTNEXTLINE(hicpp-signed-bitwise,cppcoreguidelines-pro-type-vararg)
2546          mHasError = -1 == ioctl(mFd, PERF_EVENT_IOC_ENABLE, PERF_IOC_FLAG_GROUP);
2547      }
2548  
2549      inline void endMeasure() {
2550          if (mHasError) {
2551              return;
2552          }
2553  
2554          // NOLINTNEXTLINE(hicpp-signed-bitwise,cppcoreguidelines-pro-type-vararg)
2555          mHasError = (-1 == ioctl(mFd, PERF_EVENT_IOC_DISABLE, PERF_IOC_FLAG_GROUP));
2556          if (mHasError) {
2557              return;
2558          }
2559  
2560          auto const numBytes = sizeof(uint64_t) * mCounters.size();
2561          auto ret = read(mFd, mCounters.data(), numBytes);
2562          mHasError = ret != static_cast<ssize_t>(numBytes);
2563      }
2564  
2565      void updateResults(uint64_t numIters);
2566  
2567      // rounded integer division
2568      template <typename T>
2569      static inline T divRounded(T a, T divisor) {
2570          return (a + divisor / 2) / divisor;
2571      }
2572  
2573      ANKERL_NANOBENCH_NO_SANITIZE("integer", "undefined")
2574      static inline uint32_t mix(uint32_t x) noexcept {
2575          x ^= x << 13U;
2576          x ^= x >> 17U;
2577          x ^= x << 5U;
2578          return x;
2579      }
2580  
2581      template <typename Op>
2582      ANKERL_NANOBENCH_NO_SANITIZE("integer", "undefined")
2583      void calibrate(Op&& op) {
2584          // clear current calibration data,
2585          for (auto& v : mCalibratedOverhead) {
2586              v = UINT64_C(0);
2587          }
2588  
2589          // create new calibration data
2590          auto newCalibration = mCalibratedOverhead;
2591          for (auto& v : newCalibration) {
2592              v = (std::numeric_limits<uint64_t>::max)();
2593          }
2594          for (size_t iter = 0; iter < 100; ++iter) {
2595              beginMeasure();
2596              op();
2597              endMeasure();
2598              if (mHasError) {
2599                  return;
2600              }
2601  
2602              for (size_t i = 0; i < newCalibration.size(); ++i) {
2603                  auto diff = mCounters[i];
2604                  if (newCalibration[i] > diff) {
2605                      newCalibration[i] = diff;
2606                  }
2607              }
2608          }
2609  
2610          mCalibratedOverhead = std::move(newCalibration);
2611  
2612          {
2613              // calibrate loop overhead. For branches & instructions this makes sense, not so much for everything else like cycles.
2614              // marsaglia's xorshift: mov, sal/shr, xor. Times 3.
2615              // This has the nice property that the compiler doesn't seem to be able to optimize multiple calls any further.
2616              // see https://godbolt.org/z/49RVQ5
2617              uint64_t const numIters = 100000U + (std::random_device{}() & 3U);
2618              uint64_t n = numIters;
2619              uint32_t x = 1234567;
2620  
2621              beginMeasure();
2622              while (n-- > 0) {
2623                  x = mix(x);
2624              }
2625              endMeasure();
2626              detail::doNotOptimizeAway(x);
2627              auto measure1 = mCounters;
2628  
2629              n = numIters;
2630              beginMeasure();
2631              while (n-- > 0) {
2632                  // we now run *twice* so we can easily calculate the overhead
2633                  x = mix(x);
2634                  x = mix(x);
2635              }
2636              endMeasure();
2637              detail::doNotOptimizeAway(x);
2638              auto measure2 = mCounters;
2639  
2640              for (size_t i = 0; i < mCounters.size(); ++i) {
2641                  // factor 2 because we have two instructions per loop
2642                  auto m1 = measure1[i] > mCalibratedOverhead[i] ? measure1[i] - mCalibratedOverhead[i] : 0;
2643                  auto m2 = measure2[i] > mCalibratedOverhead[i] ? measure2[i] - mCalibratedOverhead[i] : 0;
2644                  auto overhead = m1 * 2 > m2 ? m1 * 2 - m2 : 0;
2645  
2646                  mLoopOverhead[i] = divRounded(overhead, numIters);
2647              }
2648          }
2649      }
2650  
2651  private:
2652      bool monitor(uint32_t type, uint64_t eventid, Target target);
2653  
2654      std::map<uint64_t, Target> mIdToTarget{};
2655  
2656      // start with minimum size of 3 for read_format
2657      std::vector<uint64_t> mCounters = std::vector<uint64_t>(3);
2658      std::vector<uint64_t> mCalibratedOverhead = std::vector<uint64_t>(3);
2659      std::vector<uint64_t> mLoopOverhead = std::vector<uint64_t>(3);
2660  
2661      uint64_t mTimeEnabledNanos = 0;
2662      uint64_t mTimeRunningNanos = 0;
2663      int mFd = -1;
2664      bool mHasError = false;
2665  };
2666  ANKERL_NANOBENCH(IGNORE_PADDED_POP)
2667  
2668  LinuxPerformanceCounters::~LinuxPerformanceCounters() {
2669      if (-1 != mFd) {
2670          close(mFd);
2671      }
2672  }
2673  
2674  bool LinuxPerformanceCounters::monitor(perf_sw_ids swId, LinuxPerformanceCounters::Target target) {
2675      return monitor(PERF_TYPE_SOFTWARE, swId, target);
2676  }
2677  
2678  bool LinuxPerformanceCounters::monitor(perf_hw_id hwId, LinuxPerformanceCounters::Target target) {
2679      return monitor(PERF_TYPE_HARDWARE, hwId, target);
2680  }
2681  
2682  // overflow is ok, it's checked
2683  ANKERL_NANOBENCH_NO_SANITIZE("integer", "undefined")
2684  void LinuxPerformanceCounters::updateResults(uint64_t numIters) {
2685      // clear old data
2686      for (auto& id_value : mIdToTarget) {
2687          *id_value.second.targetValue = UINT64_C(0);
2688      }
2689  
2690      if (mHasError) {
2691          return;
2692      }
2693  
2694      mTimeEnabledNanos = mCounters[1] - mCalibratedOverhead[1];
2695      mTimeRunningNanos = mCounters[2] - mCalibratedOverhead[2];
2696  
2697      for (uint64_t i = 0; i < mCounters[0]; ++i) {
2698          auto idx = static_cast<size_t>(3 + i * 2 + 0);
2699          auto id = mCounters[idx + 1U];
2700  
2701          auto it = mIdToTarget.find(id);
2702          if (it != mIdToTarget.end()) {
2703  
2704              auto& tgt = it->second;
2705              *tgt.targetValue = mCounters[idx];
2706              if (tgt.correctMeasuringOverhead) {
2707                  if (*tgt.targetValue >= mCalibratedOverhead[idx]) {
2708                      *tgt.targetValue -= mCalibratedOverhead[idx];
2709                  } else {
2710                      *tgt.targetValue = 0U;
2711                  }
2712              }
2713              if (tgt.correctLoopOverhead) {
2714                  auto correctionVal = mLoopOverhead[idx] * numIters;
2715                  if (*tgt.targetValue >= correctionVal) {
2716                      *tgt.targetValue -= correctionVal;
2717                  } else {
2718                      *tgt.targetValue = 0U;
2719                  }
2720              }
2721          }
2722      }
2723  }
2724  
2725  bool LinuxPerformanceCounters::monitor(uint32_t type, uint64_t eventid, Target target) {
2726      *target.targetValue = (std::numeric_limits<uint64_t>::max)();
2727      if (mHasError) {
2728          return false;
2729      }
2730  
2731      auto pea = perf_event_attr();
2732      std::memset(&pea, 0, sizeof(perf_event_attr));
2733      pea.type = type;
2734      pea.size = sizeof(perf_event_attr);
2735      pea.config = eventid;
2736      pea.disabled = 1; // start counter as disabled
2737      pea.exclude_kernel = 1;
2738      pea.exclude_hv = 1;
2739  
2740      // NOLINTNEXTLINE(hicpp-signed-bitwise)
2741      pea.read_format = PERF_FORMAT_GROUP | PERF_FORMAT_ID | PERF_FORMAT_TOTAL_TIME_ENABLED | PERF_FORMAT_TOTAL_TIME_RUNNING;
2742  
2743      const int pid = 0;                    // the current process
2744      const int cpu = -1;                   // all CPUs
2745  #        if defined(PERF_FLAG_FD_CLOEXEC) // since Linux 3.14
2746      const unsigned long flags = PERF_FLAG_FD_CLOEXEC;
2747  #        else
2748      const unsigned long flags = 0;
2749  #        endif
2750  
2751      // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
2752      auto fd = static_cast<int>(syscall(__NR_perf_event_open, &pea, pid, cpu, mFd, flags));
2753      if (-1 == fd) {
2754          return false;
2755      }
2756      if (-1 == mFd) {
2757          // first call: set to fd, and use this from now on
2758          mFd = fd;
2759      }
2760      uint64_t id = 0;
2761      // NOLINTNEXTLINE(hicpp-signed-bitwise,cppcoreguidelines-pro-type-vararg)
2762      if (-1 == ioctl(fd, PERF_EVENT_IOC_ID, &id)) {
2763          // couldn't get id
2764          return false;
2765      }
2766  
2767      // insert into map, rely on the fact that map's references are constant.
2768      mIdToTarget.emplace(id, target);
2769  
2770      // prepare readformat with the correct size (after the insert)
2771      auto size = 3 + 2 * mIdToTarget.size();
2772      mCounters.resize(size);
2773      mCalibratedOverhead.resize(size);
2774      mLoopOverhead.resize(size);
2775  
2776      return true;
2777  }
2778  
2779  PerformanceCounters::PerformanceCounters()
2780      : mPc(new LinuxPerformanceCounters())
2781      , mVal()
2782      , mHas() {
2783  
2784      // HW events
2785      mHas.cpuCycles = mPc->monitor(PERF_COUNT_HW_REF_CPU_CYCLES, LinuxPerformanceCounters::Target(&mVal.cpuCycles, true, false));
2786      if (!mHas.cpuCycles) {
2787          // Fallback to cycles counter, reference cycles not available in many systems.
2788          mHas.cpuCycles = mPc->monitor(PERF_COUNT_HW_CPU_CYCLES, LinuxPerformanceCounters::Target(&mVal.cpuCycles, true, false));
2789      }
2790      mHas.instructions = mPc->monitor(PERF_COUNT_HW_INSTRUCTIONS, LinuxPerformanceCounters::Target(&mVal.instructions, true, true));
2791      mHas.branchInstructions =
2792          mPc->monitor(PERF_COUNT_HW_BRANCH_INSTRUCTIONS, LinuxPerformanceCounters::Target(&mVal.branchInstructions, true, false));
2793      mHas.branchMisses = mPc->monitor(PERF_COUNT_HW_BRANCH_MISSES, LinuxPerformanceCounters::Target(&mVal.branchMisses, true, false));
2794      // mHas.branchMisses = false;
2795  
2796      // SW events
2797      mHas.pageFaults = mPc->monitor(PERF_COUNT_SW_PAGE_FAULTS, LinuxPerformanceCounters::Target(&mVal.pageFaults, true, false));
2798      mHas.contextSwitches =
2799          mPc->monitor(PERF_COUNT_SW_CONTEXT_SWITCHES, LinuxPerformanceCounters::Target(&mVal.contextSwitches, true, false));
2800  
2801      mPc->start();
2802      mPc->calibrate([] {
2803          auto before = ankerl::nanobench::Clock::now();
2804          auto after = ankerl::nanobench::Clock::now();
2805          (void)before;
2806          (void)after;
2807      });
2808  
2809      if (mPc->hasError()) {
2810          // something failed, don't monitor anything.
2811          mHas = PerfCountSet<bool>{};
2812      }
2813  }
2814  
2815  PerformanceCounters::~PerformanceCounters() {
2816      // no need to check for nullptr, delete nullptr has no effect
2817      delete mPc;
2818  }
2819  
2820  void PerformanceCounters::beginMeasure() {
2821      mPc->beginMeasure();
2822  }
2823  
2824  void PerformanceCounters::endMeasure() {
2825      mPc->endMeasure();
2826  }
2827  
2828  void PerformanceCounters::updateResults(uint64_t numIters) {
2829      mPc->updateResults(numIters);
2830  }
2831  
2832  #    else
2833  
2834  PerformanceCounters::PerformanceCounters() = default;
2835  PerformanceCounters::~PerformanceCounters() = default;
2836  void PerformanceCounters::beginMeasure() {}
2837  void PerformanceCounters::endMeasure() {}
2838  void PerformanceCounters::updateResults(uint64_t) {}
2839  
2840  #    endif
2841  
2842  ANKERL_NANOBENCH(NODISCARD) PerfCountSet<uint64_t> const& PerformanceCounters::val() const noexcept {
2843      return mVal;
2844  }
2845  ANKERL_NANOBENCH(NODISCARD) PerfCountSet<bool> const& PerformanceCounters::has() const noexcept {
2846      return mHas;
2847  }
2848  
2849  // formatting utilities
2850  namespace fmt {
2851  
2852  // adds thousands separator to numbers
2853  NumSep::NumSep(char sep)
2854      : mSep(sep) {}
2855  
2856  char NumSep::do_thousands_sep() const {
2857      return mSep;
2858  }
2859  
2860  std::string NumSep::do_grouping() const {
2861      return "\003";
2862  }
2863  
2864  // RAII to save & restore a stream's state
2865  StreamStateRestorer::StreamStateRestorer(std::ostream& s)
2866      : mStream(s)
2867      , mLocale(s.getloc())
2868      , mPrecision(s.precision())
2869      , mWidth(s.width())
2870      , mFill(s.fill())
2871      , mFmtFlags(s.flags()) {}
2872  
2873  StreamStateRestorer::~StreamStateRestorer() {
2874      restore();
2875  }
2876  
2877  // sets back all stream info that we remembered at construction
2878  void StreamStateRestorer::restore() {
2879      mStream.imbue(mLocale);
2880      mStream.precision(mPrecision);
2881      mStream.width(mWidth);
2882      mStream.fill(mFill);
2883      mStream.flags(mFmtFlags);
2884  }
2885  
2886  Number::Number(int width, int precision, int64_t value)
2887      : mWidth(width)
2888      , mPrecision(precision)
2889      , mValue(d(value)) {}
2890  
2891  Number::Number(int width, int precision, double value)
2892      : mWidth(width)
2893      , mPrecision(precision)
2894      , mValue(value) {}
2895  
2896  std::ostream& Number::write(std::ostream& os) const {
2897      StreamStateRestorer const restorer(os);
2898      os.imbue(std::locale(os.getloc(), new NumSep(',')));
2899      os << std::setw(mWidth) << std::setprecision(mPrecision) << std::fixed << mValue;
2900      return os;
2901  }
2902  
2903  std::string Number::to_s() const {
2904      std::stringstream ss;
2905      write(ss);
2906      return ss.str();
2907  }
2908  
2909  std::string to_s(uint64_t n) {
2910      std::string str;
2911      do {
2912          str += static_cast<char>('0' + static_cast<char>(n % 10));
2913          n /= 10;
2914      } while (n != 0);
2915      std::reverse(str.begin(), str.end());
2916      return str;
2917  }
2918  
2919  std::ostream& operator<<(std::ostream& os, Number const& n) {
2920      return n.write(os);
2921  }
2922  
2923  MarkDownColumn::MarkDownColumn(int w, int prec, std::string tit, std::string suff, double val) noexcept
2924      : mWidth(w)
2925      , mPrecision(prec)
2926      , mTitle(std::move(tit))
2927      , mSuffix(std::move(suff))
2928      , mValue(val) {}
2929  
2930  std::string MarkDownColumn::title() const {
2931      std::stringstream ss;
2932      ss << '|' << std::setw(mWidth - 2) << std::right << mTitle << ' ';
2933      return ss.str();
2934  }
2935  
2936  std::string MarkDownColumn::separator() const {
2937      std::string sep(static_cast<size_t>(mWidth), '-');
2938      sep.front() = '|';
2939      sep.back() = ':';
2940      return sep;
2941  }
2942  
2943  std::string MarkDownColumn::invalid() const {
2944      std::string sep(static_cast<size_t>(mWidth), ' ');
2945      sep.front() = '|';
2946      sep[sep.size() - 2] = '-';
2947      return sep;
2948  }
2949  
2950  std::string MarkDownColumn::value() const {
2951      std::stringstream ss;
2952      auto width = mWidth - 2 - static_cast<int>(mSuffix.size());
2953      ss << '|' << Number(width, mPrecision, mValue) << mSuffix << ' ';
2954      return ss.str();
2955  }
2956  
2957  // Formats any text as markdown code, escaping backticks.
2958  MarkDownCode::MarkDownCode(std::string const& what) {
2959      mWhat.reserve(what.size() + 2);
2960      mWhat.push_back('`');
2961      for (char const c : what) {
2962          mWhat.push_back(c);
2963          if ('`' == c) {
2964              mWhat.push_back('`');
2965          }
2966      }
2967      mWhat.push_back('`');
2968  }
2969  
2970  std::ostream& MarkDownCode::write(std::ostream& os) const {
2971      return os << mWhat;
2972  }
2973  
2974  std::ostream& operator<<(std::ostream& os, MarkDownCode const& mdCode) {
2975      return mdCode.write(os);
2976  }
2977  } // namespace fmt
2978  } // namespace detail
2979  
2980  // provide implementation here so it's only generated once
2981  Config::Config() = default;
2982  Config::~Config() = default;
2983  Config& Config::operator=(Config const&) = default;
2984  Config& Config::operator=(Config&&) noexcept(ANKERL_NANOBENCH(NOEXCEPT_STRING_MOVE)) = default;
2985  Config::Config(Config const&) = default;
2986  Config::Config(Config&&) noexcept = default;
2987  
2988  // provide implementation here so it's only generated once
2989  Result::~Result() = default;
2990  Result& Result::operator=(Result const&) = default;
2991  Result& Result::operator=(Result&&) noexcept(ANKERL_NANOBENCH(NOEXCEPT_STRING_MOVE)) = default;
2992  Result::Result(Result const&) = default;
2993  Result::Result(Result&&) noexcept = default;
2994  
2995  namespace detail {
2996  template <typename T>
2997  inline constexpr typename std::underlying_type<T>::type u(T val) noexcept {
2998      return static_cast<typename std::underlying_type<T>::type>(val);
2999  }
3000  } // namespace detail
3001  
3002  // Result returned after a benchmark has finished. Can be used as a baseline for relative().
3003  Result::Result(Config benchmarkConfig)
3004      : mConfig(std::move(benchmarkConfig))
3005      , mNameToMeasurements{detail::u(Result::Measure::_size)} {}
3006  
3007  void Result::add(Clock::duration totalElapsed, uint64_t iters, detail::PerformanceCounters const& pc) {
3008      using detail::d;
3009      using detail::u;
3010  
3011      double const dIters = d(iters);
3012      mNameToMeasurements[u(Result::Measure::iterations)].push_back(dIters);
3013  
3014      mNameToMeasurements[u(Result::Measure::elapsed)].push_back(d(totalElapsed) / dIters);
3015      if (pc.has().pageFaults) {
3016          mNameToMeasurements[u(Result::Measure::pagefaults)].push_back(d(pc.val().pageFaults) / dIters);
3017      }
3018      if (pc.has().cpuCycles) {
3019          mNameToMeasurements[u(Result::Measure::cpucycles)].push_back(d(pc.val().cpuCycles) / dIters);
3020      }
3021      if (pc.has().contextSwitches) {
3022          mNameToMeasurements[u(Result::Measure::contextswitches)].push_back(d(pc.val().contextSwitches) / dIters);
3023      }
3024      if (pc.has().instructions) {
3025          mNameToMeasurements[u(Result::Measure::instructions)].push_back(d(pc.val().instructions) / dIters);
3026      }
3027      if (pc.has().branchInstructions) {
3028          double branchInstructions = 0.0;
3029          // correcting branches: remove branch introduced by the while (...) loop for each iteration.
3030          if (pc.val().branchInstructions > iters + 1U) {
3031              branchInstructions = d(pc.val().branchInstructions - (iters + 1U));
3032          }
3033          mNameToMeasurements[u(Result::Measure::branchinstructions)].push_back(branchInstructions / dIters);
3034  
3035          if (pc.has().branchMisses) {
3036              // correcting branch misses
3037              double branchMisses = d(pc.val().branchMisses);
3038              if (branchMisses > branchInstructions) {
3039                  // can't have branch misses when there were branches...
3040                  branchMisses = branchInstructions;
3041              }
3042  
3043              // assuming at least one missed branch for the loop
3044              branchMisses -= 1.0;
3045              if (branchMisses < 1.0) {
3046                  branchMisses = 1.0;
3047              }
3048              mNameToMeasurements[u(Result::Measure::branchmisses)].push_back(branchMisses / dIters);
3049          }
3050      }
3051  }
3052  
3053  Config const& Result::config() const noexcept {
3054      return mConfig;
3055  }
3056  
3057  inline double calcMedian(std::vector<double>& data) {
3058      if (data.empty()) {
3059          return 0.0;
3060      }
3061      std::sort(data.begin(), data.end());
3062  
3063      auto midIdx = data.size() / 2U;
3064      if (1U == (data.size() & 1U)) {
3065          return data[midIdx];
3066      }
3067      return (data[midIdx - 1U] + data[midIdx]) / 2U;
3068  }
3069  
3070  double Result::median(Measure m) const {
3071      // create a copy so we can sort
3072      auto data = mNameToMeasurements[detail::u(m)];
3073      return calcMedian(data);
3074  }
3075  
3076  double Result::average(Measure m) const {
3077      using detail::d;
3078      auto const& data = mNameToMeasurements[detail::u(m)];
3079      if (data.empty()) {
3080          return 0.0;
3081      }
3082  
3083      // create a copy so we can sort
3084      return sum(m) / d(data.size());
3085  }
3086  
3087  double Result::medianAbsolutePercentError(Measure m) const {
3088      // create copy
3089      auto data = mNameToMeasurements[detail::u(m)];
3090  
3091      // calculates MdAPE which is the median of percentage error
3092      // see https://support.numxl.com/hc/en-us/articles/115001223503-MdAPE-Median-Absolute-Percentage-Error
3093      auto med = calcMedian(data);
3094  
3095      // transform the data to absolute error
3096      for (auto& x : data) {
3097          x = (x - med) / x;
3098          if (x < 0) {
3099              x = -x;
3100          }
3101      }
3102      return calcMedian(data);
3103  }
3104  
3105  double Result::sum(Measure m) const noexcept {
3106      auto const& data = mNameToMeasurements[detail::u(m)];
3107      return std::accumulate(data.begin(), data.end(), 0.0);
3108  }
3109  
3110  double Result::sumProduct(Measure m1, Measure m2) const noexcept {
3111      auto const& data1 = mNameToMeasurements[detail::u(m1)];
3112      auto const& data2 = mNameToMeasurements[detail::u(m2)];
3113  
3114      if (data1.size() != data2.size()) {
3115          return 0.0;
3116      }
3117  
3118      double result = 0.0;
3119      for (size_t i = 0, s = data1.size(); i != s; ++i) {
3120          result += data1[i] * data2[i];
3121      }
3122      return result;
3123  }
3124  
3125  bool Result::has(Measure m) const noexcept {
3126      return !mNameToMeasurements[detail::u(m)].empty();
3127  }
3128  
3129  double Result::get(size_t idx, Measure m) const {
3130      auto const& data = mNameToMeasurements[detail::u(m)];
3131      return data.at(idx);
3132  }
3133  
3134  bool Result::empty() const noexcept {
3135      return 0U == size();
3136  }
3137  
3138  size_t Result::size() const noexcept {
3139      auto const& data = mNameToMeasurements[detail::u(Measure::elapsed)];
3140      return data.size();
3141  }
3142  
3143  double Result::minimum(Measure m) const noexcept {
3144      auto const& data = mNameToMeasurements[detail::u(m)];
3145      if (data.empty()) {
3146          return 0.0;
3147      }
3148  
3149      // here its save to assume that at least one element is there
3150      return *std::min_element(data.begin(), data.end());
3151  }
3152  
3153  double Result::maximum(Measure m) const noexcept {
3154      auto const& data = mNameToMeasurements[detail::u(m)];
3155      if (data.empty()) {
3156          return 0.0;
3157      }
3158  
3159      // here its save to assume that at least one element is there
3160      return *std::max_element(data.begin(), data.end());
3161  }
3162  
3163  std::string const& Result::context(char const* variableName) const {
3164      return mConfig.mContext.at(variableName);
3165  }
3166  
3167  std::string const& Result::context(std::string const& variableName) const {
3168      return mConfig.mContext.at(variableName);
3169  }
3170  
3171  Result::Measure Result::fromString(std::string const& str) {
3172      if (str == "elapsed") {
3173          return Measure::elapsed;
3174      }
3175      if (str == "iterations") {
3176          return Measure::iterations;
3177      }
3178      if (str == "pagefaults") {
3179          return Measure::pagefaults;
3180      }
3181      if (str == "cpucycles") {
3182          return Measure::cpucycles;
3183      }
3184      if (str == "contextswitches") {
3185          return Measure::contextswitches;
3186      }
3187      if (str == "instructions") {
3188          return Measure::instructions;
3189      }
3190      if (str == "branchinstructions") {
3191          return Measure::branchinstructions;
3192      }
3193      if (str == "branchmisses") {
3194          return Measure::branchmisses;
3195      }
3196      // not found, return _size
3197      return Measure::_size;
3198  }
3199  
3200  // Configuration of a microbenchmark.
3201  Bench::Bench() {
3202      mConfig.mOut = &std::cout;
3203  }
3204  
3205  Bench::Bench(Bench&&) noexcept = default;
3206  Bench& Bench::operator=(Bench&&) noexcept(ANKERL_NANOBENCH(NOEXCEPT_STRING_MOVE)) = default;
3207  Bench::Bench(Bench const&) = default;
3208  Bench& Bench::operator=(Bench const&) = default;
3209  Bench::~Bench() noexcept = default;
3210  
3211  double Bench::batch() const noexcept {
3212      return mConfig.mBatch;
3213  }
3214  
3215  double Bench::complexityN() const noexcept {
3216      return mConfig.mComplexityN;
3217  }
3218  
3219  // Set a baseline to compare it to. 100% it is exactly as fast as the baseline, >100% means it is faster than the baseline, <100%
3220  // means it is slower than the baseline.
3221  Bench& Bench::relative(bool isRelativeEnabled) noexcept {
3222      mConfig.mIsRelative = isRelativeEnabled;
3223      return *this;
3224  }
3225  bool Bench::relative() const noexcept {
3226      return mConfig.mIsRelative;
3227  }
3228  
3229  Bench& Bench::performanceCounters(bool showPerformanceCounters) noexcept {
3230      mConfig.mShowPerformanceCounters = showPerformanceCounters;
3231      return *this;
3232  }
3233  bool Bench::performanceCounters() const noexcept {
3234      return mConfig.mShowPerformanceCounters;
3235  }
3236  
3237  // Operation unit. Defaults to "op", could be e.g. "byte" for string processing.
3238  // If u differs from currently set unit, the stored results will be cleared.
3239  // Use singular (byte, not bytes).
3240  Bench& Bench::unit(char const* u) {
3241      if (u != mConfig.mUnit) {
3242          mResults.clear();
3243      }
3244      mConfig.mUnit = u;
3245      return *this;
3246  }
3247  
3248  Bench& Bench::unit(std::string const& u) {
3249      return unit(u.c_str());
3250  }
3251  
3252  std::string const& Bench::unit() const noexcept {
3253      return mConfig.mUnit;
3254  }
3255  
3256  Bench& Bench::timeUnit(std::chrono::duration<double> const& tu, std::string const& tuName) {
3257      mConfig.mTimeUnit = tu;
3258      mConfig.mTimeUnitName = tuName;
3259      return *this;
3260  }
3261  
3262  std::string const& Bench::timeUnitName() const noexcept {
3263      return mConfig.mTimeUnitName;
3264  }
3265  
3266  std::chrono::duration<double> const& Bench::timeUnit() const noexcept {
3267      return mConfig.mTimeUnit;
3268  }
3269  
3270  // If benchmarkTitle differs from currently set title, the stored results will be cleared.
3271  Bench& Bench::title(const char* benchmarkTitle) {
3272      if (benchmarkTitle != mConfig.mBenchmarkTitle) {
3273          mResults.clear();
3274      }
3275      mConfig.mBenchmarkTitle = benchmarkTitle;
3276      return *this;
3277  }
3278  Bench& Bench::title(std::string const& benchmarkTitle) {
3279      if (benchmarkTitle != mConfig.mBenchmarkTitle) {
3280          mResults.clear();
3281      }
3282      mConfig.mBenchmarkTitle = benchmarkTitle;
3283      return *this;
3284  }
3285  
3286  std::string const& Bench::title() const noexcept {
3287      return mConfig.mBenchmarkTitle;
3288  }
3289  
3290  Bench& Bench::name(const char* benchmarkName) {
3291      mConfig.mBenchmarkName = benchmarkName;
3292      return *this;
3293  }
3294  
3295  Bench& Bench::name(std::string const& benchmarkName) {
3296      mConfig.mBenchmarkName = benchmarkName;
3297      return *this;
3298  }
3299  
3300  std::string const& Bench::name() const noexcept {
3301      return mConfig.mBenchmarkName;
3302  }
3303  
3304  Bench& Bench::context(char const* variableName, char const* variableValue) {
3305      mConfig.mContext[variableName] = variableValue;
3306      return *this;
3307  }
3308  
3309  Bench& Bench::context(std::string const& variableName, std::string const& variableValue) {
3310      mConfig.mContext[variableName] = variableValue;
3311      return *this;
3312  }
3313  
3314  Bench& Bench::clearContext() {
3315      mConfig.mContext.clear();
3316      return *this;
3317  }
3318  
3319  // Number of epochs to evaluate. The reported result will be the median of evaluation of each epoch.
3320  Bench& Bench::epochs(size_t numEpochs) noexcept {
3321      mConfig.mNumEpochs = numEpochs;
3322      return *this;
3323  }
3324  size_t Bench::epochs() const noexcept {
3325      return mConfig.mNumEpochs;
3326  }
3327  
3328  // Desired evaluation time is a multiple of clock resolution. Default is to be 1000 times above this measurement precision.
3329  Bench& Bench::clockResolutionMultiple(size_t multiple) noexcept {
3330      mConfig.mClockResolutionMultiple = multiple;
3331      return *this;
3332  }
3333  size_t Bench::clockResolutionMultiple() const noexcept {
3334      return mConfig.mClockResolutionMultiple;
3335  }
3336  
3337  // Sets the maximum time each epoch should take. Default is 100ms.
3338  Bench& Bench::maxEpochTime(std::chrono::nanoseconds t) noexcept {
3339      mConfig.mMaxEpochTime = t;
3340      return *this;
3341  }
3342  std::chrono::nanoseconds Bench::maxEpochTime() const noexcept {
3343      return mConfig.mMaxEpochTime;
3344  }
3345  
3346  // Sets the maximum time each epoch should take. Default is 100ms.
3347  Bench& Bench::minEpochTime(std::chrono::nanoseconds t) noexcept {
3348      mConfig.mMinEpochTime = t;
3349      return *this;
3350  }
3351  std::chrono::nanoseconds Bench::minEpochTime() const noexcept {
3352      return mConfig.mMinEpochTime;
3353  }
3354  
3355  Bench& Bench::minEpochIterations(uint64_t numIters) noexcept {
3356      mConfig.mMinEpochIterations = (numIters == 0) ? 1 : numIters;
3357      return *this;
3358  }
3359  uint64_t Bench::minEpochIterations() const noexcept {
3360      return mConfig.mMinEpochIterations;
3361  }
3362  
3363  Bench& Bench::epochIterations(uint64_t numIters) noexcept {
3364      mConfig.mEpochIterations = numIters;
3365      return *this;
3366  }
3367  uint64_t Bench::epochIterations() const noexcept {
3368      return mConfig.mEpochIterations;
3369  }
3370  
3371  Bench& Bench::warmup(uint64_t numWarmupIters) noexcept {
3372      mConfig.mWarmup = numWarmupIters;
3373      return *this;
3374  }
3375  uint64_t Bench::warmup() const noexcept {
3376      return mConfig.mWarmup;
3377  }
3378  
3379  Bench& Bench::config(Config const& benchmarkConfig) {
3380      mConfig = benchmarkConfig;
3381      return *this;
3382  }
3383  Config const& Bench::config() const noexcept {
3384      return mConfig;
3385  }
3386  
3387  Bench& Bench::output(std::ostream* outstream) noexcept {
3388      mConfig.mOut = outstream;
3389      return *this;
3390  }
3391  
3392  ANKERL_NANOBENCH(NODISCARD) std::ostream* Bench::output() const noexcept {
3393      return mConfig.mOut;
3394  }
3395  
3396  std::vector<Result> const& Bench::results() const noexcept {
3397      return mResults;
3398  }
3399  
3400  Bench& Bench::render(char const* templateContent, std::ostream& os) {
3401      ::ankerl::nanobench::render(templateContent, *this, os);
3402      return *this;
3403  }
3404  
3405  Bench& Bench::render(std::string const& templateContent, std::ostream& os) {
3406      ::ankerl::nanobench::render(templateContent, *this, os);
3407      return *this;
3408  }
3409  
3410  std::vector<BigO> Bench::complexityBigO() const {
3411      std::vector<BigO> bigOs;
3412      auto rangeMeasure = BigO::collectRangeMeasure(mResults);
3413      bigOs.emplace_back("O(1)", rangeMeasure, [](double) {
3414          return 1.0;
3415      });
3416      bigOs.emplace_back("O(n)", rangeMeasure, [](double n) {
3417          return n;
3418      });
3419      bigOs.emplace_back("O(log n)", rangeMeasure, [](double n) {
3420          return std::log2(n);
3421      });
3422      bigOs.emplace_back("O(n log n)", rangeMeasure, [](double n) {
3423          return n * std::log2(n);
3424      });
3425      bigOs.emplace_back("O(n^2)", rangeMeasure, [](double n) {
3426          return n * n;
3427      });
3428      bigOs.emplace_back("O(n^3)", rangeMeasure, [](double n) {
3429          return n * n * n;
3430      });
3431      std::sort(bigOs.begin(), bigOs.end());
3432      return bigOs;
3433  }
3434  
3435  Rng::Rng()
3436      : mX(0)
3437      , mY(0) {
3438      std::random_device rd;
3439      std::uniform_int_distribution<uint64_t> dist;
3440      do {
3441          mX = dist(rd);
3442          mY = dist(rd);
3443      } while (mX == 0 && mY == 0);
3444  }
3445  
3446  ANKERL_NANOBENCH_NO_SANITIZE("integer", "undefined")
3447  uint64_t splitMix64(uint64_t& state) noexcept {
3448      uint64_t z = (state += UINT64_C(0x9e3779b97f4a7c15));
3449      z = (z ^ (z >> 30U)) * UINT64_C(0xbf58476d1ce4e5b9);
3450      z = (z ^ (z >> 27U)) * UINT64_C(0x94d049bb133111eb);
3451      return z ^ (z >> 31U);
3452  }
3453  
3454  // Seeded as described in romu paper (update april 2020)
3455  Rng::Rng(uint64_t seed) noexcept
3456      : mX(splitMix64(seed))
3457      , mY(splitMix64(seed)) {
3458      for (size_t i = 0; i < 10; ++i) {
3459          operator()();
3460      }
3461  }
3462  
3463  // only internally used to copy the RNG.
3464  Rng::Rng(uint64_t x, uint64_t y) noexcept
3465      : mX(x)
3466      , mY(y) {}
3467  
3468  Rng Rng::copy() const noexcept {
3469      return Rng{mX, mY};
3470  }
3471  
3472  Rng::Rng(std::vector<uint64_t> const& data)
3473      : mX(0)
3474      , mY(0) {
3475      if (data.size() != 2) {
3476          throw std::runtime_error("ankerl::nanobench::Rng::Rng: needed exactly 2 entries in data, but got " +
3477                                   detail::fmt::to_s(data.size()));
3478      }
3479      mX = data[0];
3480      mY = data[1];
3481  }
3482  
3483  std::vector<uint64_t> Rng::state() const {
3484      std::vector<uint64_t> data(2);
3485      data[0] = mX;
3486      data[1] = mY;
3487      return data;
3488  }
3489  
3490  BigO::RangeMeasure BigO::collectRangeMeasure(std::vector<Result> const& results) {
3491      BigO::RangeMeasure rangeMeasure;
3492      for (auto const& result : results) {
3493          if (result.config().mComplexityN > 0.0) {
3494              rangeMeasure.emplace_back(result.config().mComplexityN, result.median(Result::Measure::elapsed));
3495          }
3496      }
3497      return rangeMeasure;
3498  }
3499  
3500  BigO::BigO(std::string bigOName, RangeMeasure const& rangeMeasure)
3501      : mName(std::move(bigOName)) {
3502  
3503      // estimate the constant factor
3504      double sumRangeMeasure = 0.0;
3505      double sumRangeRange = 0.0;
3506  
3507      for (const auto& rm : rangeMeasure) {
3508          sumRangeMeasure += rm.first * rm.second;
3509          sumRangeRange += rm.first * rm.first;
3510      }
3511      mConstant = sumRangeMeasure / sumRangeRange;
3512  
3513      // calculate root mean square
3514      double err = 0.0;
3515      double sumMeasure = 0.0;
3516      for (const auto& rm : rangeMeasure) {
3517          auto diff = mConstant * rm.first - rm.second;
3518          err += diff * diff;
3519  
3520          sumMeasure += rm.second;
3521      }
3522  
3523      auto n = detail::d(rangeMeasure.size());
3524      auto mean = sumMeasure / n;
3525      mNormalizedRootMeanSquare = std::sqrt(err / n) / mean;
3526  }
3527  
3528  BigO::BigO(const char* bigOName, RangeMeasure const& rangeMeasure)
3529      : BigO(std::string(bigOName), rangeMeasure) {}
3530  
3531  std::string const& BigO::name() const noexcept {
3532      return mName;
3533  }
3534  
3535  double BigO::constant() const noexcept {
3536      return mConstant;
3537  }
3538  
3539  double BigO::normalizedRootMeanSquare() const noexcept {
3540      return mNormalizedRootMeanSquare;
3541  }
3542  
3543  bool BigO::operator<(BigO const& other) const noexcept {
3544      return std::tie(mNormalizedRootMeanSquare, mName) < std::tie(other.mNormalizedRootMeanSquare, other.mName);
3545  }
3546  
3547  std::ostream& operator<<(std::ostream& os, BigO const& bigO) {
3548      return os << bigO.constant() << " * " << bigO.name() << ", rms=" << bigO.normalizedRootMeanSquare();
3549  }
3550  
3551  std::ostream& operator<<(std::ostream& os, std::vector<ankerl::nanobench::BigO> const& bigOs) {
3552      detail::fmt::StreamStateRestorer const restorer(os);
3553      os << std::endl << "|   coefficient |   err% | complexity" << std::endl << "|--------------:|-------:|------------" << std::endl;
3554      for (auto const& bigO : bigOs) {
3555          os << "|" << std::setw(14) << std::setprecision(7) << std::scientific << bigO.constant() << " ";
3556          os << "|" << detail::fmt::Number(6, 1, bigO.normalizedRootMeanSquare() * 100.0) << "% ";
3557          os << "| " << bigO.name();
3558          os << std::endl;
3559      }
3560      return os;
3561  }
3562  
3563  } // namespace nanobench
3564  } // namespace ankerl
3565  
3566  #endif // ANKERL_NANOBENCH_IMPLEMENT
3567  #endif // ANKERL_NANOBENCH_H_INCLUDED
3568