diff --git a/src/benchmark/benchmark_result.rs b/src/benchmark/benchmark_result.rs index b37f725..10ff0a8 100644 --- a/src/benchmark/benchmark_result.rs +++ b/src/benchmark/benchmark_result.rs @@ -1,20 +1,20 @@ use std::collections::BTreeMap; -use serde::Serialize; +use serde::{Deserialize, Serialize}; use crate::util::units::Second; /// 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)] +#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)] pub struct BenchmarkResult { /// The full command line of the program that is being benchmarked pub command: String, /// 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. - #[serde(skip_serializing)] + #[serde(skip)] pub command_with_unused_parameters: String, /// The average run time @@ -46,6 +46,6 @@ pub struct BenchmarkResult { pub exit_codes: Vec>, /// Parameter values for this benchmark - #[serde(skip_serializing_if = "BTreeMap::is_empty")] + #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] pub parameters: BTreeMap, } diff --git a/src/benchmark/scheduler.rs b/src/benchmark/scheduler.rs index 941fa5a..bce7cd2 100644 --- a/src/benchmark/scheduler.rs +++ b/src/benchmark/scheduler.rs @@ -22,12 +22,13 @@ impl<'a> Scheduler<'a> { commands: &'a Commands, options: &'a Options, export_manager: &'a ExportManager, + results: &'a Vec, ) -> Self { Self { commands, options, export_manager, - results: vec![], + results: results.to_vec(), } } @@ -69,6 +70,14 @@ impl<'a> Scheduler<'a> { &self.results, self.options.sort_order_speed_comparison, ) { + fn get_command_from_result<'b>(result: &'b BenchmarkResult) -> &'b str { + if !result.command_with_unused_parameters.is_empty() { + &result.command_with_unused_parameters + } else { + &result.command + } + } + match self.options.sort_order_speed_comparison { SortOrder::MeanTime => { println!("{}", "Summary".bold()); @@ -78,7 +87,7 @@ impl<'a> Scheduler<'a> { println!( " {} ran", - fastest.result.command_with_unused_parameters.cyan() + (get_command_from_result(&fastest.result)).cyan() ); for item in others { @@ -90,7 +99,8 @@ impl<'a> Scheduler<'a> { } else { "".into() }, - &item.result.command_with_unused_parameters.magenta() + // &item.result.command_with_unused_parameters.magenta() + &(get_command_from_result(&item.result)).magenta() ); } } @@ -108,7 +118,7 @@ impl<'a> Scheduler<'a> { } else { " ".into() }, - &item.result.command_with_unused_parameters, + &(get_command_from_result(item.result)), ); } } diff --git a/src/cli.rs b/src/cli.rs index cc924cb..77e77e5 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -283,6 +283,13 @@ fn build_command() -> Command { .help("Export the timing summary statistics and timings of individual runs as JSON to the given FILE. \ The output time unit is always seconds"), ) + .arg( + Arg::new("import-json") + .long("import-json") + .action(ArgAction::Set) + .value_name("FILE") + .help("Import the timing summary statistics and timings of individual runs from a JSON FILE.") + ) .arg( Arg::new("export-markdown") .long("export-markdown") diff --git a/src/export/json.rs b/src/export/json.rs index d2542c1..27c7d83 100644 --- a/src/export/json.rs +++ b/src/export/json.rs @@ -8,9 +8,9 @@ use crate::util::units::Unit; use anyhow::Result; -#[derive(Serialize, Debug)] -struct HyperfineSummary<'a> { - results: &'a [BenchmarkResult], +#[derive(Serialize, Deserialize, Debug)] +pub struct HyperfineSummary { + pub results: Vec, } #[derive(Default)] @@ -23,7 +23,9 @@ impl Exporter for JsonExporter { _unit: Option, _sort_order: SortOrder, ) -> Result> { - let mut output = to_vec_pretty(&HyperfineSummary { results }); + let mut output = to_vec_pretty(&HyperfineSummary { + results: results.to_vec(), + }); if let Ok(ref mut content) = output { content.push(b'\n'); } diff --git a/src/export/mod.rs b/src/export/mod.rs index 4fdab0c..c00ab09 100644 --- a/src/export/mod.rs +++ b/src/export/mod.rs @@ -3,7 +3,7 @@ use std::io::Write; mod asciidoc; mod csv; -mod json; +pub mod json; mod markdown; mod markup; mod orgmode; diff --git a/src/import.rs b/src/import.rs new file mode 100644 index 0000000..34e50c4 --- /dev/null +++ b/src/import.rs @@ -0,0 +1,31 @@ +use crate::{benchmark::benchmark_result::BenchmarkResult, export::json::HyperfineSummary}; +use clap::ArgMatches; +use std::fs; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Importer {} + +impl Importer { + pub fn from_cli_arguments(matches: &ArgMatches) -> Option> { + match matches.get_one::("import-json") { + Some(file_name) => read_summary_from_file(file_name), + None => None, + } + } +} + +fn read_summary_from_file(file_name: &str) -> Option> { + let file_content = match fs::read_to_string(file_name) { + Ok(content) => content, + Err(_) => { + eprintln!("Unable to load previous run from file {}", file_name); + return None; + } + }; + + let hyperfine_summary = serde_json::from_str::(&file_content); + match hyperfine_summary { + Ok(summary) => Some(summary.results), + Err(_) => None, + } +} diff --git a/src/main.rs b/src/main.rs index a75d4bd..b5f259a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,6 +9,7 @@ use benchmark::scheduler::Scheduler; use cli::get_cli_arguments; use command::Commands; use export::ExportManager; +use import::Importer; use options::Options; use anyhow::Result; @@ -19,6 +20,7 @@ pub mod cli; pub mod command; pub mod error; pub mod export; +pub mod import; pub mod options; pub mod outlier_detection; pub mod output; @@ -32,13 +34,15 @@ fn run() -> Result<()> { colored::control::set_virtual_terminal(true).unwrap(); let cli_arguments = get_cli_arguments(env::args_os()); + let previous_results = Importer::from_cli_arguments(&cli_arguments).unwrap_or(Vec::new()); + let options = Options::from_cli_arguments(&cli_arguments)?; let commands = Commands::from_cli_arguments(&cli_arguments)?; let export_manager = ExportManager::from_cli_arguments(&cli_arguments, options.time_unit)?; options.validate_against_command_list(&commands)?; - let mut scheduler = Scheduler::new(&commands, &options, &export_manager); + let mut scheduler = Scheduler::new(&commands, &options, &export_manager, &previous_results); scheduler.run_benchmarks()?; scheduler.print_relative_speed_comparison(); scheduler.final_export()?;