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 // Environment measurement 10 11 #ifndef TWOBLUECUBES_CATCH_DETAIL_ESTIMATE_CLOCK_HPP_INCLUDED 12 #define TWOBLUECUBES_CATCH_DETAIL_ESTIMATE_CLOCK_HPP_INCLUDED 13 14 #include "../catch_clock.hpp" 15 #include "../catch_environment.hpp" 16 #include "catch_stats.hpp" 17 #include "catch_measure.hpp" 18 #include "catch_run_for_at_least.hpp" 19 #include "../catch_clock.hpp" 20 21 #include <algorithm> 22 #include <iterator> 23 #include <tuple> 24 #include <vector> 25 #include <cmath> 26 27 namespace Catch { 28 namespace Benchmark { 29 namespace Detail { 30 template <typename Clock> resolution(int k)31 std::vector<double> resolution(int k) { 32 std::vector<TimePoint<Clock>> times; 33 times.reserve(k + 1); 34 std::generate_n(std::back_inserter(times), k + 1, now<Clock>{}); 35 36 std::vector<double> deltas; 37 deltas.reserve(k); 38 std::transform(std::next(times.begin()), times.end(), times.begin(), 39 std::back_inserter(deltas), 40 [](TimePoint<Clock> a, TimePoint<Clock> b) { return static_cast<double>((a - b).count()); }); 41 42 return deltas; 43 } 44 45 const auto warmup_iterations = 10000; 46 const auto warmup_time = std::chrono::milliseconds(100); 47 const auto minimum_ticks = 1000; 48 const auto warmup_seed = 10000; 49 const auto clock_resolution_estimation_time = std::chrono::milliseconds(500); 50 const auto clock_cost_estimation_time_limit = std::chrono::seconds(1); 51 const auto clock_cost_estimation_tick_limit = 100000; 52 const auto clock_cost_estimation_time = std::chrono::milliseconds(10); 53 const auto clock_cost_estimation_iterations = 10000; 54 55 template <typename Clock> warmup()56 int warmup() { 57 return run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(warmup_time), warmup_seed, &resolution<Clock>) 58 .iterations; 59 } 60 template <typename Clock> estimate_clock_resolution(int iterations)61 EnvironmentEstimate<FloatDuration<Clock>> estimate_clock_resolution(int iterations) { 62 auto r = run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(clock_resolution_estimation_time), iterations, &resolution<Clock>) 63 .result; 64 return { 65 FloatDuration<Clock>(mean(r.begin(), r.end())), 66 classify_outliers(r.begin(), r.end()), 67 }; 68 } 69 template <typename Clock> estimate_clock_cost(FloatDuration<Clock> resolution)70 EnvironmentEstimate<FloatDuration<Clock>> estimate_clock_cost(FloatDuration<Clock> resolution) { 71 auto time_limit = std::min(resolution * clock_cost_estimation_tick_limit, FloatDuration<Clock>(clock_cost_estimation_time_limit)); 72 auto time_clock = [](int k) { 73 return Detail::measure<Clock>([k] { 74 for (int i = 0; i < k; ++i) { 75 volatile auto ignored = Clock::now(); 76 (void)ignored; 77 } 78 }).elapsed; 79 }; 80 time_clock(1); 81 int iters = clock_cost_estimation_iterations; 82 auto&& r = run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(clock_cost_estimation_time), iters, time_clock); 83 std::vector<double> times; 84 int nsamples = static_cast<int>(std::ceil(time_limit / r.elapsed)); 85 times.reserve(nsamples); 86 std::generate_n(std::back_inserter(times), nsamples, [time_clock, &r] { 87 return static_cast<double>((time_clock(r.iterations) / r.iterations).count()); 88 }); 89 return { 90 FloatDuration<Clock>(mean(times.begin(), times.end())), 91 classify_outliers(times.begin(), times.end()), 92 }; 93 } 94 95 template <typename Clock> measure_environment()96 Environment<FloatDuration<Clock>> measure_environment() { 97 static Environment<FloatDuration<Clock>>* env = nullptr; 98 if (env) { 99 return *env; 100 } 101 102 auto iters = Detail::warmup<Clock>(); 103 auto resolution = Detail::estimate_clock_resolution<Clock>(iters); 104 auto cost = Detail::estimate_clock_cost<Clock>(resolution.mean); 105 106 env = new Environment<FloatDuration<Clock>>{ resolution, cost }; 107 return *env; 108 } 109 } // namespace Detail 110 } // namespace Benchmark 111 } // namespace Catch 112 113 #endif // TWOBLUECUBES_CATCH_DETAIL_ESTIMATE_CLOCK_HPP_INCLUDED 114