/ externals / catch / src / catch2 / benchmark / catch_benchmark.hpp
catch_benchmark.hpp
  1  
  2  //              Copyright Catch2 Authors
  3  // Distributed under the Boost Software License, Version 1.0.
  4  //   (See accompanying file LICENSE.txt or copy at
  5  //        https://www.boost.org/LICENSE_1_0.txt)
  6  
  7  // SPDX-License-Identifier: BSL-1.0
  8  // Adapted from donated nonius code.
  9  
 10  #ifndef CATCH_BENCHMARK_HPP_INCLUDED
 11  #define CATCH_BENCHMARK_HPP_INCLUDED
 12  
 13  #include <catch2/catch_user_config.hpp>
 14  #include <catch2/internal/catch_compiler_capabilities.hpp>
 15  #include <catch2/internal/catch_context.hpp>
 16  #include <catch2/internal/catch_move_and_forward.hpp>
 17  #include <catch2/internal/catch_test_failure_exception.hpp>
 18  #include <catch2/internal/catch_unique_name.hpp>
 19  #include <catch2/interfaces/catch_interfaces_capture.hpp>
 20  #include <catch2/interfaces/catch_interfaces_config.hpp>
 21  #include <catch2/interfaces/catch_interfaces_registry_hub.hpp>
 22  #include <catch2/benchmark/detail/catch_benchmark_stats.hpp>
 23  #include <catch2/benchmark/catch_clock.hpp>
 24  #include <catch2/benchmark/catch_environment.hpp>
 25  #include <catch2/benchmark/catch_execution_plan.hpp>
 26  #include <catch2/benchmark/detail/catch_estimate_clock.hpp>
 27  #include <catch2/benchmark/detail/catch_analyse.hpp>
 28  #include <catch2/benchmark/detail/catch_benchmark_function.hpp>
 29  #include <catch2/benchmark/detail/catch_run_for_at_least.hpp>
 30  
 31  #include <algorithm>
 32  #include <chrono>
 33  #include <exception>
 34  #include <string>
 35  #include <cmath>
 36  
 37  namespace Catch {
 38      namespace Benchmark {
 39          struct Benchmark {
 40              Benchmark(std::string&& benchmarkName)
 41                  : name(CATCH_MOVE(benchmarkName)) {}
 42  
 43              template <class FUN>
 44              Benchmark(std::string&& benchmarkName , FUN &&func)
 45                  : fun(CATCH_MOVE(func)), name(CATCH_MOVE(benchmarkName)) {}
 46  
 47              template <typename Clock>
 48              ExecutionPlan prepare(const IConfig &cfg, Environment env) const {
 49                  auto min_time = env.clock_resolution.mean * Detail::minimum_ticks;
 50                  auto run_time = std::max(min_time, std::chrono::duration_cast<decltype(min_time)>(cfg.benchmarkWarmupTime()));
 51                  auto&& test = Detail::run_for_at_least<Clock>(std::chrono::duration_cast<IDuration>(run_time), 1, fun);
 52                  int new_iters = static_cast<int>(std::ceil(min_time * test.iterations / test.elapsed));
 53                  return { new_iters, test.elapsed / test.iterations * new_iters * cfg.benchmarkSamples(), fun, std::chrono::duration_cast<FDuration>(cfg.benchmarkWarmupTime()), Detail::warmup_iterations };
 54              }
 55  
 56              template <typename Clock = default_clock>
 57              void run() {
 58                  static_assert( Clock::is_steady,
 59                                 "Benchmarking clock should be steady" );
 60                  auto const* cfg = getCurrentContext().getConfig();
 61  
 62                  auto env = Detail::measure_environment<Clock>();
 63  
 64                  getResultCapture().benchmarkPreparing(name);
 65                  CATCH_TRY{
 66                      auto plan = user_code([&] {
 67                          return prepare<Clock>(*cfg, env);
 68                      });
 69  
 70                      BenchmarkInfo info {
 71                          CATCH_MOVE(name),
 72                          plan.estimated_duration.count(),
 73                          plan.iterations_per_sample,
 74                          cfg->benchmarkSamples(),
 75                          cfg->benchmarkResamples(),
 76                          env.clock_resolution.mean.count(),
 77                          env.clock_cost.mean.count()
 78                      };
 79  
 80                      getResultCapture().benchmarkStarting(info);
 81  
 82                      auto samples = user_code([&] {
 83                          return plan.template run<Clock>(*cfg, env);
 84                      });
 85  
 86                      auto analysis = Detail::analyse(*cfg, samples.data(), samples.data() + samples.size());
 87                      BenchmarkStats<> stats{ CATCH_MOVE(info), CATCH_MOVE(analysis.samples), analysis.mean, analysis.standard_deviation, analysis.outliers, analysis.outlier_variance };
 88                      getResultCapture().benchmarkEnded(stats);
 89                  } CATCH_CATCH_ANON (TestFailureException const&) {
 90                      getResultCapture().benchmarkFailed("Benchmark failed due to failed assertion"_sr);
 91                  } CATCH_CATCH_ALL{
 92                      getResultCapture().benchmarkFailed(translateActiveException());
 93                      // We let the exception go further up so that the
 94                      // test case is marked as failed.
 95                      std::rethrow_exception(std::current_exception());
 96                  }
 97              }
 98  
 99              // sets lambda to be used in fun *and* executes benchmark!
100              template <typename Fun, std::enable_if_t<!Detail::is_related<Fun, Benchmark>::value, int> = 0>
101                  Benchmark & operator=(Fun func) {
102                  auto const* cfg = getCurrentContext().getConfig();
103                  if (!cfg->skipBenchmarks()) {
104                      fun = Detail::BenchmarkFunction(func);
105                      run();
106                  }
107                  return *this;
108              }
109  
110              explicit operator bool() {
111                  return true;
112              }
113  
114          private:
115              Detail::BenchmarkFunction fun;
116              std::string name;
117          };
118      }
119  } // namespace Catch
120  
121  #define INTERNAL_CATCH_GET_1_ARG(arg1, arg2, ...) arg1
122  #define INTERNAL_CATCH_GET_2_ARG(arg1, arg2, ...) arg2
123  
124  #define INTERNAL_CATCH_BENCHMARK(BenchmarkName, name, benchmarkIndex)\
125      if( Catch::Benchmark::Benchmark BenchmarkName{name} ) \
126          BenchmarkName = [&](int benchmarkIndex)
127  
128  #define INTERNAL_CATCH_BENCHMARK_ADVANCED(BenchmarkName, name)\
129      if( Catch::Benchmark::Benchmark BenchmarkName{name} ) \
130          BenchmarkName = [&]
131  
132  #if defined(CATCH_CONFIG_PREFIX_ALL)
133  
134  #define CATCH_BENCHMARK(...) \
135      INTERNAL_CATCH_BENCHMARK(INTERNAL_CATCH_UNIQUE_NAME(CATCH2_INTERNAL_BENCHMARK_), INTERNAL_CATCH_GET_1_ARG(__VA_ARGS__,,), INTERNAL_CATCH_GET_2_ARG(__VA_ARGS__,,))
136  #define CATCH_BENCHMARK_ADVANCED(name) \
137      INTERNAL_CATCH_BENCHMARK_ADVANCED(INTERNAL_CATCH_UNIQUE_NAME(CATCH2_INTERNAL_BENCHMARK_), name)
138  
139  #else
140  
141  #define BENCHMARK(...) \
142      INTERNAL_CATCH_BENCHMARK(INTERNAL_CATCH_UNIQUE_NAME(CATCH2_INTERNAL_BENCHMARK_), INTERNAL_CATCH_GET_1_ARG(__VA_ARGS__,,), INTERNAL_CATCH_GET_2_ARG(__VA_ARGS__,,))
143  #define BENCHMARK_ADVANCED(name) \
144      INTERNAL_CATCH_BENCHMARK_ADVANCED(INTERNAL_CATCH_UNIQUE_NAME(CATCH2_INTERNAL_BENCHMARK_), name)
145  
146  #endif
147  
148  #endif // CATCH_BENCHMARK_HPP_INCLUDED