|
11 | 11 |
|
12 | 12 | #include "codspeed.h" |
13 | 13 | #include "utils.h" |
| 14 | +#include "walltime_internal.h" |
14 | 15 | #ifdef _WIN32 |
15 | 16 | #include <process.h> |
16 | 17 | #else |
|
20 | 21 |
|
21 | 22 | namespace codspeed { |
22 | 23 |
|
23 | | -// Times are per iteration |
24 | | -struct BenchmarkStats { |
25 | | - double min_ns; |
26 | | - double max_ns; |
27 | | - double mean_ns; |
28 | | - double stdev_ns; |
29 | | - double q1_ns; |
30 | | - double median_ns; |
31 | | - double q3_ns; |
32 | | - uint64_t rounds; |
33 | | - double total_time; |
34 | | - uint64_t iqr_outlier_rounds; |
35 | | - uint64_t stdev_outlier_rounds; |
36 | | - uint64_t iter_per_round; |
37 | | - uint64_t warmup_iters; |
38 | | - |
39 | | - BenchmarkStats(double min_ns = 0.0, double max_ns = 0.0, double mean_ns = 0.0, |
40 | | - double stdev_ns = 0.0, double q1_ns = 0.0, |
41 | | - double median_ns = 0.0, double q3_ns = 0.0, |
42 | | - uint64_t rounds = 0, double total_time = 0.0, |
43 | | - uint64_t iqr_outlier_rounds = 0, |
44 | | - uint64_t stdev_outlier_rounds = 0, uint64_t iter_per_round = 0, |
45 | | - uint64_t warmup_iters = 0) |
46 | | - : min_ns(min_ns), |
47 | | - max_ns(max_ns), |
48 | | - mean_ns(mean_ns), |
49 | | - stdev_ns(stdev_ns), |
50 | | - q1_ns(q1_ns), |
51 | | - median_ns(median_ns), |
52 | | - q3_ns(q3_ns), |
53 | | - rounds(rounds), |
54 | | - total_time(total_time), |
55 | | - iqr_outlier_rounds(iqr_outlier_rounds), |
56 | | - stdev_outlier_rounds(stdev_outlier_rounds), |
57 | | - iter_per_round(iter_per_round), |
58 | | - warmup_iters(warmup_iters) {} |
59 | | -}; |
60 | | - |
61 | 24 | struct BenchmarkMetadata { |
62 | 25 | std::string name; |
63 | 26 | std::string uri; |
@@ -190,85 +153,90 @@ static void write_codspeed_benchmarks_to_json( |
190 | 153 | } |
191 | 154 | } |
192 | 155 |
|
| 156 | +BenchmarkStats compute_benchmark_stats( |
| 157 | + const RawWalltimeBenchmark &raw_benchmark) { |
| 158 | + assert(raw_benchmark.iters_per_round.size() == |
| 159 | + raw_benchmark.times_per_round_ns.size()); |
| 160 | + |
| 161 | + assert(raw_benchmark.iters_per_round.size() != 0); |
| 162 | + |
| 163 | + // Convert total round times to per-iteration times |
| 164 | + std::vector<double> per_iteration_times_ns; |
| 165 | + for (size_t i = 0; i < raw_benchmark.times_per_round_ns.size(); i++) { |
| 166 | + assert(raw_benchmark.iters_per_round[i] != 0); |
| 167 | + double per_iter_time_ns = |
| 168 | + raw_benchmark.times_per_round_ns[i] / raw_benchmark.iters_per_round[i]; |
| 169 | + per_iteration_times_ns.push_back(per_iter_time_ns); |
| 170 | + } |
| 171 | + |
| 172 | + // Sort for quantile computation |
| 173 | + std::vector<double> sorted_per_iter_times_ns = per_iteration_times_ns; |
| 174 | + std::sort(sorted_per_iter_times_ns.begin(), sorted_per_iter_times_ns.end()); |
| 175 | + |
| 176 | + // Compute statistics from per-iteration times |
| 177 | + double mean_ns = std::accumulate(per_iteration_times_ns.begin(), |
| 178 | + per_iteration_times_ns.end(), 0.0) / |
| 179 | + per_iteration_times_ns.size(); |
| 180 | + |
| 181 | + double variance = 0.0; |
| 182 | + for (double time_ns : per_iteration_times_ns) { |
| 183 | + double diff = time_ns - mean_ns; |
| 184 | + variance += diff * diff; |
| 185 | + } |
| 186 | + double stdev_ns = std::sqrt(variance / per_iteration_times_ns.size()); |
| 187 | + const double STDEV_OUTLIER_FACTOR = 3.0; |
| 188 | + size_t stdev_outlier_rounds = std::count_if( |
| 189 | + sorted_per_iter_times_ns.begin(), sorted_per_iter_times_ns.end(), |
| 190 | + [mean_ns, stdev_ns, STDEV_OUTLIER_FACTOR](double x) { |
| 191 | + return x < mean_ns - STDEV_OUTLIER_FACTOR * stdev_ns || |
| 192 | + x > mean_ns + STDEV_OUTLIER_FACTOR * stdev_ns; |
| 193 | + }); |
| 194 | + |
| 195 | + double q1_ns = compute_quantile(sorted_per_iter_times_ns, 0.25); |
| 196 | + double median_ns = compute_quantile(sorted_per_iter_times_ns, 0.5); |
| 197 | + double q3_ns = compute_quantile(sorted_per_iter_times_ns, 0.75); |
| 198 | + |
| 199 | + double iqr_ns = q3_ns - q1_ns; |
| 200 | + const double IQR_OUTLIER_FACTOR = 1.5; |
| 201 | + size_t iqr_outlier_rounds = std::count_if( |
| 202 | + sorted_per_iter_times_ns.begin(), sorted_per_iter_times_ns.end(), |
| 203 | + [q1_ns, q3_ns, iqr_ns, IQR_OUTLIER_FACTOR](double x) { |
| 204 | + return x < q1_ns - IQR_OUTLIER_FACTOR * iqr_ns || |
| 205 | + x > q3_ns + IQR_OUTLIER_FACTOR * iqr_ns; |
| 206 | + }); |
| 207 | + |
| 208 | + // Compute total time in seconds |
| 209 | + double total_time = |
| 210 | + std::accumulate(raw_benchmark.times_per_round_ns.begin(), |
| 211 | + raw_benchmark.times_per_round_ns.end(), 0.0) / |
| 212 | + 1e9; |
| 213 | + |
| 214 | + // TODO: CodSpeed format only supports one iter_per_round for all rounds, |
| 215 | + // for now take the average |
| 216 | + uint64_t avg_iters_per_round = |
| 217 | + std::accumulate(raw_benchmark.iters_per_round.begin(), |
| 218 | + raw_benchmark.iters_per_round.end(), 0ULL) / |
| 219 | + raw_benchmark.iters_per_round.size(); |
| 220 | + |
| 221 | + // Populate stats |
| 222 | + return BenchmarkStats(*std::min_element(sorted_per_iter_times_ns.begin(), |
| 223 | + sorted_per_iter_times_ns.end()), |
| 224 | + *std::max_element(sorted_per_iter_times_ns.begin(), |
| 225 | + sorted_per_iter_times_ns.end()), |
| 226 | + mean_ns, stdev_ns, q1_ns, median_ns, q3_ns, |
| 227 | + raw_benchmark.times_per_round_ns.size(), total_time, |
| 228 | + iqr_outlier_rounds, stdev_outlier_rounds, |
| 229 | + avg_iters_per_round, |
| 230 | + 0 // TODO: warmup_iters |
| 231 | + ); |
| 232 | +} |
| 233 | + |
193 | 234 | void generate_codspeed_walltime_report( |
194 | 235 | const std::vector<RawWalltimeBenchmark> &raw_walltime_benchmarks) { |
195 | 236 | std::vector<CodspeedWalltimeBenchmark> codspeed_walltime_benchmarks; |
196 | 237 |
|
197 | 238 | for (const auto &raw_benchmark : raw_walltime_benchmarks) { |
198 | | - assert(raw_benchmark.iters_per_round.size() == |
199 | | - raw_benchmark.times_per_round_ns.size()); |
200 | | - |
201 | | - assert(raw_benchmark.iters_per_round.size() != 0); |
202 | | - |
203 | | - // Convert total round times to per-iteration times |
204 | | - std::vector<double> per_iteration_times_ns; |
205 | | - for (size_t i = 0; i < raw_benchmark.times_per_round_ns.size(); i++) { |
206 | | - assert(raw_benchmark.iters_per_round[i] != 0); |
207 | | - double per_iter_time_ns = raw_benchmark.times_per_round_ns[i] / |
208 | | - raw_benchmark.iters_per_round[i]; |
209 | | - per_iteration_times_ns.push_back(per_iter_time_ns); |
210 | | - } |
211 | | - |
212 | | - // Sort for quantile computation |
213 | | - std::vector<double> sorted_per_iter_times_ns = per_iteration_times_ns; |
214 | | - std::sort(sorted_per_iter_times_ns.begin(), sorted_per_iter_times_ns.end()); |
215 | | - |
216 | | - // Compute statistics from per-iteration times |
217 | | - double mean_ns = std::accumulate(per_iteration_times_ns.begin(), |
218 | | - per_iteration_times_ns.end(), 0.0) / |
219 | | - per_iteration_times_ns.size(); |
220 | | - |
221 | | - double variance = 0.0; |
222 | | - for (double time_ns : per_iteration_times_ns) { |
223 | | - double diff = time_ns - mean_ns; |
224 | | - variance += diff * diff; |
225 | | - } |
226 | | - double stdev_ns = std::sqrt(variance / per_iteration_times_ns.size()); |
227 | | - const double STDEV_OUTLIER_FACTOR = 3.0; |
228 | | - size_t stdev_outlier_rounds = std::count_if( |
229 | | - sorted_per_iter_times_ns.begin(), sorted_per_iter_times_ns.end(), |
230 | | - [mean_ns, stdev_ns, STDEV_OUTLIER_FACTOR](double x) { |
231 | | - return x < mean_ns - STDEV_OUTLIER_FACTOR * stdev_ns || |
232 | | - x > mean_ns + STDEV_OUTLIER_FACTOR * stdev_ns; |
233 | | - }); |
234 | | - |
235 | | - double q1_ns = compute_quantile(sorted_per_iter_times_ns, 0.25); |
236 | | - double median_ns = compute_quantile(sorted_per_iter_times_ns, 0.5); |
237 | | - double q3_ns = compute_quantile(sorted_per_iter_times_ns, 0.75); |
238 | | - |
239 | | - double iqr_ns = q3_ns - q1_ns; |
240 | | - const double IQR_OUTLIER_FACTOR = 1.5; |
241 | | - size_t iqr_outlier_rounds = std::count_if( |
242 | | - sorted_per_iter_times_ns.begin(), sorted_per_iter_times_ns.end(), |
243 | | - [q1_ns, q3_ns, iqr_ns, IQR_OUTLIER_FACTOR](double x) { |
244 | | - return x < q1_ns - IQR_OUTLIER_FACTOR * iqr_ns || |
245 | | - x > q3_ns + IQR_OUTLIER_FACTOR * iqr_ns; |
246 | | - }); |
247 | | - |
248 | | - // Compute total time in seconds |
249 | | - double total_time = |
250 | | - std::accumulate(raw_benchmark.times_per_round_ns.begin(), |
251 | | - raw_benchmark.times_per_round_ns.end(), 0.0) / |
252 | | - 1e9; |
253 | | - |
254 | | - // TODO: CodSpeed format only supports one iter_per_round for all rounds, |
255 | | - // for now take the average |
256 | | - uint64_t avg_iters_per_round = |
257 | | - std::accumulate(raw_benchmark.iters_per_round.begin(), |
258 | | - raw_benchmark.iters_per_round.end(), 0ULL) / |
259 | | - raw_benchmark.iters_per_round.size(); |
260 | | - |
261 | | - // Populate stats |
262 | | - BenchmarkStats stats(*std::min_element(sorted_per_iter_times_ns.begin(), |
263 | | - sorted_per_iter_times_ns.end()), |
264 | | - *std::max_element(sorted_per_iter_times_ns.begin(), |
265 | | - sorted_per_iter_times_ns.end()), |
266 | | - mean_ns, stdev_ns, q1_ns, median_ns, q3_ns, |
267 | | - raw_benchmark.times_per_round_ns.size(), total_time, |
268 | | - iqr_outlier_rounds, stdev_outlier_rounds, |
269 | | - avg_iters_per_round, |
270 | | - 0 // TODO: warmup_iters |
271 | | - ); |
| 239 | + BenchmarkStats stats = compute_benchmark_stats(raw_benchmark); |
272 | 240 | CodspeedWalltimeBenchmark codspeed_benchmark; |
273 | 241 | codspeed_benchmark.metadata = {raw_benchmark.name, raw_benchmark.uri}; |
274 | 242 | codspeed_benchmark.stats = stats; |
|
0 commit comments