forked from sharkdp/hyperfine
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Change markdown export to use seconds, closes sharkdp#71
The markdown results exporter was using milliseconds, but the other results exporters are using the default seconds unit. Apply some refactoring so the same logic used to select units for the CLI output can be used to select the units for the markdown exporter. Now the units of the markdown exporter match that of the CLI. When there are multiple benchmarks, the CLI will choose the appropriate unit for each benchmark separately. In this case, the markdown exporter will use the first benchmark to select the units for all the results. *This will change the markdown results for all users from milliseconds to seconds, a UX breaking change.* Add unit tests for the markdown exporter to verify the output. Issue sharkdp#80 proposes a new option to choose the units used for both the CLI report and results export.
- Loading branch information
1 parent
de74c21
commit e912e0f
Showing
2 changed files
with
207 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,39 +1,157 @@ | ||
use super::Exporter; | ||
|
||
use hyperfine::format::{Unit, format_duration_value}; | ||
use hyperfine::types::BenchmarkResult; | ||
|
||
use std::io::Result; | ||
|
||
const MULTIPLIER: f64 = 1e3; | ||
macro_rules! TABLE_HEADER { | ||
($unit:expr) => { | ||
format!("| Command | Mean [{unit}] | Min…Max [{unit}] |\n|:---|---:|---:|\n", unit=$unit) }; | ||
} | ||
|
||
#[derive(Default)] | ||
pub struct MarkdownExporter {} | ||
|
||
impl Exporter for MarkdownExporter { | ||
fn serialize(&self, results: &Vec<BenchmarkResult>) -> Result<Vec<u8>> { | ||
let mut destination = start_table(); | ||
// Default to `Second`. | ||
let mut unit = Unit::Second; | ||
|
||
if !results.is_empty() { | ||
// Use the first BenchmarkResult entry to determine the unit for all entries. | ||
unit = format_duration_value(results[0].mean, None).1; | ||
} | ||
|
||
let mut destination = start_table(unit); | ||
|
||
for result in results { | ||
add_table_row(&mut destination, result); | ||
add_table_row(&mut destination, result, unit); | ||
} | ||
|
||
Ok(destination) | ||
} | ||
} | ||
|
||
fn start_table() -> Vec<u8> { | ||
"| Command | Mean [ms] | Min…Max [ms] |\n|:---|---:|---:|\n" | ||
.bytes() | ||
.collect() | ||
fn start_table(unit: Unit) -> Vec<u8> { | ||
TABLE_HEADER!(unit.short_name()).bytes().collect() | ||
} | ||
fn add_table_row(dest: &mut Vec<u8>, entry: &BenchmarkResult) { | ||
|
||
fn add_table_row(dest: &mut Vec<u8>, entry: &BenchmarkResult, unit: Unit) { | ||
let mean_str = format_duration_value(entry.mean, Some(unit)).0; | ||
let stddev_str = format_duration_value(entry.stddev, Some(unit)).0; | ||
let min_str = format_duration_value(entry.min, Some(unit)).0; | ||
let max_str = format_duration_value(entry.max, Some(unit)).0; | ||
|
||
dest.extend( | ||
format!( | ||
"| `{}` | {:.1} ± {:.1} | {:.1}…{:.1} |\n", | ||
entry.command.replace("|", "\\|"), | ||
entry.mean * MULTIPLIER, | ||
entry.stddev * MULTIPLIER, | ||
entry.min * MULTIPLIER, | ||
entry.max * MULTIPLIER | ||
"| `{command}` | {mean} ± {stddev} | {min}…{max} |\n", | ||
command=entry.command.replace("|", "\\|"), | ||
mean=mean_str, | ||
stddev=stddev_str, | ||
min=min_str, | ||
max=max_str, | ||
).as_bytes(), | ||
); | ||
} | ||
|
||
/// Ensure the markdown output includes the table header and the multiple | ||
/// benchmark results as a table. The list of actual times is not included | ||
/// in the output. | ||
/// | ||
/// This also demonstrates that the first entry's units (ms) are used to set | ||
/// the units for all entries. | ||
#[test] | ||
fn test_markdown_format_ms() { | ||
let exporter = MarkdownExporter::default(); | ||
|
||
let mut timing_results = vec![]; | ||
|
||
timing_results.push(BenchmarkResult::new( | ||
String::from("sleep 0.1"), | ||
0.1057, // mean | ||
0.0016, // std dev | ||
0.0009, // user_mean | ||
0.0011, // system_mean | ||
0.1023, // min | ||
0.1080, // max | ||
vec![0.1, 0.1, 0.1], // times | ||
)); | ||
|
||
timing_results.push(BenchmarkResult::new( | ||
String::from("sleep 2"), | ||
2.0050, // mean | ||
0.0020, // std dev | ||
0.0009, // user_mean | ||
0.0012, // system_mean | ||
2.0020, // min | ||
2.0080, // max | ||
vec![2.0, 2.0, 2.0], // times | ||
)); | ||
|
||
let formatted = String::from_utf8(exporter.serialize(&timing_results).unwrap()).unwrap(); | ||
|
||
let formatted_expected = format!( | ||
"{}\ | ||
| `sleep 0.1` | 105.7 ± 1.6 | 102.3…108.0 | | ||
| `sleep 2` | 2005.0 ± 2.0 | 2002.0…2008.0 | | ||
", TABLE_HEADER!("ms")); | ||
|
||
assert_eq!(formatted_expected, formatted); | ||
} | ||
|
||
/// This (again) demonstrates that the first entry's units (s) are used to set | ||
/// the units for all entries. | ||
#[test] | ||
fn test_markdown_format_s() { | ||
let exporter = MarkdownExporter::default(); | ||
|
||
let mut timing_results = vec![]; | ||
|
||
timing_results.push(BenchmarkResult::new( | ||
String::from("sleep 2"), | ||
2.0050, // mean | ||
0.0020, // std dev | ||
0.0009, // user_mean | ||
0.0012, // system_mean | ||
2.0020, // min | ||
2.0080, // max | ||
vec![2.0, 2.0, 2.0], // times | ||
)); | ||
|
||
timing_results.push(BenchmarkResult::new( | ||
String::from("sleep 0.1"), | ||
0.1057, // mean | ||
0.0016, // std dev | ||
0.0009, // user_mean | ||
0.0011, // system_mean | ||
0.1023, // min | ||
0.1080, // max | ||
vec![0.1, 0.1, 0.1], // times | ||
)); | ||
|
||
let formatted = String::from_utf8(exporter.serialize(&timing_results).unwrap()).unwrap(); | ||
|
||
let formatted_expected = format!( | ||
"{}\ | ||
| `sleep 2` | 2.005 ± 0.002 | 2.002…2.008 | | ||
| `sleep 0.1` | 0.106 ± 0.002 | 0.102…0.108 | | ||
", TABLE_HEADER!("s")); | ||
|
||
assert_eq!(formatted_expected, formatted); | ||
} | ||
|
||
/// An empty list of benchmark results will only include the table header | ||
/// in the markdown output, using the default `Seconds` unit. | ||
#[test] | ||
fn test_markdown_format_empty_results() { | ||
let exporter = MarkdownExporter::default(); | ||
|
||
let timing_results = vec![]; | ||
|
||
let formatted = String::from_utf8(exporter.serialize(&timing_results).unwrap()).unwrap(); | ||
|
||
let formatted_expected = TABLE_HEADER!("s"); | ||
|
||
assert_eq!(formatted_expected, formatted); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters