mirror of
https://github.com/sharkdp/hyperfine.git
synced 2026-11-01 08:50:19 +00:00
147 lines
4.9 KiB
Rust
147 lines
4.9 KiB
Rust
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 super::Exporter;
|
|
use anyhow::Result;
|
|
use std::cmp::Ordering;
|
|
|
|
pub enum Alignment {
|
|
Left,
|
|
Right,
|
|
}
|
|
|
|
pub trait MarkupExporter {
|
|
fn table_results(
|
|
&self,
|
|
entries: &[BenchmarkResultWithRelativeSpeed],
|
|
time_unit: TimeUnit,
|
|
reference_pending: bool,
|
|
explicit_reference: bool,
|
|
) -> String {
|
|
// prepare table header strings
|
|
let notation = format!("[{}]", time_unit.short_name());
|
|
|
|
// prepare table cells alignment
|
|
let cells_alignment = [
|
|
Alignment::Left,
|
|
Alignment::Right,
|
|
Alignment::Right,
|
|
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!("Min {notation}"),
|
|
&format!("Max {notation}"),
|
|
"Relative",
|
|
]));
|
|
|
|
// 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.command_with_unused_parameters().replace('|', "\\|");
|
|
let mean_str = format_duration_value(result.mean_wall_clock_time(), Some(time_unit)).0;
|
|
let stddev_str = if let Some(stddev) = result.measurements.stddev() {
|
|
format!(" ± {}", format_duration_value(stddev, Some(time_unit)).0)
|
|
} else {
|
|
"".into()
|
|
};
|
|
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,
|
|
]))
|
|
}
|
|
|
|
// 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_header(&self, _cell_aligmnents: &[Alignment]) -> String {
|
|
"".to_string()
|
|
}
|
|
|
|
fn table_footer(&self, _cell_aligmnents: &[Alignment]) -> String {
|
|
"".to_string()
|
|
}
|
|
|
|
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.
|
|
format_duration_value(first_result.mean_wall_clock_time(), None).1
|
|
} else {
|
|
// Default to `Second`.
|
|
TimeUnit::Second
|
|
}
|
|
}
|
|
|
|
impl<T: MarkupExporter> Exporter for T {
|
|
fn serialize(
|
|
&self,
|
|
results: &[BenchmarkResult],
|
|
unit: Option<TimeUnit>,
|
|
sort_order: SortOrder,
|
|
reference_index: Option<usize>,
|
|
) -> Result<Vec<u8>> {
|
|
let unit = 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())
|
|
}
|
|
}
|