Add support for showing other metrics in the terminal

This commit is contained in:
David Peter committed 2026-10-06 12:53:42 +02:00
1 parent 7395dde05d
commit 908b33cc1d
31 files changed
+1514 -1898

No files matched your search

+2 -2
View File
@@ -24,7 +24,7 @@ impl MarkupExporter for AsciidocExporter {
}
fn table_row(&self, cells: &[&str]) -> String {
format!("\n| {} \n", cells.join(" \n| "))
format!("\n| {}\n", cells.join("\n| "))
}
fn table_divider(&self, _cell_aligmnents: &[Alignment]) -> String {
@@ -43,7 +43,7 @@ fn test_asciidoc_exporter_table_data() {
let data = vec!["a", "b", "c"];
let actual = exporter.table_row(&data);
let expect = "\n| a \n| b \n| c \n";
let expect = "\n| a\n| b\n| c\n";
assert_eq!(expect, actual);
}
+75 -53
View File
@@ -1,65 +1,73 @@
use std::borrow::Cow;
use csv::WriterBuilder;
use super::Exporter;
use crate::benchmark::benchmark_result::BenchmarkResult;
use crate::options::SortOrder;
use crate::quantity::{FormatQuantity, TimeUnit};
use crate::metric::MetricSelection;
use anyhow::Result;
use anyhow::{Context, Result};
#[derive(Default)]
pub struct CsvExporter {}
impl Exporter for CsvExporter {
fn serialize(
&self,
results: &[BenchmarkResult],
_time_unit: Option<TimeUnit>,
_sort_order: SortOrder,
_reference_index: Option<usize>,
) -> Result<Vec<u8>> {
const CSV_UNIT: TimeUnit = TimeUnit::Second;
const CSV_PRECISION: usize = 6;
fn serialize(&self, results: &[BenchmarkResult], primary: MetricSelection) -> Result<Vec<u8>> {
// CSV never chooses units automatically: an omitted suffix means base units.
let unit = primary.csv_unit();
let mut writer = WriterBuilder::new().from_writer(vec![]);
{
let mut headers: Vec<Cow<[u8]>> = [
// The list of times and exit codes cannot be exported to the CSV file - omit them.
"command", "mean", "stddev", "median", "user", "system", "min", "max",
]
.iter()
.map(|x| Cow::Borrowed(x.as_bytes()))
.collect();
if let Some(res) = results.first() {
for param_name in res.parameters.keys() {
headers.push(Cow::Owned(format!("parameter_{param_name}").into_bytes()));
}
}
writer.write_record(headers)?;
let mut headers: Vec<String> = [
"command", "metric", "unit", "mean", "stddev", "median", "min", "max",
]
.iter()
.copied()
.map(str::to_owned)
.collect();
if let Some(result) = results.first() {
headers.extend(
result
.parameters
.keys()
.map(|name| format!("parameter_{name}")),
);
}
writer.write_record(headers)?;
for res in results {
let mut fields = vec![Cow::Borrowed(res.get_name().as_bytes())];
for f in &[
res.mean_wall_clock_time(),
res.measurements.stddev().unwrap_or_default(),
res.measurements.median(),
res.measurements.time_user_mean(),
res.measurements.time_system_mean(),
res.measurements.min(),
res.measurements.max(),
] {
fields.push(Cow::Owned(
f.format_with_precision(CSV_UNIT, CSV_PRECISION)
.into_bytes(),
))
}
for v in res.parameters.values() {
fields.push(Cow::Borrowed(v.value.as_bytes()))
}
for result in results {
let stats = primary
.metric
.summarize(&result.measurements)
.with_context(|| {
format!(
"Metric '{}' is unavailable for '{}'",
primary.metric.name(),
result.get_name()
)
})?;
let mut fields = vec![
result.get_name().to_owned(),
primary.metric.name().to_owned(),
unit.symbol.to_owned(),
];
// Preserve numeric precision rather than using rounded display values.
fields.extend(
[
Some(stats.mean),
stats.stddev,
Some(stats.median),
Some(stats.min),
Some(stats.max),
]
.map(|value| {
value
.map(|value| (value / unit.scale).to_string())
.unwrap_or_default()
}),
);
fields.extend(
result
.parameters
.values()
.map(|parameter| parameter.value.clone()),
);
writer.write_record(fields)?;
}
@@ -177,14 +185,28 @@ fn test_csv() {
let actual = String::from_utf8(
exporter
.serialize(&results, Some(TimeUnit::Second), SortOrder::Command, None)
.serialize(&results, MetricSelection::default())
.unwrap(),
)
.unwrap();
insta::assert_snapshot!(actual, @r#"
command,mean,stddev,median,user,system,min,max,parameter_bar,parameter_foo
command_a,9.000000,2.645751,8.000000,9.000000,0.000000,7.000000,12.000000,two,one
command_b,18.000000,1.000000,18.000000,18.000000,0.000000,17.000000,19.000000,seven,one
command,metric,unit,mean,stddev,median,min,max,parameter_bar,parameter_foo
command_a,time_wall_clock,s,9,2.6457513110645907,8,7,12,two,one
command_b,time_wall_clock,s,18,1,18,17,19,seven,one
"#);
for (selection, unit, mean) in [
("memory_peak_resident", "B", "1024"),
("memory_peak_resident:KiB", "KiB", "1"),
] {
let output = exporter
.serialize(&results, MetricSelection::parse_list(selection).unwrap()[0])
.unwrap();
let mut reader = csv::Reader::from_reader(output.as_slice());
let row = reader.records().next().unwrap().unwrap();
assert_eq!(&row[1], "memory_peak_resident");
assert_eq!(&row[2], unit);
assert_eq!(&row[3], mean);
}
}
+33 -178
View File
@@ -1,14 +1,11 @@
use std::ops::{Add, AddAssign, Div};
use std::collections::BTreeMap;
use serde::*;
use serde::Serialize;
use serde_json::to_vec_pretty;
use super::Exporter;
use crate::benchmark::benchmark_result::BenchmarkResult;
use crate::benchmark::measurement::Measurements;
use crate::options::SortOrder;
use crate::quantity::statistics::{max, mean, median, min, standard_deviation, UnsafeRawValue};
use crate::quantity::{byte, ratio, second, QuantityInUnit, Ratio, TimeUnit, Zero};
use crate::metric::{Metric, MetricSelection, Stats};
use anyhow::Result;
@@ -21,6 +18,7 @@ const JSON_SCHEMA_VERSION: u32 = 2;
struct HyperfineSummary<'a> {
schema_version: u32,
metadata: &'a Metadata,
primary_metric: &'static str,
results: Vec<JsonBenchmarkResult<'a>>,
}
@@ -28,98 +26,7 @@ struct HyperfineSummary<'a> {
struct JsonBenchmarkResult<'a> {
#[serde(flatten)]
result: &'a BenchmarkResult,
#[serde(skip_serializing_if = "std::ops::Not::not")]
is_reference: bool,
summary: BenchmarkSummary,
}
#[derive(Serialize)]
struct BenchmarkSummary {
time_wall_clock: StatisticalSummary,
time_user: StatisticalSummary,
time_system: StatisticalSummary,
#[serde(skip_serializing_if = "Option::is_none")]
memory_peak_resident: Option<StatisticalSummary>,
#[serde(skip_serializing_if = "Option::is_none")]
cpu_cycles: Option<StatisticalSummary>,
#[serde(skip_serializing_if = "Option::is_none")]
instructions: Option<StatisticalSummary>,
#[serde(skip_serializing_if = "Option::is_none")]
cache_references: Option<StatisticalSummary>,
#[serde(skip_serializing_if = "Option::is_none")]
cache_misses: Option<StatisticalSummary>,
#[serde(skip_serializing_if = "Option::is_none")]
branch_misses: Option<StatisticalSummary>,
}
impl BenchmarkSummary {
fn from_measurements(measurements: &Measurements) -> Self {
let values = &measurements.measurements;
Self {
time_wall_clock: StatisticalSummary::from_values(
values.iter().map(|m| Some(m.time_wall_clock)),
Some(second),
)
.expect("Benchmarks have at least one measurement"),
time_user: StatisticalSummary::from_values(
values.iter().map(|m| Some(m.time_user)),
Some(second),
)
.expect("Benchmarks have at least one measurement"),
time_system: StatisticalSummary::from_values(
values.iter().map(|m| Some(m.time_system)),
Some(second),
)
.expect("Benchmarks have at least one measurement"),
memory_peak_resident: StatisticalSummary::from_values(
values
.iter()
.filter_map(|m| m.memory_peak_resident)
.map(Some),
Some(byte),
),
cpu_cycles: StatisticalSummary::from_values(
values.iter().map(|m| {
m.hardware_counters
.cpu_cycles
.map(|v| Ratio::new::<ratio>(v as f64))
}),
None::<ratio>,
),
instructions: StatisticalSummary::from_values(
values.iter().map(|m| {
m.hardware_counters
.instructions
.map(|v| Ratio::new::<ratio>(v as f64))
}),
None::<ratio>,
),
cache_references: StatisticalSummary::from_values(
values.iter().map(|m| {
m.hardware_counters
.cache_references
.map(|v| Ratio::new::<ratio>(v as f64))
}),
None::<ratio>,
),
cache_misses: StatisticalSummary::from_values(
values.iter().map(|m| {
m.hardware_counters
.cache_misses
.map(|v| Ratio::new::<ratio>(v as f64))
}),
None::<ratio>,
),
branch_misses: StatisticalSummary::from_values(
values.iter().map(|m| {
m.hardware_counters
.branch_misses
.map(|v| Ratio::new::<ratio>(v as f64))
}),
None::<ratio>,
),
}
}
summary: BTreeMap<&'static str, StatisticalSummary>,
}
#[derive(Serialize)]
@@ -135,37 +42,20 @@ struct StatisticalSummary {
}
impl StatisticalSummary {
fn from_values<Q, P, U>(
values: impl IntoIterator<Item = Option<Q>>,
unit: Option<U>,
) -> Option<Self>
where
Q: Copy
+ PartialOrd
+ Add<Output = Q>
+ AddAssign
+ Zero
+ Div<Ratio, Output = P>
+ UnsafeRawValue
+ QuantityInUnit<U>,
P: Into<Q>,
U: uom::si::Unit,
{
// Only summarize a metric when it is available for every supplied measurement.
let values: Vec<_> = values.into_iter().collect::<Option<_>>()?;
if values.is_empty() {
return None;
fn new(metric: Metric, stats: Stats) -> Self {
Self {
unit: match metric.base_unit().symbol {
"s" => Some("second"),
"B" => Some("byte"),
_ => None,
},
count: stats.count,
mean: stats.mean,
stddev: stats.stddev,
median: stats.median,
min: stats.min,
max: stats.max,
}
Some(Self {
unit: unit.map(|_| U::singular()),
count: values.len(),
mean: mean(values.iter().copied()).value_in_unit(),
stddev: (values.len() > 1)
.then(|| standard_deviation(values.iter().copied()).value_in_unit()),
median: median(values.iter().copied()).value_in_unit(),
min: min(values.iter().copied()).value_in_unit(),
max: max(values.iter().copied()).value_in_unit(),
})
}
}
@@ -175,63 +65,28 @@ pub struct JsonExporter {
}
impl Exporter for JsonExporter {
fn serialize(
&self,
results: &[BenchmarkResult],
_time_unit: Option<TimeUnit>,
_sort_order: SortOrder,
reference_index: Option<usize>,
) -> Result<Vec<u8>> {
fn serialize(&self, results: &[BenchmarkResult], primary: MetricSelection) -> Result<Vec<u8>> {
let mut output = to_vec_pretty(&HyperfineSummary {
schema_version: JSON_SCHEMA_VERSION,
metadata: &self.metadata,
primary_metric: primary.metric.name(),
results: results
.iter()
.enumerate()
.map(|(index, result)| JsonBenchmarkResult {
.map(|result| JsonBenchmarkResult {
result,
is_reference: reference_index == Some(index),
summary: BenchmarkSummary::from_measurements(&result.measurements),
summary: Metric::ALL
.iter()
.copied()
.filter_map(|metric| {
metric.summarize(&result.measurements).map(|stats| {
(metric.name(), StatisticalSummary::new(metric, stats))
})
})
.collect(),
})
.collect(),
});
if let Ok(ref mut content) = output {
content.push(b'\n');
}
Ok(output?)
}
}
#[test]
fn test_statistical_summary_unit_conversion() {
use crate::quantity::{kibibyte, millisecond, Information, Time};
let time = StatisticalSummary::from_values(
[Time::new::<second>(1.0), Time::new::<second>(3.0)].map(Some),
Some(millisecond),
)
.unwrap();
let information = StatisticalSummary::from_values(
[
Information::new::<byte>(1024.0),
Information::new::<byte>(3072.0),
]
.map(Some),
Some(kibibyte),
)
.unwrap();
for (summary, unit, scale) in [
(time, "millisecond", 1000.0),
(information, "kibibyte", 1.0),
] {
assert_eq!(summary.unit, Some(unit));
assert_eq!(summary.count, 2);
approx::assert_relative_eq!(summary.mean, 2.0 * scale);
approx::assert_relative_eq!(summary.median, 2.0 * scale);
approx::assert_relative_eq!(summary.min, scale);
approx::assert_relative_eq!(summary.max, 3.0 * scale);
approx::assert_relative_eq!(summary.stddev.unwrap(), 2.0_f64.sqrt() * scale);
})?;
output.push(b'\n');
Ok(output)
}
}
+57 -99
View File
@@ -1,11 +1,8 @@
use crate::benchmark::relative_speed::BenchmarkResultWithRelativeSpeed;
use crate::benchmark::{benchmark_result::BenchmarkResult, relative_speed};
use crate::options::SortOrder;
use crate::quantity::{FormatQuantity, IsUnit, Time, TimeUnit, Zero};
use crate::benchmark::benchmark_result::BenchmarkResult;
use crate::metric::{MetricSelection, Stats, Unit};
use super::Exporter;
use anyhow::Result;
use std::cmp::Ordering;
use anyhow::{Context, Result};
pub enum Alignment {
Left,
@@ -15,15 +12,12 @@ pub enum Alignment {
pub trait MarkupExporter {
fn table_results(
&self,
entries: &[BenchmarkResultWithRelativeSpeed],
time_unit: TimeUnit,
reference_pending: bool,
explicit_reference: bool,
results: &[BenchmarkResult],
summaries: &[Stats],
primary: MetricSelection,
unit: Unit,
) -> String {
// prepare table header strings
let notation = format!("[{}]", time_unit.short_name());
// prepare table cells alignment
let notation = format!("[{}]", unit.symbol);
let cells_alignment = [
Alignment::Left,
Alignment::Right,
@@ -31,116 +25,80 @@ pub trait MarkupExporter {
Alignment::Right,
Alignment::Right,
];
// emit table header format
let mut table = self.table_header(&cells_alignment);
// emit table header data
table.push_str(&self.table_row(&[
"Command",
&format!("Mean {notation}"),
&format!("Mean {} {notation}", primary.metric.label()),
&format!("Min {notation}"),
&format!("Max {notation}"),
"Relative",
"Change",
]));
// emit horizontal line
table.push_str(&self.table_divider(&cells_alignment));
for entry in entries {
let result = &entry.result;
// prepare data row strings
let cmd_str = result.display_name.replace('|', "\\|");
let mean_str = result.mean_wall_clock_time().format_value(time_unit);
let stddev_str = if let Some(stddev) = result.measurements.stddev() {
format!(" ± {}", stddev.format_value(time_unit))
for (index, (result, stats)) in results.iter().zip(summaries).enumerate() {
let command = result.display_name.replace('|', "\\|");
let mean = unit.format_value(stats.mean);
let stddev = stats
.stddev
.map(|value| format!(" ± {}", unit.format_value(value)))
.unwrap_or_default();
let change = if index == 0 {
"reference".to_owned()
} else {
"".into()
stats.format_change_from(&summaries[0])
};
let min_str = result.measurements.min().format_value(time_unit);
let max_str = result.measurements.max().format_value(time_unit);
// The ratio of two zero times is undefined, even if they compare equal.
let relative_unavailable = reference_pending
|| (explicit_reference
&& !entry.is_reference
&& entry.relative_ordering == Ordering::Equal
&& result.mean_wall_clock_time() == Time::zero());
let relative = if relative_unavailable {
"N/A".to_string()
} else {
let stddev = match (entry.is_reference, entry.relative_speed_stddev) {
(false, Some(stddev)) => format!(" ± {stddev:.2}"),
_ => String::new(),
};
let label = if explicit_reference {
entry.reference_label()
} else {
""
};
format!("{:.2}{stddev}{label}", entry.relative_speed)
};
// prepare table row entries
table.push_str(&self.table_row(&[
&self.command(&cmd_str),
&format!("{mean_str}{stddev_str}"),
&min_str,
&max_str,
&relative,
]))
&self.command(&command),
&format!("{mean}{stddev}"),
&unit.format_value(stats.min),
&unit.format_value(stats.max),
&change,
]));
}
// emit table footer format
table.push_str(&self.table_footer(&cells_alignment));
table
}
fn table_row(&self, cells: &[&str]) -> String;
fn table_divider(&self, cell_aligmnents: &[Alignment]) -> String;
fn table_divider(&self, cell_alignments: &[Alignment]) -> String;
fn table_header(&self, _cell_aligmnents: &[Alignment]) -> String {
"".to_string()
fn table_header(&self, _cell_alignments: &[Alignment]) -> String {
String::new()
}
fn table_footer(&self, _cell_aligmnents: &[Alignment]) -> String {
"".to_string()
fn table_footer(&self, _cell_alignments: &[Alignment]) -> String {
String::new()
}
fn command(&self, size: &str) -> String;
}
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.
first_result.mean_wall_clock_time().suitable_unit()
} else {
// Default to `Second`.
TimeUnit::Second
}
fn command(&self, command: &str) -> String;
}
impl<T: MarkupExporter> Exporter for T {
fn serialize(
&self,
results: &[BenchmarkResult],
time_unit: Option<TimeUnit>,
sort_order: SortOrder,
reference_index: Option<usize>,
) -> Result<Vec<u8>> {
let unit = time_unit.unwrap_or_else(|| determine_unit_from_results(results));
// Do not report ratios against another benchmark while the selected
// reference is still pending in an intermediate export.
let reference_pending = reference_index.is_some_and(|i| i >= results.len());
let entries = relative_speed::compute(
results,
sort_order,
reference_index.and_then(|i| results.get(i)),
);
let table =
self.table_results(&entries, unit, reference_pending, reference_index.is_some());
Ok(table.as_bytes().to_vec())
fn serialize(&self, results: &[BenchmarkResult], primary: MetricSelection) -> Result<Vec<u8>> {
let stats: Vec<_> = results
.iter()
.map(|result| {
primary
.metric
.summarize(&result.measurements)
.with_context(|| {
format!(
"Metric '{}' is unavailable for '{}'",
primary.metric.name(),
result.get_name()
)
})
})
.collect::<Result<_>>()?;
// Use one common unit, avoiding an unscaled column if the reference is zero.
let mean = stats
.iter()
.find(|stats| stats.mean != 0.0)
.map_or(0.0, |stats| stats.mean);
let unit = primary.display_unit(mean);
Ok(self
.table_results(results, &stats, primary, unit)
.into_bytes())
}
}
+7 -30
View File
@@ -17,9 +17,8 @@ use self::markdown::MarkdownExporter;
use self::orgmode::OrgmodeExporter;
use crate::benchmark::benchmark_result::BenchmarkResult;
use crate::options::SortOrder;
use crate::metric::MetricSelection;
use crate::output::console_writeln;
use crate::quantity::TimeUnit;
use anyhow::{Context, Result};
use clap::ArgMatches;
@@ -46,13 +45,7 @@ pub enum ExportType {
/// Interface for different exporters.
trait Exporter {
/// Export the given entries in the serialized form.
fn serialize(
&self,
results: &[BenchmarkResult],
time_unit: Option<TimeUnit>,
sort_order: SortOrder,
reference_index: Option<usize>,
) -> Result<Vec<u8>>;
fn serialize(&self, results: &[BenchmarkResult], primary: MetricSelection) -> Result<Vec<u8>>;
}
pub enum ExportTarget {
@@ -68,22 +61,16 @@ struct ExporterWithTarget {
/// Handles the management of multiple file exporters.
pub struct ExportManager {
exporters: Vec<ExporterWithTarget>,
time_unit: Option<TimeUnit>,
sort_order: SortOrder,
primary: MetricSelection,
}
impl ExportManager {
/// Build the ExportManager that will export the results specified
/// in the given ArgMatches
pub fn from_cli_arguments(
matches: &ArgMatches,
time_unit: Option<TimeUnit>,
sort_order: SortOrder,
) -> Result<Self> {
pub fn from_cli_arguments(matches: &ArgMatches, primary: MetricSelection) -> Result<Self> {
let mut export_manager = Self {
exporters: vec![],
time_unit,
sort_order,
primary,
};
{
let mut add_exporter = |flag, exporttype| -> Result<()> {
@@ -131,19 +118,9 @@ impl ExportManager {
/// results are written to all file targets (to always have them up to date, even
/// if a benchmark fails). In the latter case, we only print to stdout targets (in
/// order not to clutter the output of hyperfine with intermediate results).
/// `reference_index` refers to the complete benchmark sequence, so it can
/// point past `results` while that benchmark is still pending.
pub fn write_results(
&self,
results: &[BenchmarkResult],
intermediate: bool,
reference_index: Option<usize>,
) -> Result<()> {
pub fn write_results(&self, results: &[BenchmarkResult], intermediate: bool) -> Result<()> {
for e in &self.exporters {
let content = || {
e.exporter
.serialize(results, self.time_unit, self.sort_order, reference_index)
};
let content = || e.exporter.serialize(results, self.primary);
match e.target {
ExportTarget::File(ref filename) => {
+108 -107
View File
@@ -2,19 +2,27 @@ use super::Exporter;
use crate::benchmark::benchmark_result::BenchmarkResult;
use crate::benchmark::measurement::{Measurement, Measurements};
use crate::export::asciidoc::AsciidocExporter;
use crate::export::markdown::MarkdownExporter;
use crate::export::orgmode::OrgmodeExporter;
use crate::quantity::{byte, second, Information, Time, TimeUnit, Zero};
use crate::{export::markdown::MarkdownExporter, options::SortOrder};
use crate::metric::{MetricSelection, Unit};
use crate::quantity::{byte, second, Information, Time, Zero};
use std::collections::BTreeMap;
use std::process::ExitStatus;
fn get_output<E: Exporter + Default>(
results: &[BenchmarkResult],
unit: Option<TimeUnit>,
sort_order: SortOrder,
) -> String {
fn get_output<E: Exporter + Default>(results: &[BenchmarkResult], unit: Option<Unit>) -> String {
let exporter = E::default();
String::from_utf8(exporter.serialize(results, unit, sort_order, None).unwrap()).unwrap()
String::from_utf8(
exporter
.serialize(
results,
MetricSelection {
unit,
..MetricSelection::default()
},
)
.unwrap(),
)
.unwrap()
}
/// Ensure the makrup output includes the table header and the multiple
@@ -92,41 +100,41 @@ fn test_markup_export_auto_ms() {
},
];
insta::assert_snapshot!(get_output::<MarkdownExporter>(&results, None, SortOrder::Command), @r#"
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
insta::assert_snapshot!(get_output::<MarkdownExporter>(&results, None), @r#"
| Command | Mean Time [ms] | Min [ms] | Max [ms] | Change |
|:---|---:|---:|---:|---:|
| `sleep 0.1` | 110.0 ± 26.5 | 90.0 | 140.0 | 1.00 |
| `sleep 2` | 3000.0 ± 1000.0 | 2000.0 | 4000.0 | 27.27 ± 11.21 |
| `sleep 0.1` | 110.0 ± 26.5 | 90.0 | 140.0 | reference |
| `sleep 2` | 3000.0 ± 1000.0 | 2000.0 | 4000.0 | +2627.3% |
"#);
insta::assert_snapshot!(get_output::<AsciidocExporter>(&results, None, SortOrder::Command), @r#"
insta::assert_snapshot!(get_output::<AsciidocExporter>(&results, None), @r#"
[cols="<,>,>,>,>"]
|===
| Command
| Mean [ms]
| Min [ms]
| Max [ms]
| Relative
| Command
| Mean Time [ms]
| Min [ms]
| Max [ms]
| Change
| `sleep 0.1`
| 110.0 ± 26.5
| 90.0
| 140.0
| 1.00
| `sleep 0.1`
| 110.0 ± 26.5
| 90.0
| 140.0
| reference
| `sleep 2`
| 3000.0 ± 1000.0
| 2000.0
| 4000.0
| 27.27 ± 11.21
| `sleep 2`
| 3000.0 ± 1000.0
| 2000.0
| 4000.0
| +2627.3%
|===
"#);
insta::assert_snapshot!(get_output::<OrgmodeExporter>(&results, None, SortOrder::Command), @r#"
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
insta::assert_snapshot!(get_output::<OrgmodeExporter>(&results, None), @r#"
| Command | Mean Time [ms] | Min [ms] | Max [ms] | Change |
|--+--+--+--+--|
| =sleep 0.1= | 110.0 ± 26.5 | 90.0 | 140.0 | 1.00 |
| =sleep 2= | 3000.0 ± 1000.0 | 2000.0 | 4000.0 | 27.27 ± 11.21 |
| =sleep 0.1= | 110.0 ± 26.5 | 90.0 | 140.0 | reference |
| =sleep 2= | 3000.0 ± 1000.0 | 2000.0 | 4000.0 | +2627.3% |
"#);
}
@@ -201,41 +209,41 @@ fn test_markup_export_auto_s() {
},
];
insta::assert_snapshot!(get_output::<MarkdownExporter>(&results, None, SortOrder::Command), @r#"
| Command | Mean [s] | Min [s] | Max [s] | Relative |
insta::assert_snapshot!(get_output::<MarkdownExporter>(&results, None), @r#"
| Command | Mean Time [s] | Min [s] | Max [s] | Change |
|:---|---:|---:|---:|---:|
| `sleep 2` | 2.200 ± 0.100 | 2.100 | 2.300 | 11.00 ± 5.52 |
| `sleep 0.1` | 0.200 ± 0.100 | 0.100 | 0.300 | 1.00 |
| `sleep 2` | 2.200 ± 0.100 | 2.100 | 2.300 | reference |
| `sleep 0.1` | 0.200 ± 0.100 | 0.100 | 0.300 | -90.9% |
"#);
insta::assert_snapshot!(get_output::<AsciidocExporter>(&results, None, SortOrder::Command), @r#"
insta::assert_snapshot!(get_output::<AsciidocExporter>(&results, None), @r#"
[cols="<,>,>,>,>"]
|===
| Command
| Mean [s]
| Min [s]
| Max [s]
| Relative
| Command
| Mean Time [s]
| Min [s]
| Max [s]
| Change
| `sleep 2`
| 2.200 ± 0.100
| 2.100
| 2.300
| 11.00 ± 5.52
| `sleep 2`
| 2.200 ± 0.100
| 2.100
| 2.300
| reference
| `sleep 0.1`
| 0.200 ± 0.100
| 0.100
| 0.300
| 1.00
| `sleep 0.1`
| 0.200 ± 0.100
| 0.100
| 0.300
| -90.9%
|===
"#);
insta::assert_snapshot!(get_output::<OrgmodeExporter>(&results, None, SortOrder::Command), @r#"
| Command | Mean [s] | Min [s] | Max [s] | Relative |
insta::assert_snapshot!(get_output::<OrgmodeExporter>(&results, None), @r#"
| Command | Mean Time [s] | Min [s] | Max [s] | Change |
|--+--+--+--+--|
| =sleep 2= | 2.200 ± 0.100 | 2.100 | 2.300 | 11.00 ± 5.52 |
| =sleep 0.1= | 0.200 ± 0.100 | 0.100 | 0.300 | 1.00 |
| =sleep 2= | 2.200 ± 0.100 | 2.100 | 2.300 | reference |
| =sleep 0.1= | 0.200 ± 0.100 | 0.100 | 0.300 | -90.9% |
"#);
}
@@ -310,41 +318,41 @@ fn test_markup_export_manual_ms() {
},
];
insta::assert_snapshot!(get_output::<MarkdownExporter>(&timing_results, Some(TimeUnit::MilliSecond), SortOrder::Command), @r#"
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
insta::assert_snapshot!(get_output::<MarkdownExporter>(&timing_results, Some(Unit { symbol: "ms", scale: 0.001 })), @r#"
| Command | Mean Time [ms] | Min [ms] | Max [ms] | Change |
|:---|---:|---:|---:|---:|
| `sleep 2` | 2200.0 ± 100.0 | 2100.0 | 2300.0 | 11.00 ± 5.52 |
| `sleep 0.1` | 200.0 ± 100.0 | 100.0 | 300.0 | 1.00 |
| `sleep 2` | 2200.0 ± 100.0 | 2100.0 | 2300.0 | reference |
| `sleep 0.1` | 200.0 ± 100.0 | 100.0 | 300.0 | -90.9% |
"#);
insta::assert_snapshot!(get_output::<AsciidocExporter>(&timing_results, Some(TimeUnit::MilliSecond), SortOrder::Command), @r#"
insta::assert_snapshot!(get_output::<AsciidocExporter>(&timing_results, Some(Unit { symbol: "ms", scale: 0.001 })), @r#"
[cols="<,>,>,>,>"]
|===
| Command
| Mean [ms]
| Min [ms]
| Max [ms]
| Relative
| Command
| Mean Time [ms]
| Min [ms]
| Max [ms]
| Change
| `sleep 2`
| 2200.0 ± 100.0
| 2100.0
| 2300.0
| 11.00 ± 5.52
| `sleep 2`
| 2200.0 ± 100.0
| 2100.0
| 2300.0
| reference
| `sleep 0.1`
| 200.0 ± 100.0
| 100.0
| 300.0
| 1.00
| `sleep 0.1`
| 200.0 ± 100.0
| 100.0
| 300.0
| -90.9%
|===
"#);
insta::assert_snapshot!(get_output::<OrgmodeExporter>(&timing_results, Some(TimeUnit::MilliSecond), SortOrder::Command), @r#"
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
insta::assert_snapshot!(get_output::<OrgmodeExporter>(&timing_results, Some(Unit { symbol: "ms", scale: 0.001 })), @r#"
| Command | Mean Time [ms] | Min [ms] | Max [ms] | Change |
|--+--+--+--+--|
| =sleep 2= | 2200.0 ± 100.0 | 2100.0 | 2300.0 | 11.00 ± 5.52 |
| =sleep 0.1= | 200.0 ± 100.0 | 100.0 | 300.0 | 1.00 |
| =sleep 2= | 2200.0 ± 100.0 | 2100.0 | 2300.0 | reference |
| =sleep 0.1= | 200.0 ± 100.0 | 100.0 | 300.0 | -90.9% |
"#);
}
@@ -418,40 +426,33 @@ fn test_markup_export_manual_s() {
},
];
insta::assert_snapshot!(get_output::<MarkdownExporter>(&results, Some(TimeUnit::Second), SortOrder::Command), @r#"
| Command | Mean [s] | Min [s] | Max [s] | Relative |
insta::assert_snapshot!(get_output::<MarkdownExporter>(&results, Some(Unit { symbol: "s", scale: 1.0 })), @r#"
| Command | Mean Time [s] | Min [s] | Max [s] | Change |
|:---|---:|---:|---:|---:|
| `sleep 2` | 2.020 ± 0.010 | 2.010 | 2.030 | 16.83 ± 1.41 |
| `sleep 0.1` | 0.120 ± 0.010 | 0.110 | 0.130 | 1.00 |
| `sleep 2` | 2.020 ± 0.010 | 2.010 | 2.030 | reference |
| `sleep 0.1` | 0.120 ± 0.010 | 0.110 | 0.130 | -94.1% |
"#);
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.120 ± 0.010 | 0.110 | 0.130 | 1.00 |
| `sleep 2` | 2.020 ± 0.010 | 2.010 | 2.030 | 16.83 ± 1.41 |
"#);
insta::assert_snapshot!(get_output::<AsciidocExporter>(&results, Some(TimeUnit::Second), SortOrder::Command), @r#"
insta::assert_snapshot!(get_output::<AsciidocExporter>(&results, Some(Unit { symbol: "s", scale: 1.0 })), @r#"
[cols="<,>,>,>,>"]
|===
| Command
| Mean [s]
| Min [s]
| Max [s]
| Relative
| Command
| Mean Time [s]
| Min [s]
| Max [s]
| Change
| `sleep 2`
| 2.020 ± 0.010
| 2.010
| 2.030
| 16.83 ± 1.41
| `sleep 2`
| 2.020 ± 0.010
| 2.010
| 2.030
| reference
| `sleep 0.1`
| 0.120 ± 0.010
| 0.110
| 0.130
| 1.00
| `sleep 0.1`
| 0.120 ± 0.010
| 0.110
| 0.130
| -94.1%
|===
"#);
}