Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/release-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
### Fixes
* Fixed `--warn InfiniteGenerators` triggering even if the generator was limited to specific element via path filtering.
* Fixed `-Wunused-parameter` triggering in `-fnoexceptions` builds.
* Fixed benchmark clock-resolution estimation potentially exhausting memory on fast or memory-constrained platforms. (#3180)

### Improvements
* `catch_discover_tests` can handle cases where the binary prints out non-Catch2 output due to global constructors (#3162)
Expand Down
42 changes: 26 additions & 16 deletions src/catch2/benchmark/detail/catch_estimate_clock.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,14 @@ namespace Catch {
namespace Detail {
template <typename Clock>
std::vector<double> resolution(int k) {
const size_t points = static_cast<size_t>( k + 1 );
// To avoid overhead from the branch inside vector::push_back,
// we allocate them all and then overwrite.
std::vector<TimePoint<Clock>> times(points);
for ( auto& time : times ) {
time = Clock::now();
}

std::vector<double> deltas;
deltas.reserve(static_cast<size_t>(k));
for ( size_t idx = 1; idx < points; ++idx ) {
auto previous = Clock::now();
for ( int idx = 0; idx < k; ++idx ) {
auto current = Clock::now();
deltas.push_back( static_cast<double>(
( times[idx] - times[idx - 1] ).count() ) );
( current - previous ).count() ) );
previous = current;
}

return deltas;
Expand All @@ -49,6 +44,7 @@ namespace Catch {
constexpr auto minimum_ticks = 1000;
constexpr auto warmup_seed = 10000;
constexpr auto clock_resolution_estimation_time = std::chrono::milliseconds(500);
constexpr auto clock_resolution_estimation_iteration_limit = 100000;
constexpr auto clock_cost_estimation_time_limit = std::chrono::seconds(1);
constexpr auto clock_cost_estimation_tick_limit = 100000;
constexpr auto clock_cost_estimation_time = std::chrono::milliseconds(10);
Expand All @@ -61,12 +57,26 @@ namespace Catch {
}
template <typename Clock>
EnvironmentEstimate estimate_clock_resolution(int iterations) {
auto r = run_for_at_least<Clock>(clock_resolution_estimation_time, iterations, &resolution<Clock>)
.result;
return {
FDuration(mean(r.data(), r.data() + r.size())),
classify_outliers(r.data(), r.data() + r.size()),
};
auto iters = (std::min)( iterations,
clock_resolution_estimation_iteration_limit );
while ( true ) {
auto timing =
Detail::measure<Clock>( &resolution<Clock>, iters );
auto const& r = timing.result;
if ( timing.elapsed >= clock_resolution_estimation_time ||
iters == clock_resolution_estimation_iteration_limit ) {
return {
FDuration(mean(r.data(), r.data() + r.size())),
classify_outliers(r.data(), r.data() + r.size()),
};
}
if ( iters >
clock_resolution_estimation_iteration_limit / 2 ) {
iters = clock_resolution_estimation_iteration_limit;
} else {
iters *= 2;
}
}
}
template <typename Clock>
EnvironmentEstimate estimate_clock_cost(FDuration resolution) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,11 +109,17 @@ TEST_CASE("estimate_clock_resolution", "[benchmark]") {
auto rate = 2'000;
counting_clock::set_rate(rate);

int iters = 160'000;
int iters =
Catch::Benchmark::Detail::clock_resolution_estimation_iteration_limit *
2;
auto res = Catch::Benchmark::Detail::estimate_clock_resolution<counting_clock>(iters);

REQUIRE(res.mean.count() == rate);
REQUIRE(res.outliers.total() == 0);
if ( res.outliers.samples_seen >
Catch::Benchmark::Detail::clock_resolution_estimation_iteration_limit ) {
FAIL( "Clock resolution estimation exceeded the sample limit" );
}
}

TEST_CASE("benchmark function call", "[benchmark]") {
Expand Down