File indexing completed on 2025-08-05 08:10:24
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <boost/test/data/test_case.hpp>
0010 #include <boost/test/unit_test.hpp>
0011
0012 #include "Acts/Tests/CommonHelpers/BenchmarkTools.hpp"
0013
0014 #include "Acts/Tests/CommonHelpers/FloatComparisons.hpp"
0015
0016 #include <cmath>
0017 #include <complex>
0018 #include <iostream>
0019 #include <sstream>
0020 #include <tuple>
0021
0022 namespace Acts::Test {
0023
0024
0025
0026 BOOST_AUTO_TEST_SUITE(benchmark_tools)
0027
0028 BOOST_AUTO_TEST_CASE(assume_accessed) {
0029 int x = 42;
0030 assumeAccessed(x);
0031 BOOST_CHECK_EQUAL(x, 42);
0032 }
0033
0034 BOOST_AUTO_TEST_CASE(assume_read) {
0035 float x = 4.2f;
0036 assumeRead(x);
0037 BOOST_CHECK_EQUAL(x, 4.2f);
0038
0039 const std::string y = "LOL";
0040 assumeRead(x);
0041 BOOST_CHECK_EQUAL(y, "LOL");
0042
0043 assumeRead(std::make_tuple(1, false, 3.5));
0044 }
0045
0046 BOOST_AUTO_TEST_CASE(assume_written) {
0047 std::complex c(1.2, 3.4);
0048 assumeWritten(c);
0049 BOOST_CHECK_EQUAL(c, std::complex(1.2, 3.4));
0050 }
0051
0052 BOOST_AUTO_TEST_CASE(micro_benchmark_result) {
0053 MicroBenchmarkResult res;
0054 res.iters_per_run = 42;
0055 res.run_timings = {
0056 std::chrono::microseconds(420), std::chrono::microseconds(21),
0057 std::chrono::milliseconds(4), std::chrono::microseconds(84),
0058 std::chrono::microseconds(294), std::chrono::microseconds(378),
0059 std::chrono::microseconds(126), std::chrono::milliseconds(42)};
0060
0061 CHECK_CLOSE_REL(res.totalTime().count() / 1'000'000., 47.323, 1e-6);
0062
0063 const auto sorted = res.sortedRunTimes();
0064 BOOST_CHECK_EQUAL(sorted.size(), res.run_timings.size());
0065 BOOST_CHECK_EQUAL(sorted[0].count(), 21'000.);
0066 BOOST_CHECK_EQUAL(sorted[1].count(), 84'000.);
0067 BOOST_CHECK_EQUAL(sorted[2].count(), 126'000.);
0068 BOOST_CHECK_EQUAL(sorted[3].count(), 294'000.);
0069 BOOST_CHECK_EQUAL(sorted[4].count(), 378'000.);
0070 BOOST_CHECK_EQUAL(sorted[5].count(), 420'000.);
0071 BOOST_CHECK_EQUAL(sorted[6].count(), 4'000'000.);
0072 BOOST_CHECK_EQUAL(sorted[7].count(), 42'000'000.);
0073
0074 CHECK_CLOSE_REL(res.runTimeMedian().count() / 1000., (294. + 378.) / 2.,
0075 1e-6);
0076
0077 const auto [firstq, thirdq] = res.runTimeQuartiles();
0078 CHECK_CLOSE_REL(firstq.count() / 1000., (84. + 126.) / 2., 1e-6);
0079 CHECK_CLOSE_REL(thirdq.count() / 1000., (420. + 4000.) / 2., 1e-6);
0080
0081 const auto robustRTStddev = res.runTimeRobustStddev();
0082 CHECK_CLOSE_REL(robustRTStddev.count(), (thirdq - firstq).count() / 1.349,
0083 1e-3);
0084
0085 const auto runTimeError = res.runTimeError();
0086 CHECK_CLOSE_REL(
0087 runTimeError.count(),
0088 1.2533 * robustRTStddev.count() / std::sqrt(res.run_timings.size()),
0089 1e-3);
0090
0091 CHECK_CLOSE_REL(res.iterTimeAverage().count(),
0092 res.runTimeMedian().count() / res.iters_per_run, 1e-6);
0093
0094 CHECK_CLOSE_REL(res.iterTimeError().count(),
0095 runTimeError.count() / std::sqrt(res.iters_per_run), 1e-6);
0096
0097 std::ostringstream os;
0098 os << res;
0099 BOOST_CHECK_EQUAL(os.str(),
0100 "8 runs of 42 iteration(s), 47.3ms total, "
0101 "336.0000+/-1355.2296µs per run, "
0102 "8000.000+/-209116.462ns per iteration");
0103 }
0104
0105 BOOST_AUTO_TEST_CASE(micro_benchmark) {
0106 int counter = 0;
0107 microBenchmark([&] { ++counter; }, 15, 7, std::chrono::milliseconds(0));
0108 BOOST_CHECK_EQUAL(counter, 15 * 7);
0109
0110 counter = 0;
0111 microBenchmark(
0112 [&] {
0113 ++counter;
0114 return counter;
0115 },
0116 17, 11, std::chrono::milliseconds(0));
0117 BOOST_CHECK_EQUAL(counter, 17 * 11);
0118
0119 counter = 0;
0120 int previous = 64;
0121 std::vector<int> ints{1, 2, 4, 8, 16, 32, 64};
0122 microBenchmark(
0123 [&](int input) {
0124 if (input == 1) {
0125 BOOST_CHECK_EQUAL(previous, 64);
0126 counter = 1;
0127 } else {
0128 BOOST_CHECK_EQUAL(input, previous * 2);
0129 counter += input;
0130 }
0131 previous = input;
0132 },
0133 ints, 123, std::chrono::milliseconds(3));
0134 BOOST_CHECK_EQUAL(counter, 127);
0135
0136 counter = 0;
0137 previous = -81;
0138 std::vector<char> chars{-1, 3, -9, 27, -81};
0139 microBenchmark(
0140 [&](int input) {
0141 if (input == -1) {
0142 BOOST_CHECK_EQUAL(previous, -81);
0143 counter = -1;
0144 } else {
0145 BOOST_CHECK_EQUAL(input, -previous * 3);
0146 counter += input;
0147 }
0148 previous = input;
0149 return &previous;
0150 },
0151 chars, 456, std::chrono::milliseconds(8));
0152 BOOST_CHECK_EQUAL(counter, -61);
0153 }
0154
0155 BOOST_AUTO_TEST_SUITE_END()
0156
0157
0158
0159
0160
0161
0162
0163
0164
0165 BOOST_AUTO_TEST_SUITE(benchmark_timings, *boost::unit_test::disabled())
0166
0167 constexpr std::size_t bench_iters = 1'000;
0168
0169 BOOST_AUTO_TEST_CASE(micro_benchmark) {
0170 using namespace std::literals::chrono_literals;
0171
0172 // For simple microbenchmarking needs, plain use of microBenchmark is enough.
0173 //
0174 // For example, here, the microbenchmark loop isn't optimized out even though
0175
0176
0177 auto nop = [] {};
0178 const auto nop_x10 = microBenchmark(nop, 10 * bench_iters);
0179 std::cout << "nop (10x iters): " << nop_x10 << std::endl;
0180 const auto nop_x100 = microBenchmark(nop, 100 * bench_iters);
0181 std::cout << "nop (100x iters): " << nop_x100 << std::endl;
0182 const double nop_x10_iter_ns = nop_x10.iterTimeAverage().count();
0183 const double nop_x100_iter_ns = nop_x100.iterTimeAverage().count();
0184 CHECK_CLOSE_REL(nop_x10_iter_ns, nop_x100_iter_ns, 0.1);
0185
0186
0187
0188 #ifdef __OPTIMIZE__
0189
0190
0191 BOOST_CHECK_LT(nop_x100_iter_ns, 1.0);
0192
0193
0194
0195 BOOST_CHECK_LT(nop_x100.iterTimeError().count(), 0.1);
0196
0197
0198
0199
0200
0201
0202 const double x = 1.2, y = 3.4, z = 5.6;
0203 auto sqrt = microBenchmark(
0204 [&] { return std::sqrt(x * y) + std::sqrt(y * z) + std::sqrt(z * x); },
0205 bench_iters);
0206 std::cout << "sqrt (correct): " << sqrt << std::endl;
0207 BOOST_CHECK_GT(sqrt.iterTimeAverage().count(), 10. * nop_x100_iter_ns);
0208
0209
0210
0211 const auto sqrt_constprop = microBenchmark(
0212 [] {
0213 return std::sqrt(1.2 * 3.4) + std::sqrt(3.4 * 5.6) +
0214 std::sqrt(5.6 * 1.2);
0215 },
0216 bench_iters * 20);
0217 std::cout << "sqrt (constprop'd): " << sqrt_constprop << std::endl;
0218 BOOST_CHECK_LT(sqrt_constprop.iterTimeAverage().count(),
0219 sqrt.iterTimeAverage().count() / 5.);
0220
0221
0222
0223
0224
0225
0226
0227 const auto sqrt_deadcode = microBenchmark(
0228 [&] { (void)(std::sqrt(x * y) + std::sqrt(y * z) + std::sqrt(z * x)); },
0229 bench_iters * 10);
0230 std::cout << "sqrt (deadcode'd): " << sqrt_deadcode << std::endl;
0231 BOOST_CHECK_LT(sqrt_deadcode.iterTimeAverage().count(),
0232 sqrt.iterTimeAverage().count() / 3.);
0233 #endif
0234 }
0235
0236
0237
0238 #ifdef __OPTIMIZE__
0239 BOOST_AUTO_TEST_CASE(assume_read) {
0240
0241
0242
0243
0244
0245
0246
0247 const double x = 1.2, y = 3.4, z = 5.6;
0248 const auto tuple_return = microBenchmark(
0249 [&] {
0250 return std::make_tuple(
0251 std::sqrt(x * y), std::complex(std::sqrt(y * z), std::sqrt(z * x)));
0252 },
0253 bench_iters);
0254 std::cout << "tuple return: " << tuple_return << std::endl;
0255 const auto assumeread = microBenchmark(
0256 [&] {
0257 assumeRead(std::sqrt(x * y));
0258 assumeRead(std::complex(std::sqrt(y * z), std::sqrt(z * x)));
0259 },
0260 bench_iters);
0261 std::cout << "assumeRead: " << assumeread << std::endl;
0262 const double tuple_return_iter_ns = tuple_return.iterTimeAverage().count();
0263 const double assumeRead_iter_ns = assumeread.iterTimeAverage().count();
0264 CHECK_CLOSE_REL(tuple_return_iter_ns, assumeRead_iter_ns, 1e-2);
0265 }
0266 #endif
0267
0268 BOOST_AUTO_TEST_CASE(assume_written) {
0269
0270
0271
0272
0273
0274
0275
0276 double x = 1.2, y = 3.4, z = 5.6;
0277 auto sqrt_sum = microBenchmark(
0278 [&] { return std::sqrt(x * y) + std::sqrt(y * z) + std::sqrt(z * x); },
0279 bench_iters);
0280 std::cout << "sqrt sum: " << sqrt_sum << std::endl;
0281 auto sqrt_2sums = microBenchmark(
0282 [&] {
0283 double tmp = std::sqrt(x * y) + std::sqrt(y * z) + std::sqrt(z * x);
0284 assumeWritten(x);
0285 assumeWritten(y);
0286 assumeWritten(z);
0287 return tmp + std::sqrt(x * y) + std::sqrt(y * z) + std::sqrt(z * x);
0288 },
0289 bench_iters);
0290 std::cout << "2x(sqrt sum): " << sqrt_2sums << std::endl;
0291 const double sqrt_sum_iter_ns = sqrt_sum.iterTimeAverage().count();
0292 const double sqrt_2sums_iter_ns = sqrt_2sums.iterTimeAverage().count();
0293 CHECK_CLOSE_REL(2. * sqrt_sum_iter_ns, sqrt_2sums_iter_ns, 1e-2);
0294 }
0295
0296 BOOST_AUTO_TEST_SUITE_END()
0297
0298 }