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 escape_backslashes (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
@@ -132,8 +125,8 @@ void write_codspeed_benchmarks_to_json(
132125 const auto &metadata = benchmark.metadata ;
133126
134127 oss << " {\n " ;
135- oss << " \" name\" : \" " << escapeBackslashes (metadata.name ) << " \" ,\n " ;
136- oss << " \" uri\" : \" " << escapeBackslashes (metadata.uri ) << " \" ,\n " ;
128+ oss << " \" name\" : \" " << escape_backslashes (metadata.name ) << " \" ,\n " ;
129+ oss << " \" uri\" : \" " << escape_backslashes (metadata.uri ) << " \" ,\n " ;
137130 // TODO: Manage config fields from actual configuration
138131 oss << " \" config\" : {\n " ;
139132 oss << " \" warmup_time_ns\" : null,\n " ;
@@ -202,40 +195,83 @@ 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+ 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+ }
207211
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
208249 double total_time =
209- std::accumulate (raw_benchmark.round_times_ns .begin (),
210- raw_benchmark.round_times_ns .end (), 0.0 ) /
250+ std::accumulate (raw_benchmark.times_per_round_ns .begin (),
251+ raw_benchmark.times_per_round_ns .end (), 0.0 ) /
211252 1e9 ;
212253
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);
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 ();
220260
221261 // 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- };
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+ );
272+ CodspeedWalltimeBenchmark codspeed_benchmark;
273+ codspeed_benchmark.metadata = {raw_benchmark.name , raw_benchmark.uri };
274+ codspeed_benchmark.stats = stats;
239275
240276 codspeed_walltime_benchmarks.push_back (codspeed_benchmark);
241277 }
0 commit comments