diff --git a/src/benchmark/relative_speed.rs b/src/benchmark/relative_speed.rs index 02de11b6d..ad9ad5a3a 100644 --- a/src/benchmark/relative_speed.rs +++ b/src/benchmark/relative_speed.rs @@ -15,43 +15,72 @@ pub fn compare_mean_time(l: &BenchmarkResult, r: &BenchmarkResult) -> Ordering { l.mean.partial_cmp(&r.mean).unwrap_or(Ordering::Equal) } -pub fn compute(results: &[BenchmarkResult]) -> Option> { - let fastest: &BenchmarkResult = results +fn fastest_of(results: &[BenchmarkResult]) -> &BenchmarkResult { + results .iter() .min_by(|&l, &r| compare_mean_time(l, r)) - .expect("at least one benchmark result"); + .expect("at least one benchmark result") +} + +fn compute_relative_speeds<'a>( + results: &'a [BenchmarkResult], + fastest: &'a BenchmarkResult, +) -> Vec> { + results + .iter() + .map(|result| { + let is_fastest = result == fastest; + + if result.mean == 0.0 { + return BenchmarkResultWithRelativeSpeed { + result, + relative_speed: if is_fastest { 1.0 } else { f64::INFINITY }, + relative_speed_stddev: None, + is_fastest, + }; + } + + let ratio = result.mean / fastest.mean; + + // https://en.wikipedia.org/wiki/Propagation_of_uncertainty#Example_formulas + // Covariance asssumed to be 0, i.e. variables are assumed to be independent + let ratio_stddev = match (result.stddev, fastest.stddev) { + (Some(result_stddev), Some(fastest_stddev)) => Some( + ratio + * ((result_stddev / result.mean).powi(2) + + (fastest_stddev / fastest.mean).powi(2)) + .sqrt(), + ), + _ => None, + }; + + BenchmarkResultWithRelativeSpeed { + result, + relative_speed: ratio, + relative_speed_stddev: ratio_stddev, + is_fastest, + } + }) + .collect() +} + +pub fn compute_with_check( + results: &[BenchmarkResult], +) -> Option> { + let fastest = fastest_of(results); if fastest.mean == 0.0 { return None; } - Some( - results - .iter() - .map(|result| { - let ratio = result.mean / fastest.mean; - - // https://en.wikipedia.org/wiki/Propagation_of_uncertainty#Example_formulas - // Covariance asssumed to be 0, i.e. variables are assumed to be independent - let ratio_stddev = match (result.stddev, fastest.stddev) { - (Some(result_stddev), Some(fastest_stddev)) => Some( - ratio - * ((result_stddev / result.mean).powi(2) - + (fastest_stddev / fastest.mean).powi(2)) - .sqrt(), - ), - _ => None, - }; + Some(compute_relative_speeds(results, fastest)) +} - BenchmarkResultWithRelativeSpeed { - result, - relative_speed: ratio, - relative_speed_stddev: ratio_stddev, - is_fastest: result == fastest, - } - }) - .collect(), - ) +/// Same as compute_with_check, potentially resulting in relative speeds of infinity +pub fn compute(results: &[BenchmarkResult]) -> Vec { + let fastest = fastest_of(results); + + compute_relative_speeds(results, fastest) } #[cfg(test)] @@ -83,7 +112,7 @@ fn test_compute_relative_speed() { create_result("cmd3", 5.0), ]; - let annotated_results = compute(&results).unwrap(); + let annotated_results = compute_with_check(&results).unwrap(); assert_relative_eq!(1.5, annotated_results[0].relative_speed); assert_relative_eq!(1.0, annotated_results[1].relative_speed); @@ -94,7 +123,7 @@ fn test_compute_relative_speed() { fn test_compute_relative_speed_for_zero_times() { let results = vec![create_result("cmd1", 1.0), create_result("cmd2", 0.0)]; - let annotated_results = compute(&results); + let annotated_results = compute_with_check(&results); assert!(annotated_results.is_none()); } diff --git a/src/benchmark/scheduler.rs b/src/benchmark/scheduler.rs index c6ebf2845..1c86f4a23 100644 --- a/src/benchmark/scheduler.rs +++ b/src/benchmark/scheduler.rs @@ -61,7 +61,7 @@ impl<'a> Scheduler<'a> { return; } - if let Some(mut annotated_results) = relative_speed::compute(&self.results) { + if let Some(mut annotated_results) = relative_speed::compute_with_check(&self.results) { annotated_results.sort_by(|l, r| relative_speed::compare_mean_time(l.result, r.result)); let fastest = &annotated_results[0]; diff --git a/src/export/markup.rs b/src/export/markup.rs index 2ae6ad5f8..6984225d6 100644 --- a/src/export/markup.rs +++ b/src/export/markup.rs @@ -4,7 +4,7 @@ use crate::output::format::format_duration_value; use crate::util::units::Unit; use super::Exporter; -use anyhow::{anyhow, Result}; +use anyhow::Result; pub enum Alignment { Left, @@ -106,13 +106,8 @@ impl Exporter for T { fn serialize(&self, results: &[BenchmarkResult], unit: Option) -> Result> { let unit = unit.unwrap_or_else(|| determine_unit_from_results(results)); let entries = relative_speed::compute(results); - if entries.is_none() { - return Err(anyhow!( - "Relative speed comparison is not available for markup exporter." - )); - } - let table = self.table_results(&entries.unwrap(), unit); + let table = self.table_results(&entries, unit); Ok(table.as_bytes().to_vec()) } }