InternalBenchmark.tests.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  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. #include "catch.hpp"
  9. #if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
  10. namespace {
  11. struct manual_clock {
  12. public:
  13. using duration = std::chrono::nanoseconds;
  14. using time_point = std::chrono::time_point<manual_clock, duration>;
  15. using rep = duration::rep;
  16. using period = duration::period;
  17. enum { is_steady = true };
  18. static time_point now() {
  19. return time_point(duration(tick()));
  20. }
  21. static void advance(int ticks = 1) {
  22. tick() += ticks;
  23. }
  24. private:
  25. static rep& tick() {
  26. static rep the_tick = 0;
  27. return the_tick;
  28. }
  29. };
  30. struct counting_clock {
  31. public:
  32. using duration = std::chrono::nanoseconds;
  33. using time_point = std::chrono::time_point<counting_clock, duration>;
  34. using rep = duration::rep;
  35. using period = duration::period;
  36. enum { is_steady = true };
  37. static time_point now() {
  38. static rep ticks = 0;
  39. return time_point(duration(ticks += rate()));
  40. }
  41. static void set_rate(rep new_rate) { rate() = new_rate; }
  42. private:
  43. static rep& rate() {
  44. static rep the_rate = 1;
  45. return the_rate;
  46. }
  47. };
  48. struct TestChronometerModel : Catch::Benchmark::Detail::ChronometerConcept {
  49. int started = 0;
  50. int finished = 0;
  51. void start() override { ++started; }
  52. void finish() override { ++finished; }
  53. };
  54. } // namespace
  55. TEST_CASE("warmup", "[benchmark]") {
  56. auto rate = 1000;
  57. counting_clock::set_rate(rate);
  58. auto start = counting_clock::now();
  59. auto iterations = Catch::Benchmark::Detail::warmup<counting_clock>();
  60. auto end = counting_clock::now();
  61. REQUIRE((iterations * rate) > Catch::Benchmark::Detail::warmup_time.count());
  62. REQUIRE((end - start) > Catch::Benchmark::Detail::warmup_time);
  63. }
  64. TEST_CASE("resolution", "[benchmark]") {
  65. auto rate = 1000;
  66. counting_clock::set_rate(rate);
  67. size_t count = 10;
  68. auto res = Catch::Benchmark::Detail::resolution<counting_clock>(static_cast<int>(count));
  69. REQUIRE(res.size() == count);
  70. for (size_t i = 1; i < count; ++i) {
  71. REQUIRE(res[i] == rate);
  72. }
  73. }
  74. TEST_CASE("estimate_clock_resolution", "[benchmark]") {
  75. auto rate = 1000;
  76. counting_clock::set_rate(rate);
  77. int iters = 160000;
  78. auto res = Catch::Benchmark::Detail::estimate_clock_resolution<counting_clock>(iters);
  79. REQUIRE(res.mean.count() == rate);
  80. REQUIRE(res.outliers.total() == 0);
  81. }
  82. TEST_CASE("benchmark function call", "[benchmark]") {
  83. SECTION("without chronometer") {
  84. auto called = 0;
  85. auto model = TestChronometerModel{};
  86. auto meter = Catch::Benchmark::Chronometer{ model, 1 };
  87. auto fn = Catch::Benchmark::Detail::BenchmarkFunction{ [&] {
  88. CHECK(model.started == 1);
  89. CHECK(model.finished == 0);
  90. ++called;
  91. } };
  92. fn(meter);
  93. CHECK(model.started == 1);
  94. CHECK(model.finished == 1);
  95. CHECK(called == 1);
  96. }
  97. SECTION("with chronometer") {
  98. auto called = 0;
  99. auto model = TestChronometerModel{};
  100. auto meter = Catch::Benchmark::Chronometer{ model, 1 };
  101. auto fn = Catch::Benchmark::Detail::BenchmarkFunction{ [&](Catch::Benchmark::Chronometer) {
  102. CHECK(model.started == 0);
  103. CHECK(model.finished == 0);
  104. ++called;
  105. } };
  106. fn(meter);
  107. CHECK(model.started == 0);
  108. CHECK(model.finished == 0);
  109. CHECK(called == 1);
  110. }
  111. }
  112. TEST_CASE("uniform samples", "[benchmark]") {
  113. std::vector<double> samples(100);
  114. std::fill(samples.begin(), samples.end(), 23);
  115. using it = std::vector<double>::iterator;
  116. auto e = Catch::Benchmark::Detail::bootstrap(0.95, samples.begin(), samples.end(), samples, [](it a, it b) {
  117. auto sum = std::accumulate(a, b, 0.);
  118. return sum / (b - a);
  119. });
  120. CHECK(e.point == 23);
  121. CHECK(e.upper_bound == 23);
  122. CHECK(e.lower_bound == 23);
  123. CHECK(e.confidence_interval == 0.95);
  124. }
  125. TEST_CASE("normal_cdf", "[benchmark]") {
  126. using Catch::Benchmark::Detail::normal_cdf;
  127. CHECK(normal_cdf(0.000000) == Approx(0.50000000000000000));
  128. CHECK(normal_cdf(1.000000) == Approx(0.84134474606854293));
  129. CHECK(normal_cdf(-1.000000) == Approx(0.15865525393145705));
  130. CHECK(normal_cdf(2.809729) == Approx(0.99752083845315409));
  131. CHECK(normal_cdf(-1.352570) == Approx(0.08809652095066035));
  132. }
  133. TEST_CASE("erfc_inv", "[benchmark]") {
  134. using Catch::Benchmark::Detail::erfc_inv;
  135. CHECK(erfc_inv(1.103560) == Approx(-0.09203687623843015));
  136. CHECK(erfc_inv(1.067400) == Approx(-0.05980291115763361));
  137. CHECK(erfc_inv(0.050000) == Approx(1.38590382434967796));
  138. }
  139. TEST_CASE("normal_quantile", "[benchmark]") {
  140. using Catch::Benchmark::Detail::normal_quantile;
  141. CHECK(normal_quantile(0.551780) == Approx(0.13015979861484198));
  142. CHECK(normal_quantile(0.533700) == Approx(0.08457408802851875));
  143. CHECK(normal_quantile(0.025000) == Approx(-1.95996398454005449));
  144. }
  145. TEST_CASE("mean", "[benchmark]") {
  146. std::vector<double> x{ 10., 20., 14., 16., 30., 24. };
  147. auto m = Catch::Benchmark::Detail::mean(x.begin(), x.end());
  148. REQUIRE(m == 19.);
  149. }
  150. TEST_CASE("weighted_average_quantile", "[benchmark]") {
  151. std::vector<double> x{ 10., 20., 14., 16., 30., 24. };
  152. auto q1 = Catch::Benchmark::Detail::weighted_average_quantile(1, 4, x.begin(), x.end());
  153. auto med = Catch::Benchmark::Detail::weighted_average_quantile(1, 2, x.begin(), x.end());
  154. auto q3 = Catch::Benchmark::Detail::weighted_average_quantile(3, 4, x.begin(), x.end());
  155. REQUIRE(q1 == 14.5);
  156. REQUIRE(med == 18.);
  157. REQUIRE(q3 == 23.);
  158. }
  159. TEST_CASE("classify_outliers", "[benchmark]") {
  160. auto require_outliers = [](Catch::Benchmark::OutlierClassification o, int los, int lom, int him, int his) {
  161. REQUIRE(o.low_severe == los);
  162. REQUIRE(o.low_mild == lom);
  163. REQUIRE(o.high_mild == him);
  164. REQUIRE(o.high_severe == his);
  165. REQUIRE(o.total() == los + lom + him + his);
  166. };
  167. SECTION("none") {
  168. std::vector<double> x{ 10., 20., 14., 16., 30., 24. };
  169. auto o = Catch::Benchmark::Detail::classify_outliers(x.begin(), x.end());
  170. REQUIRE(o.samples_seen == static_cast<int>(x.size()));
  171. require_outliers(o, 0, 0, 0, 0);
  172. }
  173. SECTION("low severe") {
  174. std::vector<double> x{ -12., 20., 14., 16., 30., 24. };
  175. auto o = Catch::Benchmark::Detail::classify_outliers(x.begin(), x.end());
  176. REQUIRE(o.samples_seen == static_cast<int>(x.size()));
  177. require_outliers(o, 1, 0, 0, 0);
  178. }
  179. SECTION("low mild") {
  180. std::vector<double> x{ 1., 20., 14., 16., 30., 24. };
  181. auto o = Catch::Benchmark::Detail::classify_outliers(x.begin(), x.end());
  182. REQUIRE(o.samples_seen == static_cast<int>(x.size()));
  183. require_outliers(o, 0, 1, 0, 0);
  184. }
  185. SECTION("high mild") {
  186. std::vector<double> x{ 10., 20., 14., 16., 36., 24. };
  187. auto o = Catch::Benchmark::Detail::classify_outliers(x.begin(), x.end());
  188. REQUIRE(o.samples_seen == static_cast<int>(x.size()));
  189. require_outliers(o, 0, 0, 1, 0);
  190. }
  191. SECTION("high severe") {
  192. std::vector<double> x{ 10., 20., 14., 16., 49., 24. };
  193. auto o = Catch::Benchmark::Detail::classify_outliers(x.begin(), x.end());
  194. REQUIRE(o.samples_seen == static_cast<int>(x.size()));
  195. require_outliers(o, 0, 0, 0, 1);
  196. }
  197. SECTION("mixed") {
  198. std::vector<double> x{ -20., 20., 14., 16., 39., 24. };
  199. auto o = Catch::Benchmark::Detail::classify_outliers(x.begin(), x.end());
  200. REQUIRE(o.samples_seen == static_cast<int>(x.size()));
  201. require_outliers(o, 1, 0, 1, 0);
  202. }
  203. }
  204. TEST_CASE("analyse", "[benchmark]") {
  205. Catch::ConfigData data{};
  206. data.benchmarkConfidenceInterval = 0.95;
  207. data.benchmarkNoAnalysis = false;
  208. data.benchmarkResamples = 1000;
  209. data.benchmarkSamples = 99;
  210. Catch::Config config{data};
  211. using Duration = Catch::Benchmark::FloatDuration<Catch::Benchmark::default_clock>;
  212. Catch::Benchmark::Environment<Duration> env;
  213. std::vector<Duration> samples(99);
  214. for (size_t i = 0; i < samples.size(); ++i) {
  215. samples[i] = Duration(23 + (i % 3 - 1));
  216. }
  217. auto analysis = Catch::Benchmark::Detail::analyse(config, env, samples.begin(), samples.end());
  218. CHECK(analysis.mean.point.count() == 23);
  219. CHECK(analysis.mean.lower_bound.count() < 23);
  220. CHECK(analysis.mean.lower_bound.count() > 22);
  221. CHECK(analysis.mean.upper_bound.count() > 23);
  222. CHECK(analysis.mean.upper_bound.count() < 24);
  223. CHECK(analysis.standard_deviation.point.count() > 0.5);
  224. CHECK(analysis.standard_deviation.point.count() < 1);
  225. CHECK(analysis.standard_deviation.lower_bound.count() > 0.5);
  226. CHECK(analysis.standard_deviation.lower_bound.count() < 1);
  227. CHECK(analysis.standard_deviation.upper_bound.count() > 0.5);
  228. CHECK(analysis.standard_deviation.upper_bound.count() < 1);
  229. CHECK(analysis.outliers.total() == 0);
  230. CHECK(analysis.outliers.low_mild == 0);
  231. CHECK(analysis.outliers.low_severe == 0);
  232. CHECK(analysis.outliers.high_mild == 0);
  233. CHECK(analysis.outliers.high_severe == 0);
  234. CHECK(analysis.outliers.samples_seen == samples.size());
  235. CHECK(analysis.outlier_variance < 0.5);
  236. CHECK(analysis.outlier_variance > 0);
  237. }
  238. TEST_CASE("analyse no analysis", "[benchmark]") {
  239. Catch::ConfigData data{};
  240. data.benchmarkConfidenceInterval = 0.95;
  241. data.benchmarkNoAnalysis = true;
  242. data.benchmarkResamples = 1000;
  243. data.benchmarkSamples = 99;
  244. Catch::Config config{ data };
  245. using Duration = Catch::Benchmark::FloatDuration<Catch::Benchmark::default_clock>;
  246. Catch::Benchmark::Environment<Duration> env;
  247. std::vector<Duration> samples(99);
  248. for (size_t i = 0; i < samples.size(); ++i) {
  249. samples[i] = Duration(23 + (i % 3 - 1));
  250. }
  251. auto analysis = Catch::Benchmark::Detail::analyse(config, env, samples.begin(), samples.end());
  252. CHECK(analysis.mean.point.count() == 23);
  253. CHECK(analysis.mean.lower_bound.count() == 23);
  254. CHECK(analysis.mean.upper_bound.count() == 23);
  255. CHECK(analysis.standard_deviation.point.count() == 0);
  256. CHECK(analysis.standard_deviation.lower_bound.count() == 0);
  257. CHECK(analysis.standard_deviation.upper_bound.count() == 0);
  258. CHECK(analysis.outliers.total() == 0);
  259. CHECK(analysis.outliers.low_mild == 0);
  260. CHECK(analysis.outliers.low_severe == 0);
  261. CHECK(analysis.outliers.high_mild == 0);
  262. CHECK(analysis.outliers.high_severe == 0);
  263. CHECK(analysis.outliers.samples_seen == 0);
  264. CHECK(analysis.outlier_variance == 0);
  265. }
  266. TEST_CASE("run_for_at_least, int", "[benchmark]") {
  267. manual_clock::duration time(100);
  268. int old_x = 1;
  269. auto Timing = Catch::Benchmark::Detail::run_for_at_least<manual_clock>(time, 1, [&old_x](int x) -> int {
  270. CHECK(x >= old_x);
  271. manual_clock::advance(x);
  272. old_x = x;
  273. return x + 17;
  274. });
  275. REQUIRE(Timing.elapsed >= time);
  276. REQUIRE(Timing.result == Timing.iterations + 17);
  277. REQUIRE(Timing.iterations >= time.count());
  278. }
  279. TEST_CASE("run_for_at_least, chronometer", "[benchmark]") {
  280. manual_clock::duration time(100);
  281. int old_runs = 1;
  282. auto Timing = Catch::Benchmark::Detail::run_for_at_least<manual_clock>(time, 1, [&old_runs](Catch::Benchmark::Chronometer meter) -> int {
  283. CHECK(meter.runs() >= old_runs);
  284. manual_clock::advance(100);
  285. meter.measure([] {
  286. manual_clock::advance(1);
  287. });
  288. old_runs = meter.runs();
  289. return meter.runs() + 17;
  290. });
  291. REQUIRE(Timing.elapsed >= time);
  292. REQUIRE(Timing.result == Timing.iterations + 17);
  293. REQUIRE(Timing.iterations >= time.count());
  294. }
  295. TEST_CASE("measure", "[benchmark]") {
  296. auto r = Catch::Benchmark::Detail::measure<manual_clock>([](int x) -> int {
  297. CHECK(x == 17);
  298. manual_clock::advance(42);
  299. return 23;
  300. }, 17);
  301. auto s = Catch::Benchmark::Detail::measure<manual_clock>([](int x) -> int {
  302. CHECK(x == 23);
  303. manual_clock::advance(69);
  304. return 17;
  305. }, 23);
  306. CHECK(r.elapsed.count() == 42);
  307. CHECK(r.result == 23);
  308. CHECK(r.iterations == 1);
  309. CHECK(s.elapsed.count() == 69);
  310. CHECK(s.result == 17);
  311. CHECK(s.iterations == 1);
  312. }
  313. TEST_CASE("run benchmark", "[benchmark]") {
  314. counting_clock::set_rate(1000);
  315. auto start = counting_clock::now();
  316. Catch::Benchmark::Benchmark bench{ "Test Benchmark", [](Catch::Benchmark::Chronometer meter) {
  317. counting_clock::set_rate(100000);
  318. meter.measure([] { return counting_clock::now(); });
  319. } };
  320. bench.run<counting_clock>();
  321. auto end = counting_clock::now();
  322. CHECK((end - start).count() == 2867251000);
  323. }
  324. #endif // CATCH_CONFIG_ENABLE_BENCHMARKING