thrust / build /_deps /catch2-src /projects /SelfTest /IntrospectiveTests /InternalBenchmark.tests.cpp
| /* | |
| * 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) | |
| */ | |
| namespace { | |
| struct manual_clock { | |
| public: | |
| using duration = std::chrono::nanoseconds; | |
| using time_point = std::chrono::time_point<manual_clock, duration>; | |
| using rep = duration::rep; | |
| using period = duration::period; | |
| enum { is_steady = true }; | |
| static time_point now() { | |
| return time_point(duration(tick())); | |
| } | |
| static void advance(int ticks = 1) { | |
| tick() += ticks; | |
| } | |
| private: | |
| static rep& tick() { | |
| static rep the_tick = 0; | |
| return the_tick; | |
| } | |
| }; | |
| struct counting_clock { | |
| public: | |
| using duration = std::chrono::nanoseconds; | |
| using time_point = std::chrono::time_point<counting_clock, duration>; | |
| using rep = duration::rep; | |
| using period = duration::period; | |
| enum { is_steady = true }; | |
| static time_point now() { | |
| static rep ticks = 0; | |
| return time_point(duration(ticks += rate())); | |
| } | |
| static void set_rate(rep new_rate) { rate() = new_rate; } | |
| private: | |
| static rep& rate() { | |
| static rep the_rate = 1; | |
| return the_rate; | |
| } | |
| }; | |
| struct TestChronometerModel : Catch::Benchmark::Detail::ChronometerConcept { | |
| int started = 0; | |
| int finished = 0; | |
| void start() override { ++started; } | |
| void finish() override { ++finished; } | |
| }; | |
| } // namespace | |
| TEST_CASE("warmup", "[benchmark]") { | |
| auto rate = 1000; | |
| counting_clock::set_rate(rate); | |
| auto start = counting_clock::now(); | |
| auto iterations = Catch::Benchmark::Detail::warmup<counting_clock>(); | |
| auto end = counting_clock::now(); | |
| REQUIRE((iterations * rate) > Catch::Benchmark::Detail::warmup_time.count()); | |
| REQUIRE((end - start) > Catch::Benchmark::Detail::warmup_time); | |
| } | |
| TEST_CASE("resolution", "[benchmark]") { | |
| auto rate = 1000; | |
| counting_clock::set_rate(rate); | |
| size_t count = 10; | |
| auto res = Catch::Benchmark::Detail::resolution<counting_clock>(static_cast<int>(count)); | |
| REQUIRE(res.size() == count); | |
| for (size_t i = 1; i < count; ++i) { | |
| REQUIRE(res[i] == rate); | |
| } | |
| } | |
| TEST_CASE("estimate_clock_resolution", "[benchmark]") { | |
| auto rate = 1000; | |
| counting_clock::set_rate(rate); | |
| int iters = 160000; | |
| auto res = Catch::Benchmark::Detail::estimate_clock_resolution<counting_clock>(iters); | |
| REQUIRE(res.mean.count() == rate); | |
| REQUIRE(res.outliers.total() == 0); | |
| } | |
| TEST_CASE("benchmark function call", "[benchmark]") { | |
| SECTION("without chronometer") { | |
| auto called = 0; | |
| auto model = TestChronometerModel{}; | |
| auto meter = Catch::Benchmark::Chronometer{ model, 1 }; | |
| auto fn = Catch::Benchmark::Detail::BenchmarkFunction{ [&] { | |
| CHECK(model.started == 1); | |
| CHECK(model.finished == 0); | |
| ++called; | |
| } }; | |
| fn(meter); | |
| CHECK(model.started == 1); | |
| CHECK(model.finished == 1); | |
| CHECK(called == 1); | |
| } | |
| SECTION("with chronometer") { | |
| auto called = 0; | |
| auto model = TestChronometerModel{}; | |
| auto meter = Catch::Benchmark::Chronometer{ model, 1 }; | |
| auto fn = Catch::Benchmark::Detail::BenchmarkFunction{ [&](Catch::Benchmark::Chronometer) { | |
| CHECK(model.started == 0); | |
| CHECK(model.finished == 0); | |
| ++called; | |
| } }; | |
| fn(meter); | |
| CHECK(model.started == 0); | |
| CHECK(model.finished == 0); | |
| CHECK(called == 1); | |
| } | |
| } | |
| TEST_CASE("uniform samples", "[benchmark]") { | |
| std::vector<double> samples(100); | |
| std::fill(samples.begin(), samples.end(), 23); | |
| using it = std::vector<double>::iterator; | |
| auto e = Catch::Benchmark::Detail::bootstrap(0.95, samples.begin(), samples.end(), samples, [](it a, it b) { | |
| auto sum = std::accumulate(a, b, 0.); | |
| return sum / (b - a); | |
| }); | |
| CHECK(e.point == 23); | |
| CHECK(e.upper_bound == 23); | |
| CHECK(e.lower_bound == 23); | |
| CHECK(e.confidence_interval == 0.95); | |
| } | |
| TEST_CASE("normal_cdf", "[benchmark]") { | |
| using Catch::Benchmark::Detail::normal_cdf; | |
| CHECK(normal_cdf(0.000000) == Approx(0.50000000000000000)); | |
| CHECK(normal_cdf(1.000000) == Approx(0.84134474606854293)); | |
| CHECK(normal_cdf(-1.000000) == Approx(0.15865525393145705)); | |
| CHECK(normal_cdf(2.809729) == Approx(0.99752083845315409)); | |
| CHECK(normal_cdf(-1.352570) == Approx(0.08809652095066035)); | |
| } | |
| TEST_CASE("erfc_inv", "[benchmark]") { | |
| using Catch::Benchmark::Detail::erfc_inv; | |
| CHECK(erfc_inv(1.103560) == Approx(-0.09203687623843015)); | |
| CHECK(erfc_inv(1.067400) == Approx(-0.05980291115763361)); | |
| CHECK(erfc_inv(0.050000) == Approx(1.38590382434967796)); | |
| } | |
| TEST_CASE("normal_quantile", "[benchmark]") { | |
| using Catch::Benchmark::Detail::normal_quantile; | |
| CHECK(normal_quantile(0.551780) == Approx(0.13015979861484198)); | |
| CHECK(normal_quantile(0.533700) == Approx(0.08457408802851875)); | |
| CHECK(normal_quantile(0.025000) == Approx(-1.95996398454005449)); | |
| } | |
| TEST_CASE("mean", "[benchmark]") { | |
| std::vector<double> x{ 10., 20., 14., 16., 30., 24. }; | |
| auto m = Catch::Benchmark::Detail::mean(x.begin(), x.end()); | |
| REQUIRE(m == 19.); | |
| } | |
| TEST_CASE("weighted_average_quantile", "[benchmark]") { | |
| std::vector<double> x{ 10., 20., 14., 16., 30., 24. }; | |
| auto q1 = Catch::Benchmark::Detail::weighted_average_quantile(1, 4, x.begin(), x.end()); | |
| auto med = Catch::Benchmark::Detail::weighted_average_quantile(1, 2, x.begin(), x.end()); | |
| auto q3 = Catch::Benchmark::Detail::weighted_average_quantile(3, 4, x.begin(), x.end()); | |
| REQUIRE(q1 == 14.5); | |
| REQUIRE(med == 18.); | |
| REQUIRE(q3 == 23.); | |
| } | |
| TEST_CASE("classify_outliers", "[benchmark]") { | |
| auto require_outliers = [](Catch::Benchmark::OutlierClassification o, int los, int lom, int him, int his) { | |
| REQUIRE(o.low_severe == los); | |
| REQUIRE(o.low_mild == lom); | |
| REQUIRE(o.high_mild == him); | |
| REQUIRE(o.high_severe == his); | |
| REQUIRE(o.total() == los + lom + him + his); | |
| }; | |
| SECTION("none") { | |
| std::vector<double> x{ 10., 20., 14., 16., 30., 24. }; | |
| auto o = Catch::Benchmark::Detail::classify_outliers(x.begin(), x.end()); | |
| REQUIRE(o.samples_seen == static_cast<int>(x.size())); | |
| require_outliers(o, 0, 0, 0, 0); | |
| } | |
| SECTION("low severe") { | |
| std::vector<double> x{ -12., 20., 14., 16., 30., 24. }; | |
| auto o = Catch::Benchmark::Detail::classify_outliers(x.begin(), x.end()); | |
| REQUIRE(o.samples_seen == static_cast<int>(x.size())); | |
| require_outliers(o, 1, 0, 0, 0); | |
| } | |
| SECTION("low mild") { | |
| std::vector<double> x{ 1., 20., 14., 16., 30., 24. }; | |
| auto o = Catch::Benchmark::Detail::classify_outliers(x.begin(), x.end()); | |
| REQUIRE(o.samples_seen == static_cast<int>(x.size())); | |
| require_outliers(o, 0, 1, 0, 0); | |
| } | |
| SECTION("high mild") { | |
| std::vector<double> x{ 10., 20., 14., 16., 36., 24. }; | |
| auto o = Catch::Benchmark::Detail::classify_outliers(x.begin(), x.end()); | |
| REQUIRE(o.samples_seen == static_cast<int>(x.size())); | |
| require_outliers(o, 0, 0, 1, 0); | |
| } | |
| SECTION("high severe") { | |
| std::vector<double> x{ 10., 20., 14., 16., 49., 24. }; | |
| auto o = Catch::Benchmark::Detail::classify_outliers(x.begin(), x.end()); | |
| REQUIRE(o.samples_seen == static_cast<int>(x.size())); | |
| require_outliers(o, 0, 0, 0, 1); | |
| } | |
| SECTION("mixed") { | |
| std::vector<double> x{ -20., 20., 14., 16., 39., 24. }; | |
| auto o = Catch::Benchmark::Detail::classify_outliers(x.begin(), x.end()); | |
| REQUIRE(o.samples_seen == static_cast<int>(x.size())); | |
| require_outliers(o, 1, 0, 1, 0); | |
| } | |
| } | |
| TEST_CASE("analyse", "[benchmark]") { | |
| Catch::ConfigData data{}; | |
| data.benchmarkConfidenceInterval = 0.95; | |
| data.benchmarkNoAnalysis = false; | |
| data.benchmarkResamples = 1000; | |
| data.benchmarkSamples = 99; | |
| Catch::Config config{data}; | |
| using Duration = Catch::Benchmark::FloatDuration<Catch::Benchmark::default_clock>; | |
| Catch::Benchmark::Environment<Duration> env; | |
| std::vector<Duration> samples(99); | |
| for (size_t i = 0; i < samples.size(); ++i) { | |
| samples[i] = Duration(23 + (i % 3 - 1)); | |
| } | |
| auto analysis = Catch::Benchmark::Detail::analyse(config, env, samples.begin(), samples.end()); | |
| CHECK(analysis.mean.point.count() == 23); | |
| CHECK(analysis.mean.lower_bound.count() < 23); | |
| CHECK(analysis.mean.lower_bound.count() > 22); | |
| CHECK(analysis.mean.upper_bound.count() > 23); | |
| CHECK(analysis.mean.upper_bound.count() < 24); | |
| CHECK(analysis.standard_deviation.point.count() > 0.5); | |
| CHECK(analysis.standard_deviation.point.count() < 1); | |
| CHECK(analysis.standard_deviation.lower_bound.count() > 0.5); | |
| CHECK(analysis.standard_deviation.lower_bound.count() < 1); | |
| CHECK(analysis.standard_deviation.upper_bound.count() > 0.5); | |
| CHECK(analysis.standard_deviation.upper_bound.count() < 1); | |
| CHECK(analysis.outliers.total() == 0); | |
| CHECK(analysis.outliers.low_mild == 0); | |
| CHECK(analysis.outliers.low_severe == 0); | |
| CHECK(analysis.outliers.high_mild == 0); | |
| CHECK(analysis.outliers.high_severe == 0); | |
| CHECK(analysis.outliers.samples_seen == samples.size()); | |
| CHECK(analysis.outlier_variance < 0.5); | |
| CHECK(analysis.outlier_variance > 0); | |
| } | |
| TEST_CASE("analyse no analysis", "[benchmark]") { | |
| Catch::ConfigData data{}; | |
| data.benchmarkConfidenceInterval = 0.95; | |
| data.benchmarkNoAnalysis = true; | |
| data.benchmarkResamples = 1000; | |
| data.benchmarkSamples = 99; | |
| Catch::Config config{ data }; | |
| using Duration = Catch::Benchmark::FloatDuration<Catch::Benchmark::default_clock>; | |
| Catch::Benchmark::Environment<Duration> env; | |
| std::vector<Duration> samples(99); | |
| for (size_t i = 0; i < samples.size(); ++i) { | |
| samples[i] = Duration(23 + (i % 3 - 1)); | |
| } | |
| auto analysis = Catch::Benchmark::Detail::analyse(config, env, samples.begin(), samples.end()); | |
| CHECK(analysis.mean.point.count() == 23); | |
| CHECK(analysis.mean.lower_bound.count() == 23); | |
| CHECK(analysis.mean.upper_bound.count() == 23); | |
| CHECK(analysis.standard_deviation.point.count() == 0); | |
| CHECK(analysis.standard_deviation.lower_bound.count() == 0); | |
| CHECK(analysis.standard_deviation.upper_bound.count() == 0); | |
| CHECK(analysis.outliers.total() == 0); | |
| CHECK(analysis.outliers.low_mild == 0); | |
| CHECK(analysis.outliers.low_severe == 0); | |
| CHECK(analysis.outliers.high_mild == 0); | |
| CHECK(analysis.outliers.high_severe == 0); | |
| CHECK(analysis.outliers.samples_seen == 0); | |
| CHECK(analysis.outlier_variance == 0); | |
| } | |
| TEST_CASE("run_for_at_least, int", "[benchmark]") { | |
| manual_clock::duration time(100); | |
| int old_x = 1; | |
| auto Timing = Catch::Benchmark::Detail::run_for_at_least<manual_clock>(time, 1, [&old_x](int x) -> int { | |
| CHECK(x >= old_x); | |
| manual_clock::advance(x); | |
| old_x = x; | |
| return x + 17; | |
| }); | |
| REQUIRE(Timing.elapsed >= time); | |
| REQUIRE(Timing.result == Timing.iterations + 17); | |
| REQUIRE(Timing.iterations >= time.count()); | |
| } | |
| TEST_CASE("run_for_at_least, chronometer", "[benchmark]") { | |
| manual_clock::duration time(100); | |
| int old_runs = 1; | |
| auto Timing = Catch::Benchmark::Detail::run_for_at_least<manual_clock>(time, 1, [&old_runs](Catch::Benchmark::Chronometer meter) -> int { | |
| CHECK(meter.runs() >= old_runs); | |
| manual_clock::advance(100); | |
| meter.measure([] { | |
| manual_clock::advance(1); | |
| }); | |
| old_runs = meter.runs(); | |
| return meter.runs() + 17; | |
| }); | |
| REQUIRE(Timing.elapsed >= time); | |
| REQUIRE(Timing.result == Timing.iterations + 17); | |
| REQUIRE(Timing.iterations >= time.count()); | |
| } | |
| TEST_CASE("measure", "[benchmark]") { | |
| auto r = Catch::Benchmark::Detail::measure<manual_clock>([](int x) -> int { | |
| CHECK(x == 17); | |
| manual_clock::advance(42); | |
| return 23; | |
| }, 17); | |
| auto s = Catch::Benchmark::Detail::measure<manual_clock>([](int x) -> int { | |
| CHECK(x == 23); | |
| manual_clock::advance(69); | |
| return 17; | |
| }, 23); | |
| CHECK(r.elapsed.count() == 42); | |
| CHECK(r.result == 23); | |
| CHECK(r.iterations == 1); | |
| CHECK(s.elapsed.count() == 69); | |
| CHECK(s.result == 17); | |
| CHECK(s.iterations == 1); | |
| } | |
| TEST_CASE("run benchmark", "[benchmark]") { | |
| counting_clock::set_rate(1000); | |
| auto start = counting_clock::now(); | |
| Catch::Benchmark::Benchmark bench{ "Test Benchmark", [](Catch::Benchmark::Chronometer meter) { | |
| counting_clock::set_rate(100000); | |
| meter.measure([] { return counting_clock::now(); }); | |
| } }; | |
| bench.run<counting_clock>(); | |
| auto end = counting_clock::now(); | |
| CHECK((end - start).count() == 2867251000); | |
| } | |