Read-only mirrors. Development, issues and pull requests are on GitHub.

summaryrefslogtreecommitdiffhomepage
path: root/src/catch2
diff options
context:
space:
mode:
authorMartin Hořeňovský2026-08-22 19:00:32 +0200
committerMartin Hořeňovský2026-08-22 23:01:51 +0200
commit69da66ee7370d7c249ba6f2a322a78dcdd623761 (patch)
treea688adc0ac208a44dde27da6b2800b439a0f3b88 /src/catch2
parent45f9ea1e798c2e128dcba53602ccbddfe5676f6f (diff)
downloadcatch3-69da66ee7370d7c249ba6f2a322a78dcdd623761.tar.gz
Don't instantiate the default implementation of benchmarks in every TU
The underlying benchmarking machinery is templated, so that it can use different clock implementations (either for testing, or to support user with platform-specific clocks). However, 99.99% of all users interact with it using the `BENCHMARK` macro, which means that they use it with `std::chrono::steady_clock`. By adding outlined implementation of benchmarking helpers specialized onto `std::chrono::steady_clock`, we save some amount of time per every TU that uses benchmarks.
Diffstat (limited to 'src/catch2')
-rw-r--r--src/catch2/benchmark/catch_benchmark.cpp54
-rw-r--r--src/catch2/benchmark/catch_benchmark.hpp100
-rw-r--r--src/catch2/meson.build1
3 files changed, 143 insertions, 12 deletions
diff --git a/src/catch2/benchmark/catch_benchmark.cpp b/src/catch2/benchmark/catch_benchmark.cpp
new file mode 100644
index 00000000..5a34ed39
--- /dev/null
+++ b/src/catch2/benchmark/catch_benchmark.cpp
@@ -0,0 +1,54 @@
+
+// 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
+
+#include <catch2/benchmark/catch_benchmark.hpp>
+
+#include <algorithm>
+#include <chrono>
+#include <cmath>
+
+namespace Catch {
+ namespace Benchmark {
+ namespace Detail {
+
+ Environment measure_environment_default() {
+ return Detail::measure_environment<default_clock>();
+ }
+
+ ExecutionPlan prepare_default( const IConfig& cfg,
+ Environment env,
+ BenchmarkFunction&& fun ) {
+ // This mirrors Benchmark::prepare<default_clock>(), but with the
+ // clock fixed so it is instantiated once here in the library.
+ auto min_time = env.clock_resolution.mean * Detail::minimum_ticks;
+ auto run_time = std::max(
+ min_time,
+ std::chrono::duration_cast<decltype( min_time )>(
+ cfg.benchmarkWarmupTime() ) );
+ auto&& test = Detail::run_for_at_least<default_clock>(
+ std::chrono::duration_cast<IDuration>( run_time ), 1, fun );
+ int new_iters = static_cast<int>(
+ 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<FDuration>(
+ cfg.benchmarkWarmupTime() ),
+ Detail::warmup_iterations };
+ }
+
+ std::vector<FDuration> run_plan_default( ExecutionPlan const& plan,
+ const IConfig& cfg,
+ Environment env ) {
+ return plan.run<default_clock>( cfg, env );
+ }
+
+ } // namespace Detail
+ } // namespace Benchmark
+} // namespace Catch
diff --git a/src/catch2/benchmark/catch_benchmark.hpp b/src/catch2/benchmark/catch_benchmark.hpp
index ce68aee7..9b879982 100644
--- a/src/catch2/benchmark/catch_benchmark.hpp
+++ b/src/catch2/benchmark/catch_benchmark.hpp
@@ -33,9 +33,94 @@
#include <exception>
#include <string>
#include <cmath>
+#include <type_traits>
namespace Catch {
namespace Benchmark {
+ namespace Detail {
+ template <typename Clock>
+ 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<decltype( min_time )>(
+ cfg.benchmarkWarmupTime() ) );
+ auto&& test = Detail::run_for_at_least<Clock>(
+ std::chrono::duration_cast<IDuration>( run_time ), 1, fun );
+ int new_iters = static_cast<int>(
+ 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<FDuration>(
+ 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<FDuration> run_plan_default( ExecutionPlan const& plan,
+ const IConfig& cfg,
+ Environment env );
+
+ template <typename Clock>
+ std::enable_if_t<std::is_same<Clock, default_clock>::value,
+ Environment>
+ measureEnvironmentDispatch() {
+ return measure_environment_default();
+ }
+ template <typename Clock>
+ std::enable_if_t<!std::is_same<Clock, default_clock>::value,
+ Environment>
+ measureEnvironmentDispatch() {
+ return measure_environment<Clock>();
+ }
+
+ template <typename Clock>
+ std::enable_if_t<std::is_same<Clock, default_clock>::value,
+ std::vector<FDuration>>
+ runPlanDispatch( ExecutionPlan const& plan,
+ const IConfig& cfg,
+ Environment env ) {
+ return run_plan_default( plan, cfg, env );
+ }
+ template <typename Clock>
+ std::enable_if_t<!std::is_same<Clock, default_clock>::value,
+ std::vector<FDuration>>
+ runPlanDispatch( ExecutionPlan const& plan,
+ const IConfig& cfg,
+ Environment env ) {
+ return plan.template run<Clock>( cfg, env );
+ }
+
+ template <typename Clock>
+ std::enable_if_t<std::is_same<Clock, default_clock>::value,
+ ExecutionPlan>
+ prepareDispatch( const IConfig& cfg,
+ Environment env,
+ BenchmarkFunction&& fun ) {
+ return prepare_default( cfg, env, CATCH_MOVE( fun ) );
+ }
+ template <typename Clock>
+ std::enable_if_t<!std::is_same<Clock, default_clock>::value,
+ ExecutionPlan>
+ prepareDispatch( const IConfig& cfg,
+ Environment env,
+ BenchmarkFunction&& fun ) {
+ return prepare<Clock>( cfg, env, CATCH_MOVE( fun ) );
+ }
+ } // namespace Detail
+
struct Benchmark {
Benchmark(std::string&& benchmarkName)
: name(CATCH_MOVE(benchmarkName)) {}
@@ -44,27 +129,18 @@ namespace Catch {
Benchmark(std::string&& benchmarkName , FUN &&func)
: fun(CATCH_MOVE(func)), name(CATCH_MOVE(benchmarkName)) {}
- template <typename Clock>
- ExecutionPlan prepare(const IConfig &cfg, Environment env) {
- auto min_time = env.clock_resolution.mean * Detail::minimum_ticks;
- auto run_time = std::max(min_time, std::chrono::duration_cast<decltype(min_time)>(cfg.benchmarkWarmupTime()));
- auto&& test = Detail::run_for_at_least<Clock>(std::chrono::duration_cast<IDuration>(run_time), 1, fun);
- int new_iters = static_cast<int>(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<FDuration>(cfg.benchmarkWarmupTime()), Detail::warmup_iterations };
- }
-
template <typename Clock = default_clock>
void run() {
static_assert( Clock::is_steady,
"Benchmarking clock should be steady" );
auto const* cfg = getCurrentContext().getConfig();
- auto env = Detail::measure_environment<Clock>();
+ auto env = Detail::measureEnvironmentDispatch<Clock>();
getResultCapture().benchmarkPreparing(name);
CATCH_TRY{
auto plan = user_code([&] {
- return prepare<Clock>(*cfg, env);
+ return Detail::prepareDispatch<Clock>( *cfg, env, CATCH_MOVE(fun) );
});
BenchmarkInfo info {
@@ -80,7 +156,7 @@ namespace Catch {
getResultCapture().benchmarkStarting(info);
auto samples = user_code([&] {
- return plan.template run<Clock>(*cfg, env);
+ return Detail::runPlanDispatch<Clock>( plan, *cfg, env );
});
auto analysis = Detail::analyse(*cfg, samples.data(), samples.data() + samples.size());
diff --git a/src/catch2/meson.build b/src/catch2/meson.build
index 94e0496a..fcffb80e 100644
--- a/src/catch2/meson.build
+++ b/src/catch2/meson.build
@@ -47,6 +47,7 @@ benchmark_headers = [
]
benchmark_sources = files(
+ 'benchmark/catch_benchmark.cpp',
'benchmark/catch_chronometer.cpp',
'benchmark/detail/catch_analyse.cpp',
'benchmark/detail/catch_benchmark_function.cpp',