• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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