mirror of
https://github.com/sharkdp/hyperfine.git
synced 2026-11-01 08:50:19 +00:00
Add support for showing other metrics in the terminal
This commit is contained in:
1 parent
7395dde05d
commit
908b33cc1d
31 files changed
+1514
-1898
No files matched your search
+75
-53
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user