mirror of
https://github.com/sharkdp/hyperfine.git
synced 2026-11-01 08:50:19 +00:00
Use uom
This commit is contained in:
1 parent
9ec7a9c920
commit
641451aea6
15 files changed
+459
-443
No files matched your search
@@ -2,7 +2,7 @@ use std::collections::BTreeMap;
|
||||
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::benchmark::{measurement::Measurements, quantity::Second};
|
||||
use crate::benchmark::{measurement::Measurements, quantity::Time};
|
||||
|
||||
/// Parameter value and whether it was used in the command line template
|
||||
#[derive(Debug, Default, Clone, Serialize, PartialEq)]
|
||||
@@ -37,7 +37,7 @@ pub struct BenchmarkResult {
|
||||
|
||||
impl BenchmarkResult {
|
||||
/// The average wall clock time
|
||||
pub fn mean_wall_clock_time(&self) -> Second {
|
||||
pub fn mean_wall_clock_time(&self) -> Time {
|
||||
self.measurements.time_wall_clock_mean()
|
||||
}
|
||||
|
||||
|
||||
+18
-14
@@ -4,8 +4,9 @@ use std::process::ExitStatus;
|
||||
|
||||
use crate::benchmark::measurement::Measurement;
|
||||
use crate::benchmark::measurement::Measurements;
|
||||
use crate::benchmark::quantity::Byte;
|
||||
use crate::benchmark::quantity::Second;
|
||||
use crate::benchmark::quantity::Information;
|
||||
use crate::benchmark::quantity::InformationQuantity;
|
||||
use crate::benchmark::quantity::{Time, TimeQuantity};
|
||||
use crate::command::Command;
|
||||
use crate::options::{
|
||||
CmdFailureAction, CommandInputPolicy, CommandOutputPolicy, Options, OutputStyleOption, Shell,
|
||||
@@ -172,8 +173,8 @@ impl Executor for RawExecutor<'_> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn time_overhead(&self) -> Second {
|
||||
Second::zero()
|
||||
fn time_overhead(&self) -> Time {
|
||||
Time::zero()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -223,9 +224,9 @@ impl Executor for ShellExecutor<'_> {
|
||||
)?;
|
||||
|
||||
// Subtract shell spawning time
|
||||
fn ensure_non_negative(time: Second) -> Second {
|
||||
if time < Second::zero() {
|
||||
Second::zero()
|
||||
fn ensure_non_negative(time: Time) -> Time {
|
||||
if time < Time::zero() {
|
||||
Time::zero()
|
||||
} else {
|
||||
time
|
||||
}
|
||||
@@ -310,7 +311,7 @@ impl Executor for ShellExecutor<'_> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn time_overhead(&self) -> Second {
|
||||
fn time_overhead(&self) -> Time {
|
||||
self.shell_spawning_time.as_ref().unwrap().time_wall_clock
|
||||
}
|
||||
}
|
||||
@@ -327,7 +328,7 @@ impl MockExecutor {
|
||||
|
||||
fn extract_time<S: AsRef<str>>(sleep_command: S) -> Time {
|
||||
assert!(sleep_command.as_ref().starts_with("sleep "));
|
||||
Second::new(
|
||||
Time::from_seconds(
|
||||
sleep_command
|
||||
.as_ref()
|
||||
.trim_start_matches("sleep ")
|
||||
@@ -359,9 +360,9 @@ impl Executor for MockExecutor {
|
||||
|
||||
Ok(Measurement {
|
||||
time_wall_clock: Self::extract_time(command.get_command_line()),
|
||||
time_user: Second::zero(),
|
||||
time_system: Second::zero(),
|
||||
peak_memory_usage: Byte::new(0),
|
||||
time_user: Time::zero(),
|
||||
time_system: Time::zero(),
|
||||
peak_memory_usage: Information::from_bytes(0),
|
||||
exit_status,
|
||||
})
|
||||
}
|
||||
@@ -372,7 +373,7 @@ impl Executor for MockExecutor {
|
||||
|
||||
fn time_overhead(&self) -> Time {
|
||||
match &self.shell {
|
||||
None => Second::zero(),
|
||||
None => Time::zero(),
|
||||
Some(shell) => Self::extract_time(shell),
|
||||
}
|
||||
}
|
||||
@@ -380,5 +381,8 @@ impl Executor for MockExecutor {
|
||||
|
||||
#[test]
|
||||
fn test_mock_executor_extract_time() {
|
||||
assert_eq!(MockExecutor::extract_time("sleep 0.1"), Second::new(0.1));
|
||||
assert_eq!(
|
||||
MockExecutor::extract_time("sleep 0.1"),
|
||||
Time::from_seconds(0.1)
|
||||
);
|
||||
}
|
||||
@@ -2,7 +2,10 @@ use std::process::ExitStatus;
|
||||
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::benchmark::quantity::{max, mean, median, min, standard_deviation, Byte, Second};
|
||||
use crate::benchmark::quantity::{
|
||||
max, mean, median, min, second, serialize_information, serialize_time, standard_deviation,
|
||||
Information, Time, TimeQuantity,
|
||||
};
|
||||
use crate::outlier_detection::modified_zscores;
|
||||
use crate::util::exit_code::extract_exit_code;
|
||||
|
||||
@@ -20,16 +23,20 @@ where
|
||||
#[derive(Debug, Clone, Default, Serialize, PartialEq)]
|
||||
pub struct Measurement {
|
||||
/// Elapsed wall clock time (real time)
|
||||
pub time_wall_clock: Second,
|
||||
#[serde(serialize_with = "serialize_time")]
|
||||
pub time_wall_clock: Time,
|
||||
|
||||
/// Time spent in user mode
|
||||
pub time_user: Second,
|
||||
#[serde(serialize_with = "serialize_time")]
|
||||
pub time_user: Time,
|
||||
|
||||
/// Time spent in kernel mode
|
||||
pub time_system: Second,
|
||||
#[serde(serialize_with = "serialize_time")]
|
||||
pub time_system: Time,
|
||||
|
||||
/// Maximum memory usage of the process
|
||||
pub peak_memory_usage: Byte,
|
||||
#[serde(serialize_with = "serialize_information")]
|
||||
pub peak_memory_usage: Information,
|
||||
|
||||
// The exit status of the process
|
||||
#[serde(rename = "exit_code", serialize_with = "serialize_exit_status")]
|
||||
@@ -58,7 +65,7 @@ impl Measurements {
|
||||
self.measurements.push(measurement);
|
||||
}
|
||||
|
||||
pub fn wall_clock_times(&self) -> Vec<Second> {
|
||||
pub fn wall_clock_times(&self) -> Vec<Time> {
|
||||
self.measurements
|
||||
.iter()
|
||||
.map(|m| m.time_wall_clock)
|
||||
@@ -66,12 +73,12 @@ impl Measurements {
|
||||
}
|
||||
|
||||
/// The average wall clock time
|
||||
pub fn time_wall_clock_mean(&self) -> Second {
|
||||
pub fn time_wall_clock_mean(&self) -> Time {
|
||||
mean(&self.wall_clock_times())
|
||||
}
|
||||
|
||||
/// The standard deviation of all wall clock times. Not available if only one run has been performed
|
||||
pub fn stddev(&self) -> Option<Second> {
|
||||
pub fn stddev(&self) -> Option<Time> {
|
||||
let times = self.wall_clock_times();
|
||||
|
||||
if times.len() < 2 {
|
||||
@@ -82,22 +89,22 @@ impl Measurements {
|
||||
}
|
||||
|
||||
/// The median wall clock time
|
||||
pub fn median(&self) -> Second {
|
||||
pub fn median(&self) -> Time {
|
||||
median(&self.wall_clock_times())
|
||||
}
|
||||
|
||||
/// The minimum wall clock time
|
||||
pub fn min(&self) -> Second {
|
||||
pub fn min(&self) -> Time {
|
||||
min(&self.wall_clock_times())
|
||||
}
|
||||
|
||||
/// The maximum wall clock time
|
||||
pub fn max(&self) -> Second {
|
||||
pub fn max(&self) -> Time {
|
||||
max(&self.wall_clock_times())
|
||||
}
|
||||
|
||||
/// The average user time
|
||||
pub fn time_user_mean(&self) -> Second {
|
||||
pub fn time_user_mean(&self) -> Time {
|
||||
mean(
|
||||
&self
|
||||
.measurements
|
||||
@@ -108,7 +115,7 @@ impl Measurements {
|
||||
}
|
||||
|
||||
/// The average system time
|
||||
pub fn time_system_mean(&self) -> Second {
|
||||
pub fn time_system_mean(&self) -> Time {
|
||||
mean(
|
||||
&self
|
||||
.measurements
|
||||
@@ -118,7 +125,7 @@ impl Measurements {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn peak_memory_usage_mean(&self) -> Byte {
|
||||
pub fn peak_memory_usage_mean(&self) -> Information {
|
||||
self.measurements
|
||||
.iter()
|
||||
.map(|m| m.peak_memory_usage)
|
||||
@@ -131,7 +138,7 @@ impl Measurements {
|
||||
&self
|
||||
.wall_clock_times()
|
||||
.iter()
|
||||
.map(|t| t.value_in::<Second>()) // TODO
|
||||
.map(|t| t.value_in::<second>()) // TODO
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ use std::time::Instant;
|
||||
use crate::benchmark::benchmark_result::Parameter;
|
||||
use crate::benchmark::executor::BenchmarkIteration;
|
||||
use crate::benchmark::measurement::{Measurement, Measurements};
|
||||
use crate::benchmark::quantity::{MilliSecond, Second};
|
||||
use crate::benchmark::quantity::{const_time_from_seconds, Time, TimeQuantity};
|
||||
use crate::command::Command;
|
||||
use crate::options::{
|
||||
CmdFailureAction, CommandOutputPolicy, ExecutorKind, Options, OutputStyleOption,
|
||||
@@ -33,7 +33,7 @@ use colored::*;
|
||||
use self::executor::Executor;
|
||||
|
||||
/// Threshold for warning about fast execution time
|
||||
pub const MIN_EXECUTION_TIME: MilliSecond = MilliSecond::new(5.);
|
||||
pub const MIN_EXECUTION_TIME: Time = const_time_from_seconds(0.005);
|
||||
|
||||
pub struct Benchmark<'a> {
|
||||
number: usize,
|
||||
@@ -262,7 +262,7 @@ impl<'a> Benchmark<'a> {
|
||||
};
|
||||
|
||||
let preparation_result = run_preparation_command()?;
|
||||
let preparation_overhead = preparation_result.map_or(Second::zero(), |res| {
|
||||
let preparation_overhead = preparation_result.map_or(Time::zero(), |res| {
|
||||
res.time_wall_clock + self.executor.time_overhead()
|
||||
});
|
||||
|
||||
@@ -291,7 +291,7 @@ impl<'a> Benchmark<'a> {
|
||||
}
|
||||
|
||||
let conclusion_result = run_conclusion_command()?;
|
||||
let conclusion_overhead = conclusion_result.map_or(Second::zero(), |res| {
|
||||
let conclusion_overhead = conclusion_result.map_or(Time::zero(), |res| {
|
||||
res.time_wall_clock + self.executor.time_overhead()
|
||||
});
|
||||
|
||||
@@ -301,7 +301,7 @@ impl<'a> Benchmark<'a> {
|
||||
+ self.executor.time_overhead()
|
||||
+ preparation_overhead
|
||||
+ conclusion_overhead))
|
||||
.get::<ratio>() as u64;
|
||||
.get::<quantity::ratio>() as u64;
|
||||
|
||||
let count = {
|
||||
let min = cmp::max(runs_in_min_time, self.options.run_bounds.min);
|
||||
@@ -432,7 +432,7 @@ impl<'a> Benchmark<'a> {
|
||||
&& measurements
|
||||
.wall_clock_times()
|
||||
.iter()
|
||||
.any(|&t| t < MIN_EXECUTION_TIME.convert_to::<Second>())
|
||||
.any(|&t| t < MIN_EXECUTION_TIME)
|
||||
{
|
||||
warnings.push(Warnings::FastExecutionTime);
|
||||
}
|
||||
|
||||
+148
-216
@@ -1,229 +1,146 @@
|
||||
use std::{
|
||||
fmt::{self, Display, Formatter},
|
||||
ops::{Add, Div, Sub},
|
||||
};
|
||||
use core::f64;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use serde::{ser::SerializeStruct, Serialize, Serializer};
|
||||
use serde::ser::SerializeStruct;
|
||||
use serde::Serializer;
|
||||
use uom::{si, Conversion};
|
||||
|
||||
pub trait IsQuantity {
|
||||
type Value;
|
||||
pub use uom::si::information::{byte, kibibyte};
|
||||
pub use uom::si::ratio::ratio;
|
||||
pub use uom::si::time::{microsecond, millisecond, nanosecond, second};
|
||||
|
||||
const IS_TIME: bool;
|
||||
const METRIC_PREFIX_TIME: i32;
|
||||
const IS_INFORMATION: bool;
|
||||
const BINARY_PREFIX_INFORMATION: i32;
|
||||
pub use si::f64::Ratio;
|
||||
pub use si::f64::Time;
|
||||
pub use si::u64::Information;
|
||||
|
||||
fn unsafe_from(value: Self::Value) -> Self;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd)] // TODO: check partialord
|
||||
pub struct Quantity<
|
||||
Value,
|
||||
const IS_TIME: bool,
|
||||
const METRIC_PREFIX_TIME: i32,
|
||||
const IS_INFORMATION: bool,
|
||||
const BINARY_PREFIX_INFORMATION: i32,
|
||||
>(Value);
|
||||
|
||||
impl<
|
||||
Value,
|
||||
const IS_TIME: bool,
|
||||
const METRIC_PREFIX_TIME: i32,
|
||||
const IS_INFORMATION: bool,
|
||||
const BINARY_PREFIX_INFORMATION: i32,
|
||||
> IsQuantity
|
||||
for Quantity<Value, IS_TIME, METRIC_PREFIX_TIME, IS_INFORMATION, BINARY_PREFIX_INFORMATION>
|
||||
{
|
||||
type Value = Value;
|
||||
const IS_TIME: bool = IS_TIME;
|
||||
const METRIC_PREFIX_TIME: i32 = METRIC_PREFIX_TIME;
|
||||
const IS_INFORMATION: bool = IS_INFORMATION;
|
||||
const BINARY_PREFIX_INFORMATION: i32 = BINARY_PREFIX_INFORMATION;
|
||||
|
||||
fn unsafe_from(value: Self::Value) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
|
||||
type Time<const PREFIX: i32> = Quantity<f64, true, PREFIX, false, 0>;
|
||||
type Information<const PREFIX: i32> = Quantity<u64, false, 0, true, PREFIX>;
|
||||
|
||||
pub type Second = Time<0>;
|
||||
pub type MilliSecond = Time<-3>;
|
||||
pub type MicroSecond = Time<-6>;
|
||||
|
||||
pub type Byte = Information<0>;
|
||||
pub type KibiByte = Information<10>;
|
||||
pub type MebiByte = Information<20>;
|
||||
pub type GibiByte = Information<30>;
|
||||
pub type TebiByte = Information<40>;
|
||||
|
||||
impl<
|
||||
Value: Copy + Default,
|
||||
const IS_TIME: bool,
|
||||
const METRIC_PREFIX_TIME: i32,
|
||||
const IS_INFORMATION: bool,
|
||||
const BINARY_PREFIX_INFORMATION: i32,
|
||||
> Quantity<Value, IS_TIME, METRIC_PREFIX_TIME, IS_INFORMATION, BINARY_PREFIX_INFORMATION>
|
||||
{
|
||||
pub const fn new(value: Value) -> Self {
|
||||
Self(value)
|
||||
pub trait TimeQuantity {
|
||||
fn zero() -> Time {
|
||||
Time::new::<second>(0.0)
|
||||
}
|
||||
|
||||
pub fn zero() -> Self {
|
||||
Self::new(Value::default())
|
||||
fn from_seconds(value: f64) -> Time {
|
||||
Time::new::<second>(value)
|
||||
}
|
||||
|
||||
pub fn value_in<TargetUnit: IsQuantity>(&self) -> Value {
|
||||
const {
|
||||
assert!(IS_TIME == TargetUnit::IS_TIME);
|
||||
assert!(METRIC_PREFIX_TIME == TargetUnit::METRIC_PREFIX_TIME);
|
||||
assert!(IS_INFORMATION == TargetUnit::IS_INFORMATION);
|
||||
assert!(BINARY_PREFIX_INFORMATION == TargetUnit::BINARY_PREFIX_INFORMATION);
|
||||
}
|
||||
|
||||
self.0
|
||||
fn from_milliseconds(value: f64) -> Time {
|
||||
Time::new::<millisecond>(value)
|
||||
}
|
||||
|
||||
const fn unit_name(&self) -> &'static str {
|
||||
match (IS_TIME, IS_INFORMATION) {
|
||||
(true, false) => match METRIC_PREFIX_TIME {
|
||||
-6 => "µs",
|
||||
-3 => "ms",
|
||||
0 => "s",
|
||||
_ => unreachable!(),
|
||||
},
|
||||
(false, true) => match BINARY_PREFIX_INFORMATION {
|
||||
0 => "B",
|
||||
10 => "KiB",
|
||||
20 => "MiB",
|
||||
30 => "GiB",
|
||||
40 => "TiB",
|
||||
_ => unreachable!(),
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
fn from_microseconds(value: f64) -> Time {
|
||||
Time::new::<microsecond>(value)
|
||||
}
|
||||
|
||||
const fn unit_name_long(&self) -> &'static str {
|
||||
match (IS_TIME, IS_INFORMATION) {
|
||||
(true, false) => match METRIC_PREFIX_TIME {
|
||||
-6 => "microsecond",
|
||||
-3 => "millisecond",
|
||||
0 => "second",
|
||||
_ => unreachable!(),
|
||||
},
|
||||
(false, true) => match BINARY_PREFIX_INFORMATION {
|
||||
0 => "byte",
|
||||
10 => "kibibyte",
|
||||
20 => "mebibyte",
|
||||
30 => "gibibyte",
|
||||
40 => "tebibyte",
|
||||
_ => unreachable!(),
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
fn from_nanoseconds(value: f64) -> Time {
|
||||
Time::new::<nanosecond>(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const METRIC_PREFIX_TIME: i32> Time<METRIC_PREFIX_TIME> {
|
||||
pub fn convert_to<TargetUnit: IsQuantity<Value = f64>>(&self) -> TargetUnit {
|
||||
let factor = 10_f64.powi(METRIC_PREFIX_TIME - TargetUnit::METRIC_PREFIX_TIME);
|
||||
// fn square(&self) -> Time;
|
||||
|
||||
TargetUnit::unsafe_from(self.0 * factor)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const METRIC_PREFIX_TIME: i32> Add<Time<METRIC_PREFIX_TIME>> for Time<METRIC_PREFIX_TIME> {
|
||||
type Output = Time<METRIC_PREFIX_TIME>;
|
||||
|
||||
fn add(self, rhs: Time<METRIC_PREFIX_TIME>) -> Self::Output {
|
||||
Time::new(self.0 + rhs.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const METRIC_PREFIX_TIME: i32> Sub<Time<METRIC_PREFIX_TIME>> for Time<METRIC_PREFIX_TIME> {
|
||||
type Output = Time<METRIC_PREFIX_TIME>;
|
||||
|
||||
fn sub(self, rhs: Time<METRIC_PREFIX_TIME>) -> Self::Output {
|
||||
Time::new(self.0 - rhs.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const METRIC_PREFIX_TIME: i32> Div<Time<METRIC_PREFIX_TIME>> for Time<METRIC_PREFIX_TIME> {
|
||||
type Output = f64;
|
||||
|
||||
fn div(self, rhs: Time<METRIC_PREFIX_TIME>) -> Self::Output {
|
||||
self.0 / rhs.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<const METRIC_PREFIX_TIME: i32> Display for Time<METRIC_PREFIX_TIME> {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
let prefix = match METRIC_PREFIX_TIME {
|
||||
-6 => "µ",
|
||||
-3 => "m",
|
||||
0 => "s",
|
||||
_ => unreachable!(),
|
||||
};
|
||||
write!(f, "{} {}s", self.0, prefix)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const BINARY_PREFIX_INFORMATION: i32> Display for Information<BINARY_PREFIX_INFORMATION> {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
let prefix = match BINARY_PREFIX_INFORMATION {
|
||||
0 => "",
|
||||
10 => "Ki",
|
||||
20 => "Mi",
|
||||
30 => "Gi",
|
||||
40 => "Ti",
|
||||
_ => unreachable!(),
|
||||
};
|
||||
write!(f, "{} {}B", self.0, prefix)
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
Value: Copy + Default + Serialize,
|
||||
const IS_TIME: bool,
|
||||
const METRIC_PREFIX_TIME: i32,
|
||||
const IS_INFORMATION: bool,
|
||||
const BINARY_PREFIX_INFORMATION: i32,
|
||||
> Serialize
|
||||
for Quantity<Value, IS_TIME, METRIC_PREFIX_TIME, IS_INFORMATION, BINARY_PREFIX_INFORMATION>
|
||||
{
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
fn value_in<U>(&self) -> f64
|
||||
where
|
||||
S: Serializer,
|
||||
U: si::time::Unit + Conversion<f64, T = f64>;
|
||||
|
||||
fn to_string<U>(&self, u: U) -> String
|
||||
where
|
||||
U: si::time::Unit + Conversion<f64, T = f64>;
|
||||
}
|
||||
|
||||
impl TimeQuantity for Time {
|
||||
fn value_in<U>(&self) -> f64
|
||||
where
|
||||
U: si::time::Unit + Conversion<f64, T = f64>,
|
||||
{
|
||||
let mut state = serializer.serialize_struct("Quantity", 2)?;
|
||||
state.serialize_field("value", &self.0)?;
|
||||
state.serialize_field("unit", self.unit_name())?;
|
||||
state.serialize_field("unit_long", self.unit_name_long())?;
|
||||
state.end()
|
||||
self.get::<U>()
|
||||
}
|
||||
|
||||
fn to_string<U>(&self, u: U) -> String
|
||||
where
|
||||
U: si::time::Unit + Conversion<f64, T = f64>,
|
||||
{
|
||||
format!(
|
||||
"{:.3}",
|
||||
self.into_format_args(u, uom::fmt::DisplayStyle::Abbreviation)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn const_time_from_seconds(value: f64) -> Time {
|
||||
// Quantity::new in uom is not yet const: https://docs.rs/uom/0.36.0/uom/si/struct.Quantity.html
|
||||
Time {
|
||||
dimension: PhantomData,
|
||||
units: PhantomData,
|
||||
value,
|
||||
}
|
||||
}
|
||||
|
||||
pub trait InformationQuantity {
|
||||
fn zero() -> Information {
|
||||
Information::new::<byte>(0)
|
||||
}
|
||||
|
||||
fn from_bytes(value: u64) -> Information {
|
||||
Information::new::<byte>(value)
|
||||
}
|
||||
|
||||
fn from_kibibytes(value: u64) -> Information {
|
||||
Information::new::<kibibyte>(value)
|
||||
}
|
||||
|
||||
fn value_in<U>(&self) -> u64
|
||||
where
|
||||
U: si::information::Unit + Conversion<u64, T = uom::num_rational::Ratio<u64>>;
|
||||
|
||||
fn to_string<U>(&self, u: U) -> String
|
||||
where
|
||||
U: si::information::Unit + Conversion<u64, T = uom::num_rational::Ratio<u64>>;
|
||||
}
|
||||
|
||||
impl InformationQuantity for Information {
|
||||
fn value_in<U>(&self) -> u64
|
||||
where
|
||||
U: si::information::Unit + Conversion<u64, T = uom::num_rational::Ratio<u64>>,
|
||||
{
|
||||
self.get::<U>()
|
||||
}
|
||||
|
||||
fn to_string<U>(&self, u: U) -> String
|
||||
where
|
||||
U: si::information::Unit + Conversion<u64, T = uom::num_rational::Ratio<u64>>,
|
||||
{
|
||||
format!(
|
||||
"{}",
|
||||
self.into_format_args(u, uom::fmt::DisplayStyle::Abbreviation)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn serialize_time<S>(t: &Time, s: S) -> Result<S::Ok, S::Error>
|
||||
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<S>(i: &Information, s: S) -> Result<S::Ok, S::Error>
|
||||
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<
|
||||
const IS_TIME: bool,
|
||||
const METRIC_PREFIX_TIME: i32,
|
||||
const IS_INFORMATION: bool,
|
||||
const BINARY_PREFIX_INFORMATION: i32,
|
||||
>(
|
||||
values: &[Quantity<
|
||||
f64,
|
||||
IS_TIME,
|
||||
METRIC_PREFIX_TIME,
|
||||
IS_INFORMATION,
|
||||
BINARY_PREFIX_INFORMATION,
|
||||
>],
|
||||
) -> Quantity<f64, IS_TIME, METRIC_PREFIX_TIME, IS_INFORMATION, BINARY_PREFIX_INFORMATION> {
|
||||
let $unwrapped_values: Vec<f64> = values.iter().map(|v| v.0).collect();
|
||||
pub fn $name(values: &[Time]) -> Time {
|
||||
let $unwrapped_values: Vec<_> = values.iter().map(|q| q.value_in::<second>()).collect();
|
||||
let result_value = $body;
|
||||
Quantity::new(result_value)
|
||||
|
||||
Time::new::<second>(result_value)
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -238,29 +155,44 @@ quantity_fn!(standard_deviation, values, {
|
||||
});
|
||||
|
||||
#[test]
|
||||
fn test_quantity() {
|
||||
let time = MilliSecond::new(123.4);
|
||||
assert_eq!(time.value_in::<MilliSecond>(), 123.4);
|
||||
fn test_time() {
|
||||
let time = Time::from_milliseconds(123.4);
|
||||
assert_eq!(time.value_in::<millisecond>(), 123.4);
|
||||
|
||||
let time_s = time.convert_to::<Second>();
|
||||
approx::assert_relative_eq!(time_s.value_in::<Second>(), 0.1234);
|
||||
let time_s = time.value_in::<second>();
|
||||
approx::assert_relative_eq!(time_s, 0.1234);
|
||||
|
||||
let time_us = time.convert_to::<MicroSecond>();
|
||||
approx::assert_relative_eq!(time_us.value_in::<MicroSecond>(), 123400.0);
|
||||
let time_us = time.value_in::<microsecond>();
|
||||
approx::assert_relative_eq!(time_us, 123400.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_information() {
|
||||
let information = Information::from_kibibytes(8);
|
||||
assert_eq!(information.value_in::<byte>(), 8192);
|
||||
|
||||
let information_kib = information.value_in::<kibibyte>();
|
||||
assert_eq!(information_kib, 8);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_format() {
|
||||
let time = MilliSecond::new(123.4);
|
||||
assert_eq!(time.to_string(), "123.4 ms");
|
||||
let time = Time::from_milliseconds(123.4);
|
||||
assert_eq!(time.to_string(second), "0.123 s");
|
||||
assert_eq!(time.to_string(millisecond), "123.400 ms");
|
||||
assert_eq!(time.to_string(microsecond), "123400.000 µs");
|
||||
|
||||
let peak_memory_usage = KibiByte::new(8);
|
||||
assert_eq!(peak_memory_usage.to_string(), "8 KiB");
|
||||
let peak_memory_usage = Information::from_kibibytes(8);
|
||||
assert_eq!(peak_memory_usage.to_string(byte), "8192 B");
|
||||
assert_eq!(peak_memory_usage.to_string(kibibyte), "8 KiB");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mean() {
|
||||
let values = vec![MilliSecond::new(123.4), MilliSecond::new(234.5)];
|
||||
let values = vec![
|
||||
Time::from_milliseconds(123.4),
|
||||
Time::from_milliseconds(234.5),
|
||||
];
|
||||
let result = mean(&values);
|
||||
assert_eq!(result.to_string(), "178.95 ms");
|
||||
assert_eq!(result.to_string(millisecond), "178.950 ms");
|
||||
}
|
||||
@@ -1,7 +1,10 @@
|
||||
use std::cmp::Ordering;
|
||||
|
||||
use super::benchmark_result::BenchmarkResult;
|
||||
use crate::{benchmark::quantity::Second, options::SortOrder};
|
||||
use crate::{
|
||||
benchmark::quantity::{self, Ratio, Time, TimeQuantity},
|
||||
options::SortOrder,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BenchmarkResultWithRelativeSpeed<'a> {
|
||||
@@ -52,8 +55,8 @@ fn compute_relative_speeds<'a>(
|
||||
let is_reference = std::ptr::eq(result, reference);
|
||||
let relative_ordering = compare_mean_time(result, reference);
|
||||
|
||||
if result.mean_wall_clock_time() == Second::zero()
|
||||
|| reference.mean_wall_clock_time() == Second::zero()
|
||||
if result.mean_wall_clock_time() == Time::zero()
|
||||
|| reference.mean_wall_clock_time() == Time::zero()
|
||||
{
|
||||
return BenchmarkResultWithRelativeSpeed {
|
||||
result,
|
||||
@@ -66,7 +69,7 @@ fn compute_relative_speeds<'a>(
|
||||
|
||||
let ratio = match relative_ordering {
|
||||
Ordering::Less => reference.mean_wall_clock_time() / result.mean_wall_clock_time(),
|
||||
Ordering::Equal => 1.0,
|
||||
Ordering::Equal => Ratio::new::<quantity::ratio>(1.0),
|
||||
Ordering::Greater => {
|
||||
result.mean_wall_clock_time() / reference.mean_wall_clock_time()
|
||||
}
|
||||
@@ -114,8 +117,8 @@ pub fn compute_with_check_from_reference<'a>(
|
||||
reference: &'a BenchmarkResult,
|
||||
sort_order: SortOrder,
|
||||
) -> Option<Vec<BenchmarkResultWithRelativeSpeed<'a>>> {
|
||||
if fastest_of(results).mean_wall_clock_time() == Second::zero()
|
||||
|| reference.mean_wall_clock_time() == Second::zero()
|
||||
if fastest_of(results).mean_wall_clock_time() == Time::zero()
|
||||
|| reference.mean_wall_clock_time() == Time::zero()
|
||||
{
|
||||
return None;
|
||||
}
|
||||
@@ -129,7 +132,7 @@ pub fn compute_with_check(
|
||||
) -> Option<Vec<BenchmarkResultWithRelativeSpeed<'_>>> {
|
||||
let fastest = fastest_of(results);
|
||||
|
||||
if fastest.mean_wall_clock_time() == Second::zero() {
|
||||
if fastest.mean_wall_clock_time() == Time::zero() {
|
||||
return None;
|
||||
}
|
||||
|
||||
@@ -152,17 +155,17 @@ fn create_result(name: &str, mean: f64) -> BenchmarkResult {
|
||||
|
||||
use crate::benchmark::{
|
||||
measurement::{Measurement, Measurements},
|
||||
quantity::Byte,
|
||||
quantity::{Information, InformationQuantity},
|
||||
};
|
||||
|
||||
BenchmarkResult {
|
||||
command: name.into(),
|
||||
measurements: Measurements {
|
||||
measurements: vec![Measurement {
|
||||
time_wall_clock: Second::new(mean),
|
||||
time_user: Second::new(mean),
|
||||
time_system: Second::zero(),
|
||||
peak_memory_usage: Byte::new(1024),
|
||||
time_wall_clock: Time::from_seconds(mean),
|
||||
time_user: Time::from_seconds(mean),
|
||||
time_system: Time::zero(),
|
||||
peak_memory_usage: Information::from_bytes(1024),
|
||||
exit_status: ExitStatus::default(),
|
||||
}],
|
||||
},
|
||||
|
||||
+16
-32
@@ -208,73 +208,57 @@ fn scheduler_basic() -> Result<()> {
|
||||
measurements:
|
||||
- time_wall_clock:
|
||||
value: 0.123
|
||||
unit: s
|
||||
unit_long: second
|
||||
unit: second
|
||||
time_user:
|
||||
value: 0
|
||||
unit: s
|
||||
unit_long: second
|
||||
unit: second
|
||||
time_system:
|
||||
value: 0
|
||||
unit: s
|
||||
unit_long: second
|
||||
unit: second
|
||||
peak_memory_usage:
|
||||
value: 0
|
||||
unit: B
|
||||
unit_long: byte
|
||||
unit: byte
|
||||
exit_code: 0
|
||||
- time_wall_clock:
|
||||
value: 0.123
|
||||
unit: s
|
||||
unit_long: second
|
||||
unit: second
|
||||
time_user:
|
||||
value: 0
|
||||
unit: s
|
||||
unit_long: second
|
||||
unit: second
|
||||
time_system:
|
||||
value: 0
|
||||
unit: s
|
||||
unit_long: second
|
||||
unit: second
|
||||
peak_memory_usage:
|
||||
value: 0
|
||||
unit: B
|
||||
unit_long: byte
|
||||
unit: byte
|
||||
exit_code: 0
|
||||
- command: sleep 0.456
|
||||
measurements:
|
||||
- time_wall_clock:
|
||||
value: 0.456
|
||||
unit: s
|
||||
unit_long: second
|
||||
unit: second
|
||||
time_user:
|
||||
value: 0
|
||||
unit: s
|
||||
unit_long: second
|
||||
unit: second
|
||||
time_system:
|
||||
value: 0
|
||||
unit: s
|
||||
unit_long: second
|
||||
unit: second
|
||||
peak_memory_usage:
|
||||
value: 0
|
||||
unit: B
|
||||
unit_long: byte
|
||||
unit: byte
|
||||
exit_code: 0
|
||||
- time_wall_clock:
|
||||
value: 0.456
|
||||
unit: s
|
||||
unit_long: second
|
||||
unit: second
|
||||
time_user:
|
||||
value: 0
|
||||
unit: s
|
||||
unit_long: second
|
||||
unit: second
|
||||
time_system:
|
||||
value: 0
|
||||
unit: s
|
||||
unit_long: second
|
||||
unit: second
|
||||
peak_memory_usage:
|
||||
value: 0
|
||||
unit: B
|
||||
unit_long: byte
|
||||
unit: byte
|
||||
exit_code: 0
|
||||
"#);
|
||||
|
||||
|
||||
Reference in new issue
Block a user