1 /* 2 * Created by Joachim on 16/04/2019. 3 * Adapted from donated nonius code. 4 * 5 * Distributed under the Boost Software License, Version 1.0. (See accompanying 6 * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 7 */ 8 9 // Benchmark 10 #ifndef TWOBLUECUBES_CATCH_BENCHMARK_HPP_INCLUDED 11 #define TWOBLUECUBES_CATCH_BENCHMARK_HPP_INCLUDED 12 13 #include "../catch_config.hpp" 14 #include "../catch_context.h" 15 #include "../catch_interfaces_reporter.h" 16 #include "../catch_test_registry.h" 17 18 #include "catch_chronometer.hpp" 19 #include "catch_clock.hpp" 20 #include "catch_environment.hpp" 21 #include "catch_execution_plan.hpp" 22 #include "detail/catch_estimate_clock.hpp" 23 #include "detail/catch_complete_invoke.hpp" 24 #include "detail/catch_analyse.hpp" 25 #include "detail/catch_benchmark_function.hpp" 26 #include "detail/catch_run_for_at_least.hpp" 27 28 #include <algorithm> 29 #include <functional> 30 #include <string> 31 #include <vector> 32 #include <cmath> 33 34 namespace Catch { 35 namespace Benchmark { 36 struct Benchmark { BenchmarkCatch::Benchmark::Benchmark37 Benchmark(std::string &&name) 38 : name(std::move(name)) {} 39 40 template <class FUN> BenchmarkCatch::Benchmark::Benchmark41 Benchmark(std::string &&name, FUN &&func) 42 : fun(std::move(func)), name(std::move(name)) {} 43 44 template <typename Clock> prepareCatch::Benchmark::Benchmark45 ExecutionPlan<FloatDuration<Clock>> prepare(const IConfig &cfg, Environment<FloatDuration<Clock>> env) const { 46 auto min_time = env.clock_resolution.mean * Detail::minimum_ticks; 47 auto run_time = std::max(min_time, std::chrono::duration_cast<decltype(min_time)>(cfg.benchmarkWarmupTime())); 48 auto&& test = Detail::run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(run_time), 1, fun); 49 int new_iters = static_cast<int>(std::ceil(min_time * test.iterations / test.elapsed)); 50 return { new_iters, test.elapsed / test.iterations * new_iters * cfg.benchmarkSamples(), fun, std::chrono::duration_cast<FloatDuration<Clock>>(cfg.benchmarkWarmupTime()), Detail::warmup_iterations }; 51 } 52 53 template <typename Clock = default_clock> runCatch::Benchmark::Benchmark54 void run() { 55 IConfigPtr cfg = getCurrentContext().getConfig(); 56 57 auto env = Detail::measure_environment<Clock>(); 58 59 getResultCapture().benchmarkPreparing(name); 60 CATCH_TRY{ 61 auto plan = user_code([&] { 62 return prepare<Clock>(*cfg, env); 63 }); 64 65 BenchmarkInfo info { 66 name, 67 plan.estimated_duration.count(), 68 plan.iterations_per_sample, 69 cfg->benchmarkSamples(), 70 cfg->benchmarkResamples(), 71 env.clock_resolution.mean.count(), 72 env.clock_cost.mean.count() 73 }; 74 75 getResultCapture().benchmarkStarting(info); 76 77 auto samples = user_code([&] { 78 return plan.template run<Clock>(*cfg, env); 79 }); 80 81 auto analysis = Detail::analyse(*cfg, env, samples.begin(), samples.end()); 82 BenchmarkStats<FloatDuration<Clock>> stats{ info, analysis.samples, analysis.mean, analysis.standard_deviation, analysis.outliers, analysis.outlier_variance }; 83 getResultCapture().benchmarkEnded(stats); 84 85 } CATCH_CATCH_ALL{ 86 if (translateActiveException() != Detail::benchmarkErrorMsg) // benchmark errors have been reported, otherwise rethrow. 87 std::rethrow_exception(std::current_exception()); 88 } 89 } 90 91 // sets lambda to be used in fun *and* executes benchmark! 92 template <typename Fun, 93 typename std::enable_if<!Detail::is_related<Fun, Benchmark>::value, int>::type = 0> operator =Catch::Benchmark::Benchmark94 Benchmark & operator=(Fun func) { 95 fun = Detail::BenchmarkFunction(func); 96 run(); 97 return *this; 98 } 99 operator boolCatch::Benchmark::Benchmark100 explicit operator bool() { 101 return true; 102 } 103 104 private: 105 Detail::BenchmarkFunction fun; 106 std::string name; 107 }; 108 } 109 } // namespace Catch 110 111 #define INTERNAL_CATCH_GET_1_ARG(arg1, arg2, ...) arg1 112 #define INTERNAL_CATCH_GET_2_ARG(arg1, arg2, ...) arg2 113 114 #define INTERNAL_CATCH_BENCHMARK(BenchmarkName, name, benchmarkIndex)\ 115 if( Catch::Benchmark::Benchmark BenchmarkName{name} ) \ 116 BenchmarkName = [&](int benchmarkIndex) 117 118 #define INTERNAL_CATCH_BENCHMARK_ADVANCED(BenchmarkName, name)\ 119 if( Catch::Benchmark::Benchmark BenchmarkName{name} ) \ 120 BenchmarkName = [&] 121 122 #endif // TWOBLUECUBES_CATCH_BENCHMARK_HPP_INCLUDED 123