/* * Created by Joachim on 16/04/2019. * Adapted from donated nonius code. * * Distributed under the Boost Software License, Version 1.0. (See accompanying * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */ // Environment measurement #ifndef TWOBLUECUBES_CATCH_DETAIL_ESTIMATE_CLOCK_HPP_INCLUDED #define TWOBLUECUBES_CATCH_DETAIL_ESTIMATE_CLOCK_HPP_INCLUDED #include "../catch_clock.hpp" #include "../catch_environment.hpp" #include "catch_stats.hpp" #include "catch_measure.hpp" #include "catch_run_for_at_least.hpp" #include "../catch_clock.hpp" #include <algorithm> #include <iterator> #include <tuple> #include <vector> #include <cmath> namespace Catch { namespace Benchmark { namespace Detail { template <typename Clock> std::vector<double> resolution(int k) { std::vector<TimePoint<Clock>> times; times.reserve(k + 1); std::generate_n(std::back_inserter(times), k + 1, now<Clock>{}); std::vector<double> deltas; deltas.reserve(k); std::transform(std::next(times.begin()), times.end(), times.begin(), std::back_inserter(deltas), [](TimePoint<Clock> a, TimePoint<Clock> b) { return static_cast<double>((a - b).count()); }); return deltas; } const auto warmup_iterations = 10000; const auto warmup_time = std::chrono::milliseconds(100); const auto minimum_ticks = 1000; const auto warmup_seed = 10000; const auto clock_resolution_estimation_time = std::chrono::milliseconds(500); const auto clock_cost_estimation_time_limit = std::chrono::seconds(1); const auto clock_cost_estimation_tick_limit = 100000; const auto clock_cost_estimation_time = std::chrono::milliseconds(10); const auto clock_cost_estimation_iterations = 10000; template <typename Clock> int warmup() { return run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(warmup_time), warmup_seed, &resolution<Clock>) .iterations; } template <typename Clock> EnvironmentEstimate<FloatDuration<Clock>> estimate_clock_resolution(int iterations) { auto r = run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(clock_resolution_estimation_time), iterations, &resolution<Clock>) .result; return { FloatDuration<Clock>(mean(r.begin(), r.end())), classify_outliers(r.begin(), r.end()), }; } template <typename Clock> EnvironmentEstimate<FloatDuration<Clock>> estimate_clock_cost(FloatDuration<Clock> resolution) { auto time_limit = (std::min)( resolution * clock_cost_estimation_tick_limit, FloatDuration<Clock>(clock_cost_estimation_time_limit)); auto time_clock = [](int k) { return Detail::measure<Clock>([k] { for (int i = 0; i < k; ++i) { volatile auto ignored = Clock::now(); (void)ignored; } }).elapsed; }; time_clock(1); int iters = clock_cost_estimation_iterations; auto&& r = run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(clock_cost_estimation_time), iters, time_clock); std::vector<double> times; int nsamples = static_cast<int>(std::ceil(time_limit / r.elapsed)); times.reserve(nsamples); std::generate_n(std::back_inserter(times), nsamples, [time_clock, &r] { return static_cast<double>((time_clock(r.iterations) / r.iterations).count()); }); return { FloatDuration<Clock>(mean(times.begin(), times.end())), classify_outliers(times.begin(), times.end()), }; } template <typename Clock> Environment<FloatDuration<Clock>> measure_environment() { static Environment<FloatDuration<Clock>>* env = nullptr; if (env) { return *env; } auto iters = Detail::warmup<Clock>(); auto resolution = Detail::estimate_clock_resolution<Clock>(iters); auto cost = Detail::estimate_clock_cost<Clock>(resolution.mean); env = new Environment<FloatDuration<Clock>>{ resolution, cost }; return *env; } } // namespace Detail } // namespace Benchmark } // namespace Catch #endif // TWOBLUECUBES_CATCH_DETAIL_ESTIMATE_CLOCK_HPP_INCLUDED