From f342e1f5de6871414e3ed2d04d4d3f3b03c3fbf1 Mon Sep 17 00:00:00 2001 From: David Peter Date: Mon, 5 Oct 2026 14:21:00 +0200 Subject: [PATCH] Make memory units available --- src/benchmark/executor.rs | 2 +- src/benchmark/mod.rs | 2 +- src/benchmark/relative_speed.rs | 2 +- src/export/csv.rs | 4 +- src/export/tests.rs | 2 +- src/output/warnings.rs | 2 +- src/quantity/mod.rs | 104 +++++++++++++++++------------- src/quantity/units.rs | 108 +++++++++++++++++++++++--------- 8 files changed, 146 insertions(+), 80 deletions(-) diff --git a/src/benchmark/executor.rs b/src/benchmark/executor.rs index de923b3..5d56f90 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, InformationQuantity, Time, TimeQuantity}; +use crate::quantity::{second, Information, Quantity, Time}; use crate::timer::execute_and_measure; use crate::util::randomized_environment_offset; diff --git a/src/benchmark/mod.rs b/src/benchmark/mod.rs index 3c29f4f..b0cd3aa 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, Time, TimeQuantity}; +use crate::quantity::{self, const_time_from_seconds, Time, Quantity}; use benchmark_result::BenchmarkResult; use anyhow::{anyhow, Result}; diff --git a/src/benchmark/relative_speed.rs b/src/benchmark/relative_speed.rs index d722723..227859d 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, TimeQuantity}, + quantity::{self, Ratio, Time, Quantity}, }; #[derive(Debug)] diff --git a/src/export/csv.rs b/src/export/csv.rs index 614afce..68ba3c3 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::{TimeQuantity, TimeUnit}; +use crate::quantity::{Quantity, 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, Time, TimeQuantity}; + use crate::quantity::{byte, second, Information, Quantity, Time}; use std::collections::BTreeMap; use std::process::ExitStatus; diff --git a/src/export/tests.rs b/src/export/tests.rs index 501ebfb..69d5589 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, Time, TimeQuantity, TimeUnit}; +use crate::quantity::{byte, second, Information, Quantity, Time, TimeUnit}; 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 be5a78a..0d61a22 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, TimeQuantity}; +use crate::quantity::{Time, Quantity}; pub struct OutlierWarningOptions { pub warmup_in_use: bool, diff --git a/src/quantity/mod.rs b/src/quantity/mod.rs index eb9b0f2..cdac2e9 100644 --- a/src/quantity/mod.rs +++ b/src/quantity/mod.rs @@ -3,38 +3,38 @@ use std::marker::PhantomData; use serde::ser::SerializeStruct; use serde::Serializer; -use uom::{si, Conversion}; -pub use uom::si::information::{byte, kibibyte}; +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::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 TimeQuantity { +pub trait Quantity { + type Unit; + + fn zero() -> Self; + + fn suitable_unit(&self) -> Self::Unit; + + fn format_with_precision(&self, unit: Self::Unit, precision: usize) -> String; + fn format(&self, unit: Self::Unit) -> String; + fn format_auto(&self) -> String; + fn format_value(&self, unit: Self::Unit) -> String; +} + +impl Quantity for Time { + type Unit = TimeUnit; + fn zero() -> Time { Time::new::(0.0) } - fn suitable_unit(&self) -> TimeUnit; - - fn format_with_precision(&self, u: TimeUnit, precision: usize) -> String; - fn format(&self, u: TimeUnit) -> String; - fn format_auto(&self) -> String; - fn format_value(&self, time_unit: TimeUnit) -> String; -} - -impl TimeQuantity for Time { fn suitable_unit(&self) -> TimeUnit { if *self < Time::new::(1.0) { TimeUnit::MicroSecond @@ -56,17 +56,17 @@ impl TimeQuantity for Time { format!("{} {}", value, unit.short_name()) } + /// Format the given time duration. The unit will be determined automatically. + fn format_auto(&self) -> String { + let unit = self.suitable_unit(); + let value = self.format(unit); + format!("{} {}", value, unit.short_name()) + } + /// Like `format`, but without displaying the unit. fn format_value(&self, unit: TimeUnit) -> String { self.format_with_precision(unit, unit.preferred_precision()) } - - /// Format the given time duration. The unit will be determined automatically. - fn format_auto(&self) -> String { - let time_unit = self.suitable_unit(); - let value = self.format(time_unit); - format!("{} {}", value, time_unit.short_name()) - } } pub const fn const_time_from_seconds(value: f64) -> Time { @@ -78,25 +78,42 @@ pub const fn const_time_from_seconds(value: f64) -> Time { } } -pub trait InformationQuantity { +impl Quantity for Information { + type Unit = InformationUnit; + fn zero() -> Information { - Information::new::(0.) + Information::new::(0.0) } - fn to_string(&self, u: U) -> String - where - U: si::information::Unit + Conversion; -} + fn suitable_unit(&self) -> InformationUnit { + if *self < Information::new::(1.0) { + InformationUnit::Byte + } else { + InformationUnit::KibiByte + } + } -impl InformationQuantity for Information { - fn to_string(&self, u: U) -> String - where - U: si::information::Unit + Conversion, - { - format!( - "{}", - self.into_format_args(u, uom::fmt::DisplayStyle::Abbreviation) - ) + /// Format the information in the given unit with the given precision. + fn format_with_precision(&self, u: InformationUnit, precision: usize) -> String { + u.format(*self, precision) + } + + /// Format the information in the given unit. + fn format(&self, unit: InformationUnit) -> String { + let value = self.format_with_precision(unit, unit.preferred_precision()); + format!("{} {}", value, unit.short_name()) + } + + /// Format the given information. The unit will be determined automatically. + fn format_auto(&self) -> String { + let unit = self.suitable_unit(); + let value = self.format(unit); + format!("{} {}", value, unit.short_name()) + } + + /// Like `format`, but without displaying the unit. + fn format_value(&self, unit: InformationUnit) -> String { + self.format_with_precision(unit, unit.preferred_precision()) } } @@ -204,8 +221,11 @@ fn test_format() { assert_eq!(time.format(TimeUnit::MicroSecond), "123400.0 µs"); let peak_memory_usage = Information::new::(8.); - assert_eq!(peak_memory_usage.to_string(byte), "8192 B"); - assert_eq!(peak_memory_usage.to_string(kibibyte), "8 KiB"); + assert_eq!(peak_memory_usage.format(InformationUnit::Byte), "8192 B"); + assert_eq!( + peak_memory_usage.format(InformationUnit::KibiByte), + "8.0 KiB" + ); } #[test] diff --git a/src/quantity/units.rs b/src/quantity/units.rs index 2c1511e..057e5b4 100644 --- a/src/quantity/units.rs +++ b/src/quantity/units.rs @@ -23,10 +23,6 @@ pub trait IsUnit { } } -use std::marker::PhantomData; - -use crate::quantity::{hour, microsecond, millisecond, minute, second, Time}; - /// Supported time units #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum TimeUnit { @@ -37,8 +33,20 @@ pub enum TimeUnit { Hour, } -impl TimeUnit { - pub fn preferred_precision(self) -> usize { +impl IsUnit for TimeUnit { + type Quantity = Time; + + fn dispatch(&self) -> Box> { + match self { + TimeUnit::MicroSecond => Box::new(TimeUnitDispatcher::::new()), + TimeUnit::MilliSecond => Box::new(TimeUnitDispatcher::::new()), + TimeUnit::Second => Box::new(TimeUnitDispatcher::::new()), + TimeUnit::Minute => Box::new(TimeUnitDispatcher::::new()), + TimeUnit::Hour => Box::new(TimeUnitDispatcher::::new()), + } + } + + fn preferred_precision(&self) -> usize { match self { TimeUnit::Second => 3, _ => 1, @@ -46,22 +54,58 @@ impl TimeUnit { } } -struct Dispatcher> { - u: PhantomData, +/// Supported information units +#[derive(Debug, Clone, Copy, PartialEq, Eq)] + +pub enum InformationUnit { + Byte, + KibiByte, + MebiByte, + GibiByte, + TebiByte, } -impl> Dispatcher { - fn new() -> Self { - Dispatcher { u: PhantomData } +impl IsUnit for InformationUnit { + type Quantity = Information; + + fn dispatch(&self) -> Box> { + match self { + InformationUnit::Byte => Box::new(InformationUnitDispatcher::::new()), + InformationUnit::KibiByte => Box::new(InformationUnitDispatcher::::new()), + InformationUnit::MebiByte => Box::new(InformationUnitDispatcher::::new()), + InformationUnit::GibiByte => Box::new(InformationUnitDispatcher::::new()), + InformationUnit::TebiByte => Box::new(InformationUnitDispatcher::::new()), + } + } + + fn preferred_precision(&self) -> usize { + match self { + InformationUnit::Byte => 0, + _ => 1, + } } } -trait UnitImpl { +pub trait UnitImpl { + type Quantity; + fn short_name(&self) -> &'static str; - fn format_value(&self, value: Time, precision: usize) -> String; + fn format_value(&self, value: Self::Quantity, precision: usize) -> String; } -impl> UnitImpl for Dispatcher { +struct TimeUnitDispatcher> { + u: PhantomData, +} + +impl> TimeUnitDispatcher { + fn new() -> Self { + TimeUnitDispatcher { u: PhantomData } + } +} + +impl> UnitImpl for TimeUnitDispatcher { + type Quantity = Time; + fn short_name(&self) -> &'static str { U::abbreviation() } @@ -71,25 +115,27 @@ impl> UnitImpl for Dispat } } -impl TimeUnit { - fn dispatch(self) -> Box { - match self { - TimeUnit::MicroSecond => Box::new(Dispatcher::::new()), - TimeUnit::MilliSecond => Box::new(Dispatcher::::new()), - TimeUnit::Second => Box::new(Dispatcher::::new()), - TimeUnit::Minute => Box::new(Dispatcher::::new()), - TimeUnit::Hour => Box::new(Dispatcher::::new()), - } +struct InformationUnitDispatcher> { + u: PhantomData, +} + +impl> InformationUnitDispatcher { + fn new() -> Self { + InformationUnitDispatcher { u: PhantomData } + } +} + +impl> UnitImpl + for InformationUnitDispatcher +{ + type Quantity = Information; + + fn short_name(&self) -> &'static str { + U::abbreviation() } - /// A short abbreviation like `s`, `ms`, or `µs`. - pub fn short_name(self) -> &'static str { - self.dispatch().short_name() - } - - /// Formats the quantity as a string in the given Unit. - pub fn format(self, value: Time, precision: usize) -> String { - self.dispatch().format_value(value, precision) + fn format_value(&self, value: Information, precision: usize) -> String { + format!("{value:.precision$}", value = value.get::()) } }