11#include < algorithm>
2+ #include < cassert>
23#include < cmath>
34#include < cstdlib>
45#include < filesystem>
@@ -34,6 +35,27 @@ struct BenchmarkStats {
3435 uint64_t stdev_outlier_rounds;
3536 uint64_t iter_per_round;
3637 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) {}
3759};
3860
3961struct BenchmarkMetadata {
@@ -46,51 +68,22 @@ struct CodspeedWalltimeBenchmark {
4668 BenchmarkStats stats;
4769};
4870
49- double compute_quantile (const std::vector<double > &data, double quantile) {
50- size_t n = data.size ();
71+ static double compute_quantile (const std::vector<double > &sorted_data,
72+ double quantile) {
73+ size_t n = sorted_data.size ();
5174 if (n == 0 ) return 0.0 ;
5275
5376 double pos = quantile * (n - 1 );
5477 size_t k = static_cast <size_t >(pos);
5578 double d = pos - k;
5679
5780 if (k + 1 < n) {
58- return data [k] + d * (data [k + 1 ] - data [k]);
81+ return sorted_data [k] + d * (sorted_data [k + 1 ] - sorted_data [k]);
5982 }
60- return data[k];
61- }
62-
63- void compute_iqr_and_outliers (const std::vector<double > ×_ns, double mean,
64- double stdev, double &q1, double &q3, double &iqr,
65- size_t &iqr_outlier_rounds,
66- size_t &stdev_outlier_rounds) {
67- std::vector<double > sorted_times = times_ns;
68- std::sort (sorted_times.begin (), sorted_times.end ());
69-
70- q1 = compute_quantile (sorted_times, 0.25 );
71- q3 = compute_quantile (sorted_times, 0.75 );
72-
73- iqr = q3 - q1;
74-
75- const double IQR_OUTLIER_FACTOR = 1.5 ;
76- const double STDEV_OUTLIER_FACTOR = 3.0 ;
77-
78- iqr_outlier_rounds =
79- std::count_if (sorted_times.begin (), sorted_times.end (),
80- [q1, q3, iqr, IQR_OUTLIER_FACTOR](double x) {
81- return x < q1 - IQR_OUTLIER_FACTOR * iqr ||
82- x > q3 + IQR_OUTLIER_FACTOR * iqr;
83- });
84-
85- stdev_outlier_rounds =
86- std::count_if (sorted_times.begin (), sorted_times.end (),
87- [mean, stdev, STDEV_OUTLIER_FACTOR](double x) {
88- return x < mean - STDEV_OUTLIER_FACTOR * stdev ||
89- x > mean + STDEV_OUTLIER_FACTOR * stdev;
90- });
83+ return sorted_data[k];
9184}
9285
93- std::string escapeBackslashes (const std::string &input) {
86+ static std::string escapeBackslashes (const std::string &input) {
9487 std::string output;
9588 for (char c : input) {
9689 if (c == ' \\ ' ) {
@@ -102,7 +95,7 @@ std::string escapeBackslashes(const std::string &input) {
10295 return output;
10396}
10497
105- void write_codspeed_benchmarks_to_json (
98+ static void write_codspeed_benchmarks_to_json (
10699 const std::vector<CodspeedWalltimeBenchmark> &benchmarks) {
107100 std::ostringstream oss;
108101
@@ -202,40 +195,80 @@ void generate_codspeed_walltime_report(
202195 std::vector<CodspeedWalltimeBenchmark> codspeed_walltime_benchmarks;
203196
204197 for (const auto &raw_benchmark : raw_walltime_benchmarks) {
205- CodspeedWalltimeBenchmark codspeed_benchmark;
206- codspeed_benchmark.metadata = {raw_benchmark.name , raw_benchmark.uri };
198+ assert (raw_benchmark.iters_per_round .size () ==
199+ raw_benchmark.times_per_round_ns .size ());
200+
201+ // Convert total round times to per-iteration times
202+ std::vector<double > per_iteration_times_ns;
203+ for (size_t i = 0 ; i < raw_benchmark.times_per_round_ns .size (); i++) {
204+ double per_iter_time_ns = raw_benchmark.times_per_round_ns [i] /
205+ raw_benchmark.iters_per_round [i];
206+ per_iteration_times_ns.push_back (per_iter_time_ns);
207+ }
207208
209+ // Sort for quantile computation
210+ std::vector<double > sorted_per_iter_times_ns = per_iteration_times_ns;
211+ std::sort (sorted_per_iter_times_ns.begin (), sorted_per_iter_times_ns.end ());
212+
213+ // Compute statistics from per-iteration times
214+ double mean_ns = std::accumulate (per_iteration_times_ns.begin (),
215+ per_iteration_times_ns.end (), 0.0 ) /
216+ per_iteration_times_ns.size ();
217+
218+ double variance = 0.0 ;
219+ for (double time_ns : per_iteration_times_ns) {
220+ double diff = time_ns - mean_ns;
221+ variance += diff * diff;
222+ }
223+ double stdev_ns = std::sqrt (variance / per_iteration_times_ns.size ());
224+ const double STDEV_OUTLIER_FACTOR = 3.0 ;
225+ size_t stdev_outlier_rounds = std::count_if (
226+ sorted_per_iter_times_ns.begin (), sorted_per_iter_times_ns.end (),
227+ [mean_ns, stdev_ns, STDEV_OUTLIER_FACTOR](double x) {
228+ return x < mean_ns - STDEV_OUTLIER_FACTOR * stdev_ns ||
229+ x > mean_ns + STDEV_OUTLIER_FACTOR * stdev_ns;
230+ });
231+
232+ double q1_ns = compute_quantile (sorted_per_iter_times_ns, 0.25 );
233+ double median_ns = compute_quantile (sorted_per_iter_times_ns, 0.5 );
234+ double q3_ns = compute_quantile (sorted_per_iter_times_ns, 0.75 );
235+
236+ double iqr_ns = q3_ns - q1_ns;
237+ const double IQR_OUTLIER_FACTOR = 1.5 ;
238+ size_t iqr_outlier_rounds = std::count_if (
239+ sorted_per_iter_times_ns.begin (), sorted_per_iter_times_ns.end (),
240+ [q1_ns, q3_ns, iqr_ns, IQR_OUTLIER_FACTOR](double x) {
241+ return x < q1_ns - IQR_OUTLIER_FACTOR * iqr_ns ||
242+ x > q3_ns + IQR_OUTLIER_FACTOR * iqr_ns;
243+ });
244+
245+ // Compute total time in seconds
208246 double total_time =
209- std::accumulate (raw_benchmark.round_times_ns .begin (),
210- raw_benchmark.round_times_ns .end (), 0.0 ) /
247+ std::accumulate (raw_benchmark.times_per_round_ns .begin (),
248+ raw_benchmark.times_per_round_ns .end (), 0.0 ) /
211249 1e9 ;
212250
213- double mean = raw_benchmark.mean_ns ;
214- double median = raw_benchmark.median_ns ;
215- double stdev = raw_benchmark.stdev_ns ;
216- double q1, q3, iqr;
217- size_t iqr_outlier_rounds, stdev_outlier_rounds;
218- compute_iqr_and_outliers (raw_benchmark.round_times_ns , mean, stdev, q1, q3,
219- iqr, iqr_outlier_rounds, stdev_outlier_rounds);
251+ // TODO: CodSpeed format only supports one iter_per_round for all rounds,
252+ // for now take the average
253+ uint64_t avg_iters_per_round =
254+ std::accumulate (raw_benchmark.iters_per_round .begin (),
255+ raw_benchmark.iters_per_round .end (), 0ULL ) /
256+ raw_benchmark.iters_per_round .size ();
220257
221258 // Populate stats
222- codspeed_benchmark.stats = {
223- *std::min_element (raw_benchmark.round_times_ns .begin (),
224- raw_benchmark.round_times_ns .end ()),
225- *std::max_element (raw_benchmark.round_times_ns .begin (),
226- raw_benchmark.round_times_ns .end ()),
227- mean,
228- stdev,
229- q1,
230- median,
231- q3,
232- raw_benchmark.round_times_ns .size (),
233- total_time,
234- iqr_outlier_rounds,
235- stdev_outlier_rounds,
236- raw_benchmark.iter_per_round ,
237- 0 // TODO: warmup_iters
238- };
259+ BenchmarkStats stats (*std::min_element (sorted_per_iter_times_ns.begin (),
260+ sorted_per_iter_times_ns.end ()),
261+ *std::max_element (sorted_per_iter_times_ns.begin (),
262+ sorted_per_iter_times_ns.end ()),
263+ mean_ns, stdev_ns, q1_ns, median_ns, q3_ns,
264+ raw_benchmark.times_per_round_ns .size (), total_time,
265+ iqr_outlier_rounds, stdev_outlier_rounds,
266+ avg_iters_per_round,
267+ 0 // TODO: warmup_iters
268+ );
269+ CodspeedWalltimeBenchmark codspeed_benchmark;
270+ codspeed_benchmark.metadata = {raw_benchmark.name , raw_benchmark.uri };
271+ codspeed_benchmark.stats = stats;
239272
240273 codspeed_walltime_benchmarks.push_back (codspeed_benchmark);
241274 }
0 commit comments