use crate::format::short;
use crate::DurationExt;
use crate::Throughput;
use std::time::{Duration, Instant};
pub trait ValueFormatter {
fn format_value(&self, value: f64) -> String {
let mut values = [value];
let unit = self.scale_values(value, &mut values);
format!("{:>6} {}", short(values[0]), unit)
}
fn format_throughput(&self, throughput: &Throughput, value: f64) -> String {
let mut values = [value];
let unit = self.scale_throughputs(value, throughput, &mut values);
format!("{:>6} {}", short(values[0]), unit)
}
fn scale_values(&self, typical_value: f64, values: &mut [f64]) -> &'static str;
fn scale_throughputs(
&self,
typical_value: f64,
throughput: &Throughput,
values: &mut [f64],
) -> &'static str;
fn scale_for_machines(&self, values: &mut [f64]) -> &'static str;
}
pub trait Measurement {
type Intermediate;
type Value;
fn start(&self) -> Self::Intermediate;
fn end(&self, i: Self::Intermediate) -> Self::Value;
fn add(&self, v1: &Self::Value, v2: &Self::Value) -> Self::Value;
fn zero(&self) -> Self::Value;
fn to_f64(&self, value: &Self::Value) -> f64;
fn formatter(&self) -> &dyn ValueFormatter;
}
pub(crate) struct DurationFormatter;
impl DurationFormatter {
fn bytes_per_second(&self, bytes: f64, typical: f64, values: &mut [f64]) -> &'static str {
let bytes_per_second = bytes * (1e9 / typical);
let (denominator, unit) = if bytes_per_second < 1024.0 {
(1.0, " B/s")
} else if bytes_per_second < 1024.0 * 1024.0 {
(1024.0, "KiB/s")
} else if bytes_per_second < 1024.0 * 1024.0 * 1024.0 {
(1024.0 * 1024.0, "MiB/s")
} else {
(1024.0 * 1024.0 * 1024.0, "GiB/s")
};
for val in values {
let bytes_per_second = bytes * (1e9 / *val);
*val = bytes_per_second / denominator;
}
unit
}
fn elements_per_second(&self, elems: f64, typical: f64, values: &mut [f64]) -> &'static str {
let elems_per_second = elems * (1e9 / typical);
let (denominator, unit) = if elems_per_second < 1000.0 {
(1.0, " elem/s")
} else if elems_per_second < 1000.0 * 1000.0 {
(1000.0, "Kelem/s")
} else if elems_per_second < 1000.0 * 1000.0 * 1000.0 {
(1000.0 * 1000.0, "Melem/s")
} else {
(1000.0 * 1000.0 * 1000.0, "Gelem/s")
};
for val in values {
let elems_per_second = elems * (1e9 / *val);
*val = elems_per_second / denominator;
}
unit
}
}
impl ValueFormatter for DurationFormatter {
fn scale_throughputs(
&self,
typical: f64,
throughput: &Throughput,
values: &mut [f64],
) -> &'static str {
match *throughput {
Throughput::Bytes(bytes) => self.bytes_per_second(bytes as f64, typical, values),
Throughput::Elements(elems) => self.elements_per_second(elems as f64, typical, values),
}
}
fn scale_values(&self, ns: f64, values: &mut [f64]) -> &'static str {
let (factor, unit) = if ns < 10f64.powi(0) {
(10f64.powi(3), "ps")
} else if ns < 10f64.powi(3) {
(10f64.powi(0), "ns")
} else if ns < 10f64.powi(6) {
(10f64.powi(-3), "us")
} else if ns < 10f64.powi(9) {
(10f64.powi(-6), "ms")
} else {
(10f64.powi(-9), "s")
};
for val in values {
*val *= factor;
}
unit
}
fn scale_for_machines(&self, _values: &mut [f64]) -> &'static str {
"ns"
}
}
pub struct WallTime;
impl Measurement for WallTime {
type Intermediate = Instant;
type Value = Duration;
fn start(&self) -> Self::Intermediate {
Instant::now()
}
fn end(&self, i: Self::Intermediate) -> Self::Value {
i.elapsed()
}
fn add(&self, v1: &Self::Value, v2: &Self::Value) -> Self::Value {
*v1 + *v2
}
fn zero(&self) -> Self::Value {
Duration::from_secs(0)
}
fn to_f64(&self, val: &Self::Value) -> f64 {
val.to_nanos() as f64
}
fn formatter(&self) -> &dyn ValueFormatter {
&DurationFormatter
}
}