mirror of
https://github.com/sharkdp/hyperfine.git
synced 2026-10-28 08:08:58 +00:00
Merge pull request #962 from sharkdp/codex/quantity-module-layout
Group units, formatting, and statistics under quantity
This commit is contained in:
20 files changed
+266
-265
No files matched your search
@@ -2,8 +2,6 @@ use std::collections::BTreeMap;
|
||||
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::util::units::Second;
|
||||
|
||||
/// Set of values that will be exported.
|
||||
// NOTE: `serde` is used for JSON serialization, but not for CSV serialization due to the
|
||||
// `parameters` map. Update `src/hyperfine/export/csv.rs` with new fields, as appropriate.
|
||||
@@ -18,29 +16,29 @@ pub struct BenchmarkResult {
|
||||
pub command_with_unused_parameters: String,
|
||||
|
||||
/// The average run time
|
||||
pub mean: Second,
|
||||
pub mean: f64,
|
||||
|
||||
/// The standard deviation of all run times. Not available if only one run has been performed
|
||||
pub stddev: Option<Second>,
|
||||
pub stddev: Option<f64>,
|
||||
|
||||
/// The median run time
|
||||
pub median: Second,
|
||||
pub median: f64,
|
||||
|
||||
/// Time spent in user mode
|
||||
pub user: Second,
|
||||
pub user: f64,
|
||||
|
||||
/// Time spent in kernel mode
|
||||
pub system: Second,
|
||||
pub system: f64,
|
||||
|
||||
/// Minimum of all measured times
|
||||
pub min: Second,
|
||||
pub min: f64,
|
||||
|
||||
/// Maximum of all measured times
|
||||
pub max: Second,
|
||||
pub max: f64,
|
||||
|
||||
/// All run time measurements
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub times: Option<Vec<Second>>,
|
||||
pub times: Option<Vec<f64>>,
|
||||
|
||||
/// Maximum memory usage of the process, in bytes
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
use std::process::ExitStatus;
|
||||
|
||||
use crate::outlier_detection::modified_zscores;
|
||||
use crate::quantity::statistics::{max, mean, median, min, modified_zscores, standard_deviation};
|
||||
use crate::quantity::{second, Information, Time};
|
||||
use crate::util::statistics::{max, mean, median, min, standard_deviation};
|
||||
|
||||
/// Performance measurements and exit status from running a single command
|
||||
#[derive(Debug, Default, Copy, Clone)]
|
||||
|
||||
@@ -14,11 +14,12 @@ use crate::options::{
|
||||
};
|
||||
use crate::outlier_detection::OUTLIER_THRESHOLD;
|
||||
use crate::output::console_writeln;
|
||||
use crate::output::format::{format_duration, format_duration_unit};
|
||||
use crate::output::progress_bar::get_progress_bar;
|
||||
use crate::output::warnings::{OutlierWarningOptions, Warnings};
|
||||
use crate::parameter::ParameterNameAndValue;
|
||||
use crate::quantity::{byte, const_time_from_seconds, ratio, second, Time, Zero};
|
||||
use crate::quantity::{
|
||||
byte, const_time_from_seconds, format_duration, format_duration_unit, ratio, second, Time, Zero,
|
||||
};
|
||||
use crate::util::exit_code::extract_exit_code;
|
||||
use benchmark_result::BenchmarkResult;
|
||||
use measurement::{Measurement, Measurements};
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
use std::cmp::Ordering;
|
||||
|
||||
use super::benchmark_result::BenchmarkResult;
|
||||
use crate::{options::SortOrder, util::units::Scalar};
|
||||
use crate::options::SortOrder;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BenchmarkResultWithRelativeSpeed<'a> {
|
||||
pub result: &'a BenchmarkResult,
|
||||
pub relative_speed: Scalar,
|
||||
pub relative_speed_stddev: Option<Scalar>,
|
||||
pub relative_speed: f64,
|
||||
pub relative_speed_stddev: Option<f64>,
|
||||
pub is_reference: bool,
|
||||
// Less means faster
|
||||
pub relative_ordering: Ordering,
|
||||
@@ -119,7 +119,7 @@ pub fn compute(
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn create_result(name: &str, mean: Scalar) -> BenchmarkResult {
|
||||
fn create_result(name: &str, mean: f64) -> BenchmarkResult {
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
BenchmarkResult {
|
||||
|
||||
+3
-3
@@ -5,7 +5,7 @@ use csv::WriterBuilder;
|
||||
use super::Exporter;
|
||||
use crate::benchmark::benchmark_result::BenchmarkResult;
|
||||
use crate::options::SortOrder;
|
||||
use crate::util::units::Unit;
|
||||
use crate::quantity::TimeUnit;
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
@@ -16,7 +16,7 @@ impl Exporter for CsvExporter {
|
||||
fn serialize(
|
||||
&self,
|
||||
results: &[BenchmarkResult],
|
||||
_unit: Option<Unit>,
|
||||
_unit: Option<TimeUnit>,
|
||||
_sort_order: SortOrder,
|
||||
) -> Result<Vec<u8>> {
|
||||
let mut writer = WriterBuilder::new().from_writer(vec![]);
|
||||
@@ -110,7 +110,7 @@ fn test_csv() {
|
||||
|
||||
let actual = String::from_utf8(
|
||||
exporter
|
||||
.serialize(&results, Some(Unit::Second), SortOrder::Command)
|
||||
.serialize(&results, Some(TimeUnit::Second), SortOrder::Command)
|
||||
.unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
+2
-2
@@ -4,7 +4,7 @@ use serde_json::to_vec_pretty;
|
||||
use super::Exporter;
|
||||
use crate::benchmark::benchmark_result::BenchmarkResult;
|
||||
use crate::options::SortOrder;
|
||||
use crate::util::units::Unit;
|
||||
use crate::quantity::TimeUnit;
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
@@ -20,7 +20,7 @@ impl Exporter for JsonExporter {
|
||||
fn serialize(
|
||||
&self,
|
||||
results: &[BenchmarkResult],
|
||||
_unit: Option<Unit>,
|
||||
_unit: Option<TimeUnit>,
|
||||
_sort_order: SortOrder,
|
||||
) -> Result<Vec<u8>> {
|
||||
let mut output = to_vec_pretty(&HyperfineSummary { results });
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
use crate::benchmark::relative_speed::BenchmarkResultWithRelativeSpeed;
|
||||
use crate::benchmark::{benchmark_result::BenchmarkResult, relative_speed};
|
||||
use crate::options::SortOrder;
|
||||
use crate::output::format::format_duration_value;
|
||||
use crate::util::units::Unit;
|
||||
use crate::quantity::{format_duration_value, TimeUnit};
|
||||
|
||||
use super::Exporter;
|
||||
use anyhow::Result;
|
||||
@@ -13,7 +12,11 @@ pub enum Alignment {
|
||||
}
|
||||
|
||||
pub trait MarkupExporter {
|
||||
fn table_results(&self, entries: &[BenchmarkResultWithRelativeSpeed], unit: Unit) -> String {
|
||||
fn table_results(
|
||||
&self,
|
||||
entries: &[BenchmarkResultWithRelativeSpeed],
|
||||
unit: TimeUnit,
|
||||
) -> String {
|
||||
// prepare table header strings
|
||||
let notation = format!("[{}]", unit.short_name());
|
||||
|
||||
@@ -95,13 +98,13 @@ pub trait MarkupExporter {
|
||||
fn command(&self, size: &str) -> String;
|
||||
}
|
||||
|
||||
fn determine_unit_from_results(results: &[BenchmarkResult]) -> Unit {
|
||||
fn determine_unit_from_results(results: &[BenchmarkResult]) -> TimeUnit {
|
||||
if let Some(first_result) = results.first() {
|
||||
// Use the first BenchmarkResult entry to determine the unit for all entries.
|
||||
format_duration_value(first_result.mean, None).1
|
||||
} else {
|
||||
// Default to `Second`.
|
||||
Unit::Second
|
||||
TimeUnit::Second
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,7 +112,7 @@ impl<T: MarkupExporter> Exporter for T {
|
||||
fn serialize(
|
||||
&self,
|
||||
results: &[BenchmarkResult],
|
||||
unit: Option<Unit>,
|
||||
unit: Option<TimeUnit>,
|
||||
sort_order: SortOrder,
|
||||
) -> Result<Vec<u8>> {
|
||||
let unit = unit.unwrap_or_else(|| determine_unit_from_results(results));
|
||||
|
||||
+4
-4
@@ -19,7 +19,7 @@ use self::orgmode::OrgmodeExporter;
|
||||
use crate::benchmark::benchmark_result::BenchmarkResult;
|
||||
use crate::options::SortOrder;
|
||||
use crate::output::console_writeln;
|
||||
use crate::util::units::Unit;
|
||||
use crate::quantity::TimeUnit;
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use clap::ArgMatches;
|
||||
@@ -49,7 +49,7 @@ trait Exporter {
|
||||
fn serialize(
|
||||
&self,
|
||||
results: &[BenchmarkResult],
|
||||
unit: Option<Unit>,
|
||||
unit: Option<TimeUnit>,
|
||||
sort_order: SortOrder,
|
||||
) -> Result<Vec<u8>>;
|
||||
}
|
||||
@@ -67,7 +67,7 @@ struct ExporterWithTarget {
|
||||
/// Handles the management of multiple file exporters.
|
||||
pub struct ExportManager {
|
||||
exporters: Vec<ExporterWithTarget>,
|
||||
time_unit: Option<Unit>,
|
||||
time_unit: Option<TimeUnit>,
|
||||
sort_order: SortOrder,
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ impl ExportManager {
|
||||
/// in the given ArgMatches
|
||||
pub fn from_cli_arguments(
|
||||
matches: &ArgMatches,
|
||||
time_unit: Option<Unit>,
|
||||
time_unit: Option<TimeUnit>,
|
||||
sort_order: SortOrder,
|
||||
) -> Result<Self> {
|
||||
let mut export_manager = Self {
|
||||
|
||||
+8
-8
@@ -2,13 +2,13 @@ use super::Exporter;
|
||||
use crate::benchmark::benchmark_result::BenchmarkResult;
|
||||
use crate::export::asciidoc::AsciidocExporter;
|
||||
use crate::export::orgmode::OrgmodeExporter;
|
||||
use crate::util::units::Unit;
|
||||
use crate::quantity::TimeUnit;
|
||||
use crate::{export::markdown::MarkdownExporter, options::SortOrder};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
fn get_output<E: Exporter + Default>(
|
||||
results: &[BenchmarkResult],
|
||||
unit: Option<Unit>,
|
||||
unit: Option<TimeUnit>,
|
||||
sort_order: SortOrder,
|
||||
) -> String {
|
||||
let exporter = E::default();
|
||||
@@ -206,14 +206,14 @@ fn test_markup_export_manual_ms() {
|
||||
},
|
||||
];
|
||||
|
||||
insta::assert_snapshot!(get_output::<MarkdownExporter>(&timing_results, Some(Unit::MilliSecond), SortOrder::Command), @r#"
|
||||
insta::assert_snapshot!(get_output::<MarkdownExporter>(&timing_results, Some(TimeUnit::MilliSecond), SortOrder::Command), @r#"
|
||||
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|
||||
|:---|---:|---:|---:|---:|
|
||||
| `sleep 2` | 2005.0 ± 2.0 | 2002.0 | 2008.0 | 18.97 ± 0.29 |
|
||||
| `sleep 0.1` | 105.7 ± 1.6 | 102.3 | 108.0 | 1.00 |
|
||||
"#);
|
||||
|
||||
insta::assert_snapshot!(get_output::<AsciidocExporter>(&timing_results, Some(Unit::MilliSecond), SortOrder::Command), @r#"
|
||||
insta::assert_snapshot!(get_output::<AsciidocExporter>(&timing_results, Some(TimeUnit::MilliSecond), SortOrder::Command), @r#"
|
||||
[cols="<,>,>,>,>"]
|
||||
|===
|
||||
| Command
|
||||
@@ -236,7 +236,7 @@ fn test_markup_export_manual_ms() {
|
||||
|===
|
||||
"#);
|
||||
|
||||
insta::assert_snapshot!(get_output::<OrgmodeExporter>(&timing_results, Some(Unit::MilliSecond), SortOrder::Command), @r#"
|
||||
insta::assert_snapshot!(get_output::<OrgmodeExporter>(&timing_results, Some(TimeUnit::MilliSecond), SortOrder::Command), @r#"
|
||||
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|
||||
|--+--+--+--+--|
|
||||
| =sleep 2= | 2005.0 ± 2.0 | 2002.0 | 2008.0 | 18.97 ± 0.29 |
|
||||
@@ -280,21 +280,21 @@ fn test_markup_export_manual_s() {
|
||||
},
|
||||
];
|
||||
|
||||
insta::assert_snapshot!(get_output::<MarkdownExporter>(&results, Some(Unit::Second), SortOrder::Command), @r#"
|
||||
insta::assert_snapshot!(get_output::<MarkdownExporter>(&results, Some(TimeUnit::Second), SortOrder::Command), @r#"
|
||||
| Command | Mean [s] | Min [s] | Max [s] | Relative |
|
||||
|:---|---:|---:|---:|---:|
|
||||
| `sleep 2` | 2.005 ± 0.002 | 2.002 | 2.008 | 18.97 ± 0.29 |
|
||||
| `sleep 0.1` | 0.106 ± 0.002 | 0.102 | 0.108 | 1.00 |
|
||||
"#);
|
||||
|
||||
insta::assert_snapshot!(get_output::<MarkdownExporter>(&results, Some(Unit::Second), SortOrder::MeanTime), @r#"
|
||||
insta::assert_snapshot!(get_output::<MarkdownExporter>(&results, Some(TimeUnit::Second), SortOrder::MeanTime), @r#"
|
||||
| Command | Mean [s] | Min [s] | Max [s] | Relative |
|
||||
|:---|---:|---:|---:|---:|
|
||||
| `sleep 0.1` | 0.106 ± 0.002 | 0.102 | 0.108 | 1.00 |
|
||||
| `sleep 2` | 2.005 ± 0.002 | 2.002 | 2.008 | 18.97 ± 0.29 |
|
||||
"#);
|
||||
|
||||
insta::assert_snapshot!(get_output::<AsciidocExporter>(&results, Some(Unit::Second), SortOrder::Command), @r#"
|
||||
insta::assert_snapshot!(get_output::<AsciidocExporter>(&results, Some(TimeUnit::Second), SortOrder::Command), @r#"
|
||||
[cols="<,>,>,>,>"]
|
||||
|===
|
||||
| Command
|
||||
|
||||
+7
-8
@@ -9,8 +9,7 @@ use clap::ArgMatches;
|
||||
|
||||
use crate::command::Commands;
|
||||
use crate::error::OptionsError;
|
||||
use crate::quantity::{second, Time};
|
||||
use crate::util::units::Unit;
|
||||
use crate::quantity::{second, Time, TimeUnit};
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
@@ -246,7 +245,7 @@ pub struct Options {
|
||||
pub command_output_policies: Vec<CommandOutputPolicy>,
|
||||
|
||||
/// Which time unit to use when displaying results
|
||||
pub time_unit: Option<Unit>,
|
||||
pub time_unit: Option<TimeUnit>,
|
||||
}
|
||||
|
||||
impl Default for Options {
|
||||
@@ -430,11 +429,11 @@ impl Options {
|
||||
}
|
||||
|
||||
options.time_unit = match matches.get_one::<String>("time-unit").map(|s| s.as_str()) {
|
||||
Some("µs" | "us" | "microsecond" | "microseconds") => Some(Unit::MicroSecond),
|
||||
Some("ms" | "millisecond" | "milliseconds") => Some(Unit::MilliSecond),
|
||||
Some("s" | "second" | "seconds") => Some(Unit::Second),
|
||||
Some("min" | "minute" | "minutes") => Some(Unit::Minute),
|
||||
Some("h" | "hour" | "hours") => Some(Unit::Hour),
|
||||
Some("µs" | "us" | "microsecond" | "microseconds") => Some(TimeUnit::MicroSecond),
|
||||
Some("ms" | "millisecond" | "milliseconds") => Some(TimeUnit::MilliSecond),
|
||||
Some("s" | "second" | "seconds") => Some(TimeUnit::Second),
|
||||
Some("min" | "minute" | "minutes") => Some(TimeUnit::Minute),
|
||||
Some("h" | "hour" | "hours") => Some(TimeUnit::Hour),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
|
||||
@@ -5,42 +5,17 @@
|
||||
//! The ASQC Basic References in Quality Control: Statistical Techniques, Edward F. Mykytka,
|
||||
//! Ph.D., Editor.
|
||||
|
||||
use crate::util::statistics::median;
|
||||
|
||||
/// Minimum modified Z-score for a datapoint to be an outlier. Here, 1.4826 is a factor that
|
||||
/// converts the MAD to an estimator for the standard deviation. The second factor is the number
|
||||
/// of standard deviations.
|
||||
pub const OUTLIER_THRESHOLD: f64 = 1.4826 * 10.0;
|
||||
|
||||
/// Compute modifized Z-scores for a given sample. A (unmodified) Z-score is defined by
|
||||
/// `(x_i - x_mean)/x_stddev` whereas the modified Z-score is defined by `(x_i - x_median)/MAD`
|
||||
/// where MAD is the median absolute deviation.
|
||||
///
|
||||
/// References:
|
||||
/// - <https://en.wikipedia.org/wiki/Median_absolute_deviation>
|
||||
pub fn modified_zscores(xs: &[f64]) -> Vec<f64> {
|
||||
assert!(!xs.is_empty());
|
||||
|
||||
// Compute sample median:
|
||||
let x_median = median(xs.iter().copied());
|
||||
|
||||
// Compute the absolute deviations from the median:
|
||||
let deviations: Vec<f64> = xs.iter().map(|x| (x - x_median).abs()).collect();
|
||||
|
||||
// Compute median absolute deviation:
|
||||
let mad = median(deviations.iter().copied());
|
||||
|
||||
// Handle MAD == 0 case
|
||||
let mad = if mad > 0.0 { mad } else { f64::EPSILON };
|
||||
|
||||
// Compute modified Z-scores (x_i - x_median) / MAD
|
||||
xs.iter().map(|&x| (x - x_median) / mad).collect()
|
||||
}
|
||||
|
||||
/// Return the number of outliers in a given sample. Outliers are defined as data points with a
|
||||
/// modified Z-score that is larger than `OUTLIER_THRESHOLD`.
|
||||
#[cfg(test)]
|
||||
pub fn num_outliers(xs: &[f64]) -> usize {
|
||||
use crate::quantity::statistics::modified_zscores;
|
||||
|
||||
if xs.is_empty() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
use crate::util::units::{Second, Unit};
|
||||
|
||||
/// Format the given duration as a string. The output-unit can be enforced by setting `unit` to
|
||||
/// `Some(target_unit)`. If `unit` is `None`, it will be determined automatically.
|
||||
pub fn format_duration(duration: Second, unit: Option<Unit>) -> String {
|
||||
let (duration_fmt, _) = format_duration_unit(duration, unit);
|
||||
duration_fmt
|
||||
}
|
||||
|
||||
/// Like `format_duration`, but returns the target unit as well.
|
||||
pub fn format_duration_unit(duration: Second, unit: Option<Unit>) -> (String, Unit) {
|
||||
let (out_str, out_unit) = format_duration_value(duration, unit);
|
||||
|
||||
(format!("{} {}", out_str, out_unit.short_name()), out_unit)
|
||||
}
|
||||
|
||||
/// Like `format_duration`, but returns the target unit as well.
|
||||
pub fn format_duration_value(duration: Second, unit: Option<Unit>) -> (String, Unit) {
|
||||
if (duration < 0.001 && unit.is_none()) || unit == Some(Unit::MicroSecond) {
|
||||
(Unit::MicroSecond.format(duration), Unit::MicroSecond)
|
||||
} else if (duration < 1.0 && unit.is_none()) || unit == Some(Unit::MilliSecond) {
|
||||
(Unit::MilliSecond.format(duration), Unit::MilliSecond)
|
||||
} else {
|
||||
let unit = unit.unwrap_or(Unit::Second);
|
||||
(unit.format(duration), unit)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_format_duration_unit_basic() {
|
||||
let (out_str, out_unit) = format_duration_unit(1.3, None);
|
||||
|
||||
assert_eq!("1.300 s", out_str);
|
||||
assert_eq!(Unit::Second, out_unit);
|
||||
|
||||
let (out_str, out_unit) = format_duration_unit(1.0, None);
|
||||
|
||||
assert_eq!("1.000 s", out_str);
|
||||
assert_eq!(Unit::Second, out_unit);
|
||||
|
||||
let (out_str, out_unit) = format_duration_unit(0.999, None);
|
||||
|
||||
assert_eq!("999.0 ms", out_str);
|
||||
assert_eq!(Unit::MilliSecond, out_unit);
|
||||
|
||||
let (out_str, out_unit) = format_duration_unit(0.0005, None);
|
||||
|
||||
assert_eq!("500.0 µs", out_str);
|
||||
assert_eq!(Unit::MicroSecond, out_unit);
|
||||
|
||||
let (out_str, out_unit) = format_duration_unit(0.0, None);
|
||||
|
||||
assert_eq!("0.0 µs", out_str);
|
||||
assert_eq!(Unit::MicroSecond, out_unit);
|
||||
|
||||
let (out_str, out_unit) = format_duration_unit(1000.0, None);
|
||||
|
||||
assert_eq!("1000.000 s", out_str);
|
||||
assert_eq!(Unit::Second, out_unit);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_format_duration_unit_with_unit() {
|
||||
let (out_str, out_unit) = format_duration_unit(1.3, Some(Unit::Second));
|
||||
|
||||
assert_eq!("1.300 s", out_str);
|
||||
assert_eq!(Unit::Second, out_unit);
|
||||
|
||||
let (out_str, out_unit) = format_duration_unit(1.3, Some(Unit::MilliSecond));
|
||||
|
||||
assert_eq!("1300.0 ms", out_str);
|
||||
assert_eq!(Unit::MilliSecond, out_unit);
|
||||
|
||||
let (out_str, out_unit) = format_duration_unit(1.3, Some(Unit::MicroSecond));
|
||||
|
||||
assert_eq!("1300000.0 µs", out_str);
|
||||
assert_eq!(Unit::MicroSecond, out_unit);
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
pub mod format;
|
||||
pub mod progress_bar;
|
||||
pub mod warnings;
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
use std::fmt;
|
||||
|
||||
use crate::benchmark::MIN_EXECUTION_TIME;
|
||||
use crate::output::format::format_duration;
|
||||
use crate::quantity::{second, Time};
|
||||
use crate::quantity::{format_duration, second, Time};
|
||||
|
||||
pub struct OutlierWarningOptions {
|
||||
pub warmup_in_use: bool,
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
use std::marker::PhantomData;
|
||||
|
||||
pub use uom::num_traits::Zero;
|
||||
pub use uom::si::f64::{Information, Ratio, Time};
|
||||
pub use uom::si::information::byte;
|
||||
pub use uom::si::ratio::ratio;
|
||||
pub use uom::si::time::{microsecond, nanosecond, second};
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
use uom::si::{information::kibibyte, time::millisecond};
|
||||
|
||||
#[test]
|
||||
fn test_time() {
|
||||
let time = Time::new::<millisecond>(123.4);
|
||||
assert_eq!(time.get::<millisecond>(), 123.4);
|
||||
|
||||
let time_s = time.get::<second>();
|
||||
approx::assert_relative_eq!(time_s, 0.1234);
|
||||
|
||||
let time_us = time.get::<microsecond>();
|
||||
approx::assert_relative_eq!(time_us, 123400.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_information() {
|
||||
use uom::si::information::pebibyte;
|
||||
|
||||
let information = Information::new::<kibibyte>(8.);
|
||||
assert_eq!(information.get::<byte>(), 8192.);
|
||||
|
||||
let information_kib = information.get::<kibibyte>();
|
||||
assert_eq!(information_kib, 8.);
|
||||
|
||||
let largest_exactly_representable = Information::new::<byte>(9_007_199_254_740_992.);
|
||||
assert_eq!(largest_exactly_representable.get::<pebibyte>(), 8.);
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
use std::marker::PhantomData;
|
||||
|
||||
pub use uom::num_traits::Zero;
|
||||
pub use uom::si::f64::{Information, Ratio, Time};
|
||||
pub use uom::si::information::byte;
|
||||
pub use uom::si::ratio::ratio;
|
||||
pub use uom::si::time::{microsecond, nanosecond, second};
|
||||
|
||||
pub use units::TimeUnit;
|
||||
|
||||
pub mod statistics;
|
||||
mod units;
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
/// Format the given duration as a string. The output-unit can be enforced by setting `unit` to
|
||||
/// `Some(target_unit)`. If `unit` is `None`, it will be determined automatically.
|
||||
pub fn format_duration(duration: f64, unit: Option<TimeUnit>) -> String {
|
||||
let (duration_fmt, _) = format_duration_unit(duration, unit);
|
||||
duration_fmt
|
||||
}
|
||||
|
||||
/// Like `format_duration`, but returns the target unit as well.
|
||||
pub fn format_duration_unit(duration: f64, unit: Option<TimeUnit>) -> (String, TimeUnit) {
|
||||
let (out_str, out_unit) = format_duration_value(duration, unit);
|
||||
|
||||
(format!("{} {}", out_str, out_unit.short_name()), out_unit)
|
||||
}
|
||||
|
||||
/// Like `format_duration`, but returns the target unit as well.
|
||||
pub fn format_duration_value(duration: f64, unit: Option<TimeUnit>) -> (String, TimeUnit) {
|
||||
if (duration < 0.001 && unit.is_none()) || unit == Some(TimeUnit::MicroSecond) {
|
||||
(
|
||||
TimeUnit::MicroSecond.format(duration),
|
||||
TimeUnit::MicroSecond,
|
||||
)
|
||||
} else if (duration < 1.0 && unit.is_none()) || unit == Some(TimeUnit::MilliSecond) {
|
||||
(
|
||||
TimeUnit::MilliSecond.format(duration),
|
||||
TimeUnit::MilliSecond,
|
||||
)
|
||||
} else {
|
||||
let unit = unit.unwrap_or(TimeUnit::Second);
|
||||
(unit.format(duration), unit)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
use uom::si::{information::kibibyte, time::millisecond};
|
||||
|
||||
#[test]
|
||||
fn test_time() {
|
||||
let time = Time::new::<millisecond>(123.4);
|
||||
assert_eq!(time.get::<millisecond>(), 123.4);
|
||||
|
||||
let time_s = time.get::<second>();
|
||||
approx::assert_relative_eq!(time_s, 0.1234);
|
||||
|
||||
let time_us = time.get::<microsecond>();
|
||||
approx::assert_relative_eq!(time_us, 123400.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_information() {
|
||||
use uom::si::information::pebibyte;
|
||||
|
||||
let information = Information::new::<kibibyte>(8.);
|
||||
assert_eq!(information.get::<byte>(), 8192.);
|
||||
|
||||
let information_kib = information.get::<kibibyte>();
|
||||
assert_eq!(information_kib, 8.);
|
||||
|
||||
let largest_exactly_representable = Information::new::<byte>(9_007_199_254_740_992.);
|
||||
assert_eq!(largest_exactly_representable.get::<pebibyte>(), 8.);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_format_duration_unit_basic() {
|
||||
let (out_str, out_unit) = format_duration_unit(1.3, None);
|
||||
|
||||
assert_eq!("1.300 s", out_str);
|
||||
assert_eq!(TimeUnit::Second, out_unit);
|
||||
|
||||
let (out_str, out_unit) = format_duration_unit(1.0, None);
|
||||
|
||||
assert_eq!("1.000 s", out_str);
|
||||
assert_eq!(TimeUnit::Second, out_unit);
|
||||
|
||||
let (out_str, out_unit) = format_duration_unit(0.999, None);
|
||||
|
||||
assert_eq!("999.0 ms", out_str);
|
||||
assert_eq!(TimeUnit::MilliSecond, out_unit);
|
||||
|
||||
let (out_str, out_unit) = format_duration_unit(0.0005, None);
|
||||
|
||||
assert_eq!("500.0 µs", out_str);
|
||||
assert_eq!(TimeUnit::MicroSecond, out_unit);
|
||||
|
||||
let (out_str, out_unit) = format_duration_unit(0.0, None);
|
||||
|
||||
assert_eq!("0.0 µs", out_str);
|
||||
assert_eq!(TimeUnit::MicroSecond, out_unit);
|
||||
|
||||
let (out_str, out_unit) = format_duration_unit(1000.0, None);
|
||||
|
||||
assert_eq!("1000.000 s", out_str);
|
||||
assert_eq!(TimeUnit::Second, out_unit);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_format_duration_unit_with_unit() {
|
||||
let (out_str, out_unit) = format_duration_unit(1.3, Some(TimeUnit::Second));
|
||||
|
||||
assert_eq!("1.300 s", out_str);
|
||||
assert_eq!(TimeUnit::Second, out_unit);
|
||||
|
||||
let (out_str, out_unit) = format_duration_unit(1.3, Some(TimeUnit::MilliSecond));
|
||||
|
||||
assert_eq!("1300.0 ms", out_str);
|
||||
assert_eq!(TimeUnit::MilliSecond, out_unit);
|
||||
|
||||
let (out_str, out_unit) = format_duration_unit(1.3, Some(TimeUnit::MicroSecond));
|
||||
|
||||
assert_eq!("1300000.0 µs", out_str);
|
||||
assert_eq!(TimeUnit::MicroSecond, out_unit);
|
||||
}
|
||||
@@ -106,6 +106,31 @@ pub fn standard_deviation<Q: UnsafeRawValue>(values: &[Q], mean: Q) -> Q {
|
||||
Q::unsafe_from_raw_value((sum / (values.len() as f64 - 1.0)).sqrt())
|
||||
}
|
||||
|
||||
/// Compute modifized Z-scores for a given sample. A (unmodified) Z-score is defined by
|
||||
/// `(x_i - x_mean)/x_stddev` whereas the modified Z-score is defined by `(x_i - x_median)/MAD`
|
||||
/// where MAD is the median absolute deviation.
|
||||
///
|
||||
/// References:
|
||||
/// - <https://en.wikipedia.org/wiki/Median_absolute_deviation>
|
||||
pub fn modified_zscores(xs: &[f64]) -> Vec<f64> {
|
||||
assert!(!xs.is_empty());
|
||||
|
||||
// Compute sample median:
|
||||
let x_median = median(xs.iter().copied());
|
||||
|
||||
// Compute the absolute deviations from the median:
|
||||
let deviations: Vec<f64> = xs.iter().map(|x| (x - x_median).abs()).collect();
|
||||
|
||||
// Compute median absolute deviation:
|
||||
let mad = median(deviations.iter().copied());
|
||||
|
||||
// Handle MAD == 0 case
|
||||
let mad = if mad > 0.0 { mad } else { f64::EPSILON };
|
||||
|
||||
// Compute modified Z-scores (x_i - x_median) / MAD
|
||||
xs.iter().map(|&x| (x - x_median) / mad).collect()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -0,0 +1,56 @@
|
||||
//! This module contains common units.
|
||||
|
||||
/// Supported time units
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum TimeUnit {
|
||||
Second,
|
||||
MilliSecond,
|
||||
MicroSecond,
|
||||
Minute,
|
||||
Hour,
|
||||
}
|
||||
|
||||
impl TimeUnit {
|
||||
/// The abbreviation of the TimeUnit.
|
||||
pub fn short_name(self) -> String {
|
||||
match self {
|
||||
TimeUnit::Second => String::from("s"),
|
||||
TimeUnit::MilliSecond => String::from("ms"),
|
||||
TimeUnit::MicroSecond => String::from("µs"),
|
||||
TimeUnit::Minute => String::from("min"),
|
||||
TimeUnit::Hour => String::from("h"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the value in seconds formatted for the TimeUnit.
|
||||
pub fn format(self, value: f64) -> String {
|
||||
match self {
|
||||
TimeUnit::Second => format!("{value:.3}"),
|
||||
TimeUnit::MilliSecond => format!("{:.1}", value * 1e3),
|
||||
TimeUnit::MicroSecond => format!("{:.1}", value * 1e6),
|
||||
TimeUnit::Minute => format!("{:.1}", value / 60.0),
|
||||
TimeUnit::Hour => format!("{:.1}", value / 3600.0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_unit_short_name() {
|
||||
assert_eq!("s", TimeUnit::Second.short_name());
|
||||
assert_eq!("ms", TimeUnit::MilliSecond.short_name());
|
||||
assert_eq!("µs", TimeUnit::MicroSecond.short_name());
|
||||
assert_eq!("min", TimeUnit::Minute.short_name());
|
||||
assert_eq!("h", TimeUnit::Hour.short_name());
|
||||
}
|
||||
|
||||
// Note - the values are rounded when formatted.
|
||||
#[test]
|
||||
fn test_unit_format() {
|
||||
let value: f64 = 123.456789;
|
||||
assert_eq!("123.457", TimeUnit::Second.format(value));
|
||||
assert_eq!("123456.8", TimeUnit::MilliSecond.format(value));
|
||||
|
||||
assert_eq!("1234.6", TimeUnit::MicroSecond.format(0.00123456));
|
||||
assert_eq!("2.1", TimeUnit::Minute.format(value));
|
||||
assert_eq!("0.0", TimeUnit::Hour.format(value));
|
||||
}
|
||||
@@ -1,5 +1,3 @@
|
||||
pub mod exit_code;
|
||||
pub mod number;
|
||||
pub mod randomized_environment_offset;
|
||||
pub mod statistics;
|
||||
pub mod units;
|
||||
@@ -1,61 +0,0 @@
|
||||
//! This module contains common units.
|
||||
|
||||
pub type Scalar = f64;
|
||||
|
||||
/// Type alias for unit of time
|
||||
pub type Second = Scalar;
|
||||
|
||||
/// Supported time units
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Unit {
|
||||
Second,
|
||||
MilliSecond,
|
||||
MicroSecond,
|
||||
Minute,
|
||||
Hour,
|
||||
}
|
||||
|
||||
impl Unit {
|
||||
/// The abbreviation of the Unit.
|
||||
pub fn short_name(self) -> String {
|
||||
match self {
|
||||
Unit::Second => String::from("s"),
|
||||
Unit::MilliSecond => String::from("ms"),
|
||||
Unit::MicroSecond => String::from("µs"),
|
||||
Unit::Minute => String::from("min"),
|
||||
Unit::Hour => String::from("h"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the Second value formatted for the Unit.
|
||||
pub fn format(self, value: Second) -> String {
|
||||
match self {
|
||||
Unit::Second => format!("{value:.3}"),
|
||||
Unit::MilliSecond => format!("{:.1}", value * 1e3),
|
||||
Unit::MicroSecond => format!("{:.1}", value * 1e6),
|
||||
Unit::Minute => format!("{:.1}", value / 60.0),
|
||||
Unit::Hour => format!("{:.1}", value / 3600.0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_unit_short_name() {
|
||||
assert_eq!("s", Unit::Second.short_name());
|
||||
assert_eq!("ms", Unit::MilliSecond.short_name());
|
||||
assert_eq!("µs", Unit::MicroSecond.short_name());
|
||||
assert_eq!("min", Unit::Minute.short_name());
|
||||
assert_eq!("h", Unit::Hour.short_name());
|
||||
}
|
||||
|
||||
// Note - the values are rounded when formatted.
|
||||
#[test]
|
||||
fn test_unit_format() {
|
||||
let value: Second = 123.456789;
|
||||
assert_eq!("123.457", Unit::Second.format(value));
|
||||
assert_eq!("123456.8", Unit::MilliSecond.format(value));
|
||||
|
||||
assert_eq!("1234.6", Unit::MicroSecond.format(0.00123456));
|
||||
assert_eq!("2.1", Unit::Minute.format(value));
|
||||
assert_eq!("0.0", Unit::Hour.format(value));
|
||||
}
|
||||
Reference in new issue
Block a user