// Copyright Catch2 Authors // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE.txt or copy at // https://www.boost.org/LICENSE_1_0.txt) // SPDX-License-Identifier: BSL-1.0 // Adapted from donated nonius code. #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Catch { namespace Benchmark { namespace Detail { template ExecutionPlan prepare( const IConfig& cfg, Environment env, BenchmarkFunction&& fun ) { auto min_time = env.clock_resolution.mean * Detail::minimum_ticks; auto run_time = std::max( min_time, std::chrono::duration_cast( cfg.benchmarkWarmupTime() ) ); auto&& test = Detail::run_for_at_least( std::chrono::duration_cast( run_time ), 1, fun ); int new_iters = static_cast( std::ceil( min_time * test.iterations / test.elapsed ) ); return { new_iters, test.elapsed / test.iterations * new_iters * cfg.benchmarkSamples(), CATCH_MOVE( fun ), std::chrono::duration_cast( cfg.benchmarkWarmupTime() ), Detail::warmup_iterations }; } // These are wrappers for their respective function templated // over `default_clock`. This allows outlining the usual use // of the template into single TU and save on compilation costs. Environment measure_environment_default(); ExecutionPlan prepare_default( const IConfig& cfg, Environment env, BenchmarkFunction&& fun ); std::vector run_plan_default( ExecutionPlan const& plan, const IConfig& cfg, Environment env ); template std::enable_if_t::value, Environment> measureEnvironmentDispatch() { return measure_environment_default(); } template std::enable_if_t::value, Environment> measureEnvironmentDispatch() { return measure_environment(); } template std::enable_if_t::value, std::vector> runPlanDispatch( ExecutionPlan const& plan, const IConfig& cfg, Environment env ) { return run_plan_default( plan, cfg, env ); } template std::enable_if_t::value, std::vector> runPlanDispatch( ExecutionPlan const& plan, const IConfig& cfg, Environment env ) { return plan.template run( cfg, env ); } template std::enable_if_t::value, ExecutionPlan> prepareDispatch( const IConfig& cfg, Environment env, BenchmarkFunction&& fun ) { return prepare_default( cfg, env, CATCH_MOVE( fun ) ); } template std::enable_if_t::value, ExecutionPlan> prepareDispatch( const IConfig& cfg, Environment env, BenchmarkFunction&& fun ) { return prepare( cfg, env, CATCH_MOVE( fun ) ); } } // namespace Detail struct Benchmark { Benchmark(std::string&& benchmarkName) : name(CATCH_MOVE(benchmarkName)) {} template Benchmark(std::string&& benchmarkName , FUN &&func) : fun(CATCH_MOVE(func)), name(CATCH_MOVE(benchmarkName)) {} template void run() { static_assert( Clock::is_steady, "Benchmarking clock should be steady" ); auto const* cfg = getCurrentContext().getConfig(); auto env = Detail::measureEnvironmentDispatch(); getResultCapture().benchmarkPreparing(name); CATCH_TRY{ auto plan = user_code([&] { return Detail::prepareDispatch( *cfg, env, CATCH_MOVE(fun) ); }); BenchmarkInfo info { CATCH_MOVE(name), plan.estimated_duration.count(), plan.iterations_per_sample, cfg->benchmarkSamples(), cfg->benchmarkResamples(), env.clock_resolution.mean.count(), env.clock_cost.mean.count() }; getResultCapture().benchmarkStarting(info); auto samples = user_code([&] { return Detail::runPlanDispatch( plan, *cfg, env ); }); auto analysis = Detail::analyse(*cfg, samples.data(), samples.data() + samples.size()); BenchmarkStats<> stats{ CATCH_MOVE(info), CATCH_MOVE(analysis.samples), analysis.mean, analysis.standard_deviation, analysis.outliers, analysis.outlier_variance }; getResultCapture().benchmarkEnded(stats); } CATCH_CATCH_ALL { getResultCapture().benchmarkFailed(translateActiveException()); // We let the exception go further up so that the // test case is marked as failed. std::rethrow_exception(std::current_exception()); } } // sets lambda to be used in fun *and* executes benchmark! template , int> = 0> Benchmark & operator=(Fun func) { auto const* cfg = getCurrentContext().getConfig(); if (!cfg->skipBenchmarks()) { fun = Detail::BenchmarkFunction(func); run(); } return *this; } explicit operator bool() { return true; } private: Detail::BenchmarkFunction fun; std::string name; }; } } // namespace Catch #define INTERNAL_CATCH_GET_1_ARG(arg1, arg2, ...) arg1 #define INTERNAL_CATCH_GET_2_ARG(arg1, arg2, ...) arg2 #define INTERNAL_CATCH_BENCHMARK(BenchmarkName, name, benchmarkIndex)\ if( Catch::Benchmark::Benchmark BenchmarkName{name} ) \ BenchmarkName = [&](int benchmarkIndex) #define INTERNAL_CATCH_BENCHMARK_ADVANCED(BenchmarkName, name)\ if( Catch::Benchmark::Benchmark BenchmarkName{name} ) \ BenchmarkName = [&] #if defined(CATCH_CONFIG_PREFIX_ALL) #define CATCH_BENCHMARK(...) \ INTERNAL_CATCH_BENCHMARK(INTERNAL_CATCH_UNIQUE_NAME(CATCH2_INTERNAL_BENCHMARK_), INTERNAL_CATCH_GET_1_ARG(__VA_ARGS__,,), INTERNAL_CATCH_GET_2_ARG(__VA_ARGS__,,)) #define CATCH_BENCHMARK_ADVANCED(name) \ INTERNAL_CATCH_BENCHMARK_ADVANCED(INTERNAL_CATCH_UNIQUE_NAME(CATCH2_INTERNAL_BENCHMARK_), name) #else #define BENCHMARK(...) \ INTERNAL_CATCH_BENCHMARK(INTERNAL_CATCH_UNIQUE_NAME(CATCH2_INTERNAL_BENCHMARK_), INTERNAL_CATCH_GET_1_ARG(__VA_ARGS__,,), INTERNAL_CATCH_GET_2_ARG(__VA_ARGS__,,)) #define BENCHMARK_ADVANCED(name) \ INTERNAL_CATCH_BENCHMARK_ADVANCED(INTERNAL_CATCH_UNIQUE_NAME(CATCH2_INTERNAL_BENCHMARK_), name) #endif