From af4422d2126f6b97fa687bec2d3970aa29d65330 Mon Sep 17 00:00:00 2001 From: David Peter Date: Mon, 5 Oct 2026 14:14:38 +0200 Subject: [PATCH] Remove command_with_unused_parameters field --- src/benchmark/benchmark_result.rs | 100 +++++++++--------------------- src/benchmark/mod.rs | 5 +- src/benchmark/relative_speed.rs | 1 - src/benchmark/scheduler.rs | 10 +-- src/export/csv.rs | 4 +- src/export/markup.rs | 2 +- src/export/tests.rs | 8 --- 7 files changed, 38 insertions(+), 92 deletions(-) diff --git a/src/benchmark/benchmark_result.rs b/src/benchmark/benchmark_result.rs index 5d0e638..9210501 100644 --- a/src/benchmark/benchmark_result.rs +++ b/src/benchmark/benchmark_result.rs @@ -26,11 +26,15 @@ pub struct BenchmarkRun { pub exit_code: Option, } -use crate::benchmark::measurement::Measurements; -use crate::quantity::{byte, second, Time}; -use crate::util::exit_code::extract_exit_code; +#[derive(Debug, Default, Clone, Serialize, PartialEq)] +pub struct Parameter { + pub value: String, + pub is_unused: bool, +} -/// Parameter value and whether it was used in the command line template +/// Set of values that will be exported. +// NOTE: `serde` is used for JSON serialization, but not for CSV serialization due to the +// `parameters` map. Update `src/hyperfine/export/csv.rs` with new fields, as appropriate. #[derive(Debug, Default, Clone, Serialize, PartialEq)] #[serde(transparent)] pub struct Parameter { @@ -45,80 +49,14 @@ pub struct BenchmarkResult { /// The full command line of the program that is being benchmarked pub command: String, - /// The full command line, including parameters not used in the command template. - pub command_with_unused_parameters: String, - /// All run time measurements pub runs: Vec, /// Parameter values for this benchmark + #[serde(skip_serializing_if = "BTreeMap::is_empty")] pub parameters: BTreeMap, } -impl BenchmarkResult { - /// The average wall clock time - pub fn mean_wall_clock_time(&self) -> Time { - self.measurements.time_wall_clock_mean() - } -} - -// Preserve the existing export format while storing typed measurements internally. -impl Serialize for BenchmarkResult { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let mut state = serializer.serialize_struct( - "BenchmarkResult", - if self.parameters.is_empty() { 11 } else { 12 }, - )?; - state.serialize_field("command", &self.command)?; - state.serialize_field("mean", &self.mean_wall_clock_time().get::())?; - state.serialize_field( - "stddev", - &self.measurements.stddev().map(|time| time.get::()), - )?; - state.serialize_field("median", &self.measurements.median().get::())?; - state.serialize_field("user", &self.measurements.time_user_mean().get::())?; - state.serialize_field( - "system", - &self.measurements.time_system_mean().get::(), - )?; - state.serialize_field("min", &self.measurements.min().get::())?; - state.serialize_field("max", &self.measurements.max().get::())?; - state.serialize_field( - "times", - &self - .measurements - .wall_clock_times() - .map(|time| time.get::()) - .collect::>(), - )?; - state.serialize_field( - "memory_usage_byte", - &self - .measurements - .measurements - .iter() - .map(|measurement| measurement.peak_memory_usage.get::() as u64) - .collect::>(), - )?; - state.serialize_field( - "exit_codes", - &self - .measurements - .measurements - .iter() - .map(|measurement| extract_exit_code(measurement.exit_status)) - .collect::>(), - )?; - if !self.parameters.is_empty() { - state.serialize_field("parameters", &self.parameters)?; - } - state.end() - } -} - impl BenchmarkResult { fn wall_clock_times(&self) -> Vec { self.runs.iter().map(|run| run.wall_clock_time).collect() @@ -175,4 +113,24 @@ impl BenchmarkResult { .collect::>(), ) } + + /// The full command line of the program that is being benchmarked, possibly including a list of + /// parameters that were not used in the command line template. + pub fn command_with_unused_parameters(&self) -> String { + let parameters = self + .parameters + .iter() + .filter(|(_, parameter)| parameter.is_unused) + .fold(String::new(), |output, (name, parameter)| { + output + &format!("{name} = {value}, ", value = parameter.value) + }); + let parameters = parameters.trim_end_matches(", "); + let parameters = if parameters.is_empty() { + "".into() + } else { + format!(" ({parameters})") + }; + + format!("{}{}", self.command, parameters) + } } diff --git a/src/benchmark/mod.rs b/src/benchmark/mod.rs index f4cf90a..db9a550 100644 --- a/src/benchmark/mod.rs +++ b/src/benchmark/mod.rs @@ -8,7 +8,7 @@ use std::cmp; use std::io::{self, Write}; use std::time::Instant; -use crate::benchmark::benchmark_result::BenchmarkRun; +use crate::benchmark::benchmark_result::{BenchmarkRun, Parameter}; use crate::benchmark::executor::BenchmarkIteration; use crate::command::Command; use crate::options::{ @@ -489,7 +489,6 @@ impl<'a> Benchmark<'a> { Ok(BenchmarkResult { command: self.command.get_name(), - command_with_unused_parameters: self.command.get_name_with_unused_parameters(), runs: times_real .iter() .zip(times_user.iter()) @@ -520,7 +519,7 @@ impl<'a> Benchmark<'a> { name.to_string(), Parameter { value: value.to_string(), - is_unused: self.command.is_parameter_unused(name), + is_unused: self.command.is_parameter_unused(&name), }, ) }) diff --git a/src/benchmark/relative_speed.rs b/src/benchmark/relative_speed.rs index ac8b4e6..71d63da 100644 --- a/src/benchmark/relative_speed.rs +++ b/src/benchmark/relative_speed.rs @@ -151,7 +151,6 @@ fn create_result(name: &str, mean: f64) -> BenchmarkResult { BenchmarkResult { command: name.into(), - command_with_unused_parameters: name.into(), runs: vec![BenchmarkRun { wall_clock_time: mean, user_time: mean, diff --git a/src/benchmark/scheduler.rs b/src/benchmark/scheduler.rs index 2944c22..e431007 100644 --- a/src/benchmark/scheduler.rs +++ b/src/benchmark/scheduler.rs @@ -92,8 +92,8 @@ impl<'a> Scheduler<'a> { console_writeln!( stdout, " {} ran", - reference.result.command_with_unused_parameters.cyan() - )?; + reference.result.command_with_unused_parameters().cyan() + ); for item in others { let stddev = if let Some(stddev) = item.relative_speed_stddev { @@ -122,8 +122,8 @@ impl<'a> Scheduler<'a> { stdout, "{} {}", comparator, - item.result.command_with_unused_parameters.magenta() - )?; + &item.result.command_with_unused_parameters().magenta() + ); } } SortOrder::Command => { @@ -146,7 +146,7 @@ impl<'a> Scheduler<'a> { } else { " ".into() }, - item.result.command_with_unused_parameters, + item.result.command_with_unused_parameters(), relationship, )?; } diff --git a/src/export/csv.rs b/src/export/csv.rs index ce54e3a..fcd782d 100644 --- a/src/export/csv.rs +++ b/src/export/csv.rs @@ -68,7 +68,7 @@ impl Exporter for CsvExporter { } #[cfg(test)] -use crate::benchmark::benchmark_result::BenchmarkRun; +use crate::benchmark::benchmark_result::{BenchmarkRun, Parameter}; #[test] fn test_csv() { @@ -84,7 +84,6 @@ fn test_csv() { let results = vec![ BenchmarkResult { command: String::from("command_a"), - command_with_unused_parameters: String::from("command_a"), runs: vec![ BenchmarkRun { wall_clock_time: 7.0, @@ -129,7 +128,6 @@ fn test_csv() { }, BenchmarkResult { command: String::from("command_b"), - command_with_unused_parameters: String::from("command_b"), runs: vec![ BenchmarkRun { wall_clock_time: 17.0, diff --git a/src/export/markup.rs b/src/export/markup.rs index aac3795..e201a2e 100644 --- a/src/export/markup.rs +++ b/src/export/markup.rs @@ -51,7 +51,7 @@ pub trait MarkupExporter { let measurement = &entry.result; // prepare data row strings let cmd_str = measurement - .command_with_unused_parameters + .command_with_unused_parameters() .replace('|', "\\|"); let mean_str = format_duration_value(measurement.mean(), Some(unit)).0; let stddev_str = if let Some(stddev) = measurement.stddev() { diff --git a/src/export/tests.rs b/src/export/tests.rs index 1366e50..2c303d2 100644 --- a/src/export/tests.rs +++ b/src/export/tests.rs @@ -27,7 +27,6 @@ fn test_markup_export_auto_ms() { let results = [ BenchmarkResult { command: String::from("sleep 0.1"), - command_with_unused_parameters: String::from("sleep 0.1"), runs: vec![ BenchmarkRun { wall_clock_time: 0.09, @@ -55,7 +54,6 @@ fn test_markup_export_auto_ms() { }, BenchmarkResult { command: String::from("sleep 2"), - command_with_unused_parameters: String::from("sleep 2"), runs: vec![ BenchmarkRun { wall_clock_time: 2.0, @@ -128,7 +126,6 @@ fn test_markup_export_auto_s() { let results = [ BenchmarkResult { command: String::from("sleep 2"), - command_with_unused_parameters: String::from("sleep 2"), runs: vec![ BenchmarkRun { wall_clock_time: 2.1, @@ -156,7 +153,6 @@ fn test_markup_export_auto_s() { }, BenchmarkResult { command: String::from("sleep 0.1"), - command_with_unused_parameters: String::from("sleep 0.1"), runs: vec![ BenchmarkRun { wall_clock_time: 0.1, @@ -229,7 +225,6 @@ fn test_markup_export_manual_ms() { let timing_results = [ BenchmarkResult { command: String::from("sleep 2"), - command_with_unused_parameters: String::from("sleep 2"), runs: vec![ BenchmarkRun { wall_clock_time: 2.1, @@ -257,7 +252,6 @@ fn test_markup_export_manual_ms() { }, BenchmarkResult { command: String::from("sleep 0.1"), - command_with_unused_parameters: String::from("sleep 0.1"), runs: vec![ BenchmarkRun { wall_clock_time: 0.1, @@ -329,7 +323,6 @@ fn test_markup_export_manual_s() { let results = [ BenchmarkResult { command: String::from("sleep 2"), - command_with_unused_parameters: String::from("sleep 2"), runs: vec![ BenchmarkRun { wall_clock_time: 2.01, @@ -357,7 +350,6 @@ fn test_markup_export_manual_s() { }, BenchmarkResult { command: String::from("sleep 0.1"), - command_with_unused_parameters: String::from("sleep 0.1"), runs: vec![ BenchmarkRun { wall_clock_time: 0.11,