diff --git a/src/benchmark/executor.rs b/src/benchmark/executor.rs index 5d56f90..acdb3e5 100644 --- a/src/benchmark/executor.rs +++ b/src/benchmark/executor.rs @@ -9,7 +9,7 @@ use crate::options::{ CmdFailureAction, CommandInputPolicy, CommandOutputPolicy, Options, OutputStyleOption, Shell, }; use crate::output::progress_bar::get_progress_bar; -use crate::quantity::{second, Information, Quantity, Time}; +use crate::quantity::{second, Information, Time, Zero}; use crate::timer::execute_and_measure; use crate::util::randomized_environment_offset; diff --git a/src/benchmark/measurement.rs b/src/benchmark/measurement.rs index 4740eb4..2a91811 100644 --- a/src/benchmark/measurement.rs +++ b/src/benchmark/measurement.rs @@ -86,7 +86,7 @@ impl Measurements { /// The median wall clock time pub fn median(&self) -> Time { - median(&self.wall_clock_times().collect::>()) + median(self.wall_clock_times()) } /// The minimum wall clock time diff --git a/src/benchmark/mod.rs b/src/benchmark/mod.rs index 14bbbc3..a2d7709 100644 --- a/src/benchmark/mod.rs +++ b/src/benchmark/mod.rs @@ -22,7 +22,7 @@ use crate::output::progress_bar::{ }; use crate::output::warnings::{OutlierWarningOptions, Warnings}; use crate::parameter::ParameterNameAndValue; -use crate::quantity::{self, const_time_from_seconds, Quantity, Time}; +use crate::quantity::{self, const_time_from_seconds, FormatQuantity, Time, Zero}; use benchmark_result::BenchmarkResult; use anyhow::{anyhow, Result}; diff --git a/src/benchmark/relative_speed.rs b/src/benchmark/relative_speed.rs index 227859d..c60f57e 100644 --- a/src/benchmark/relative_speed.rs +++ b/src/benchmark/relative_speed.rs @@ -3,7 +3,7 @@ use std::cmp::Ordering; use super::benchmark_result::BenchmarkResult; use crate::{ options::SortOrder, - quantity::{self, Ratio, Time, Quantity}, + quantity::{self, Ratio, Time, Zero}, }; #[derive(Debug)] diff --git a/src/export/csv.rs b/src/export/csv.rs index 68ba3c3..b7baf14 100644 --- a/src/export/csv.rs +++ b/src/export/csv.rs @@ -5,7 +5,7 @@ use csv::WriterBuilder; use super::Exporter; use crate::benchmark::benchmark_result::BenchmarkResult; use crate::options::SortOrder; -use crate::quantity::{Quantity, TimeUnit}; +use crate::quantity::{FormatQuantity, TimeUnit}; use anyhow::Result; @@ -71,7 +71,7 @@ impl Exporter for CsvExporter { fn test_csv() { use crate::benchmark::benchmark_result::Parameter; use crate::benchmark::measurement::{Measurement, Measurements}; - use crate::quantity::{byte, second, Information, Quantity, Time}; + use crate::quantity::{byte, second, Information, Time, Zero}; use std::collections::BTreeMap; use std::process::ExitStatus; diff --git a/src/export/tests.rs b/src/export/tests.rs index 69d5589..ba4d4fa 100644 --- a/src/export/tests.rs +++ b/src/export/tests.rs @@ -3,7 +3,7 @@ use crate::benchmark::benchmark_result::BenchmarkResult; use crate::benchmark::measurement::{Measurement, Measurements}; use crate::export::asciidoc::AsciidocExporter; use crate::export::orgmode::OrgmodeExporter; -use crate::quantity::{byte, second, Information, Quantity, Time, TimeUnit}; +use crate::quantity::{byte, second, Information, Time, TimeUnit, Zero}; use crate::{export::markdown::MarkdownExporter, options::SortOrder}; use std::collections::BTreeMap; use std::process::ExitStatus; diff --git a/src/output/warnings.rs b/src/output/warnings.rs index 0d61a22..8400a6c 100644 --- a/src/output/warnings.rs +++ b/src/output/warnings.rs @@ -1,7 +1,7 @@ use std::fmt; use crate::benchmark::MIN_EXECUTION_TIME; -use crate::quantity::{Time, Quantity}; +use crate::quantity::{FormatQuantity, Time}; pub struct OutlierWarningOptions { pub warmup_in_use: bool, diff --git a/src/quantity/mod.rs b/src/quantity/mod.rs index a2f9816..eca4a96 100644 --- a/src/quantity/mod.rs +++ b/src/quantity/mod.rs @@ -1,5 +1,6 @@ use core::f64; use std::marker::PhantomData; +use std::ops::Add; use std::ops::AddAssign; use std::ops::Div; @@ -8,21 +9,20 @@ use serde::Serializer; use uom::num_traits; use uom::si; -pub use uom::si::information::{byte, gibibyte, kibibyte, mebibyte, tebibyte}; -pub use uom::si::ratio::ratio; -pub use uom::si::time::{hour, microsecond, millisecond, minute, nanosecond, second}; pub use si::f64::{Information, Ratio, Time}; +pub use si::information::{byte, gibibyte, kibibyte, mebibyte, tebibyte}; +pub use si::ratio::ratio; +pub use si::time::{hour, microsecond, millisecond, minute, nanosecond, second}; +pub use uom::num_traits::Zero; pub use units::{InformationUnit, IsUnit, TimeUnit}; mod units; -pub trait Quantity { +pub trait FormatQuantity { type Unit; - fn zero() -> Self; - fn suitable_unit(&self) -> Self::Unit; fn format_with_precision(&self, unit: Self::Unit, precision: usize) -> String; @@ -31,13 +31,9 @@ pub trait Quantity { fn format_value(&self, unit: Self::Unit) -> String; } -impl Quantity for Time { +impl FormatQuantity for Time { type Unit = TimeUnit; - fn zero() -> Time { - Time::new::(0.0) - } - fn suitable_unit(&self) -> TimeUnit { if *self < Time::new::(1.0) { TimeUnit::MicroSecond @@ -81,13 +77,9 @@ pub const fn const_time_from_seconds(value: f64) -> Time { } } -impl Quantity for Information { +impl FormatQuantity for Information { type Unit = InformationUnit; - fn zero() -> Information { - Information::new::(0.0) - } - fn suitable_unit(&self) -> InformationUnit { if *self < Information::new::(1.0) { InformationUnit::Byte @@ -120,6 +112,26 @@ impl Quantity for Information { } } +pub fn serialize_time(t: &Time, s: S) -> Result +where + S: Serializer, +{ + let mut state = s.serialize_struct("Time", 3)?; + state.serialize_field("value", &t.get::())?; + state.serialize_field("unit", "second")?; + state.end() +} + +pub fn serialize_information(i: &Information, s: S) -> Result +where + S: Serializer, +{ + let mut state = s.serialize_struct("Information", 3)?; + state.serialize_field("value", &i.get::())?; + state.serialize_field("unit", "byte")?; + state.end() +} + pub trait UnsafeRawValue { fn unsafe_raw_value(&self) -> f64; fn unsafe_from_raw_value(value: f64) -> Self; @@ -145,26 +157,6 @@ impl UnsafeRawValue for Information { } } -pub fn serialize_time(t: &Time, s: S) -> Result -where - S: Serializer, -{ - let mut state = s.serialize_struct("Time", 3)?; - state.serialize_field("value", &t.value)?; - state.serialize_field("unit", "second")?; - state.end() -} - -pub fn serialize_information(i: &Information, s: S) -> Result -where - S: Serializer, -{ - let mut state = s.serialize_struct("Information", 3)?; - state.serialize_field("value", &i.value)?; - state.serialize_field("unit", "byte")?; - state.end() -} - macro_rules! quantity_fn { ($name:ident, $unwrapped_values:ident, $body:expr) => { pub fn $name(values: &[Q]) -> Q { @@ -209,7 +201,25 @@ where (sum / count).into() } -quantity_fn!(median, values, statistical::median(&values)); +pub fn median(values: impl IntoIterator) -> Q +where + Q: Copy + PartialOrd + Add + Div, + P: Into, +{ + let mut values = values.into_iter().collect::>(); + values.sort_by(|a, b| a.partial_cmp(b).expect("No NaN values")); + + let len = values.len(); + if len % 2 == 0 { + let mid = len / 2; + let a = &values[mid - 1]; + let b = &values[mid]; + ((*a + *b) / Ratio::new::(2.)).into() + } else { + values[len / 2] + } +} + quantity_fn!(standard_deviation, values, { let mean_value = statistical::mean(&values); statistical::standard_deviation(&values, Some(mean_value)) @@ -326,7 +336,10 @@ fn statistics() { mean(values.iter().copied()).format(TimeUnit::Second), "2.000 s" ); - assert_eq!(median(&values).format(TimeUnit::Second), "2.000 s"); + assert_eq!( + median(values.iter().copied()).format(TimeUnit::Second), + "2.000 s" + ); assert_eq!(min(&values).format(TimeUnit::Second), "1.000 s"); assert_eq!(max(&values).format(TimeUnit::Second), "3.000 s"); assert_eq!(