catch_estimate_clock.hpp 4.9 KB

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