From 72d27729faf82cf58dd8caa456c645a0ce319cf8 Mon Sep 17 00:00:00 2001 From: David Peter Date: Mon, 7 Feb 2022 21:03:23 +0100 Subject: [PATCH] New Scheduler struct --- src/benchmark/mod.rs | 2 +- src/benchmark/schedule.rs | 81 --------------------------------- src/benchmark/scheduler.rs | 92 ++++++++++++++++++++++++++++++++++++++ src/main.rs | 10 ++++- src/options.rs | 16 +++++++ tests/integration_tests.rs | 1 + 6 files changed, 118 insertions(+), 84 deletions(-) delete mode 100644 src/benchmark/schedule.rs create mode 100644 src/benchmark/scheduler.rs diff --git a/src/benchmark/mod.rs b/src/benchmark/mod.rs index a32605e..3379cc3 100644 --- a/src/benchmark/mod.rs +++ b/src/benchmark/mod.rs @@ -1,6 +1,6 @@ pub mod relative_speed; pub mod result; -pub mod schedule; +pub mod scheduler; use std::cmp; use std::process::{ExitStatus, Stdio}; diff --git a/src/benchmark/schedule.rs b/src/benchmark/schedule.rs deleted file mode 100644 index 2040dad..0000000 --- a/src/benchmark/schedule.rs +++ /dev/null @@ -1,81 +0,0 @@ -use crate::command::Commands; -use crate::export::ExportManager; -use crate::options::{Options, OutputStyleOption}; - -use super::{mean_shell_spawning_time, relative_speed, result::BenchmarkResult, run_benchmark}; - -use anyhow::{bail, Result}; -use colored::*; - -pub fn write_benchmark_comparison(results: &[BenchmarkResult]) { - if results.len() < 2 { - return; - } - - if let Some(mut annotated_results) = relative_speed::compute(results) { - annotated_results.sort_by(|l, r| relative_speed::compare_mean_time(l.result, r.result)); - - let fastest = &annotated_results[0]; - let others = &annotated_results[1..]; - - println!("{}", "Summary".bold()); - println!(" '{}' ran", fastest.result.command.cyan()); - - for item in others { - println!( - "{}{} times faster than '{}'", - format!("{:8.2}", item.relative_speed).bold().green(), - if let Some(stddev) = item.relative_speed_stddev { - format!(" ± {}", format!("{:.2}", stddev).green()) - } else { - "".into() - }, - &item.result.command.magenta() - ); - } - } else { - eprintln!( - "{}: The benchmark comparison could not be computed as some benchmark times are zero. \ - This could be caused by background interference during the initial calibration phase \ - of hyperfine, in combination with very fast commands (faster than a few milliseconds). \ - Try to re-run the benchmark on a quiet system. If it does not help, you command is \ - most likely too fast to be accurately benchmarked by hyperfine.", - "Note".bold().red() - ); - } -} - -pub fn run_benchmarks_and_print_comparison( - commands: &Commands, - options: &Options, - export_manager: &ExportManager, -) -> Result<()> { - let shell_spawning_time = - mean_shell_spawning_time(&options.shell, options.output_style, options.show_output)?; - - let mut timing_results = vec![]; - - if let Some(preparation_command) = &options.preparation_command { - if preparation_command.len() > 1 && commands.len() != preparation_command.len() { - bail!( - "The '--prepare' option has to be provided just once or N times, where N is the \ - number of benchmark commands." - ); - } - } - - // Run the benchmarks - for (num, cmd) in commands.iter().enumerate() { - timing_results.push(run_benchmark(num, cmd, shell_spawning_time, options)?); - - // Export (intermediate) results - export_manager.write_results(&timing_results, options.time_unit)?; - } - - // Print relative speed comparison - if options.output_style != OutputStyleOption::Disabled { - write_benchmark_comparison(&timing_results); - } - - Ok(()) -} diff --git a/src/benchmark/scheduler.rs b/src/benchmark/scheduler.rs new file mode 100644 index 0000000..eeda551 --- /dev/null +++ b/src/benchmark/scheduler.rs @@ -0,0 +1,92 @@ +use crate::command::Commands; +use crate::export::ExportManager; +use crate::options::{Options, OutputStyleOption}; + +use super::{mean_shell_spawning_time, relative_speed, result::BenchmarkResult, run_benchmark}; + +use anyhow::Result; +use colored::*; + +pub struct Scheduler<'a> { + commands: &'a Commands<'a>, + options: &'a Options, + export_manager: &'a ExportManager, + results: Vec, +} + +impl<'a> Scheduler<'a> { + pub fn new( + commands: &'a Commands, + options: &'a Options, + export_manager: &'a ExportManager, + ) -> Self { + Self { + commands, + options, + export_manager, + results: vec![], + } + } + + pub fn run_benchmarks(&mut self) -> Result<()> { + let shell_spawning_time = mean_shell_spawning_time( + &self.options.shell, + self.options.output_style, + self.options.show_output, + )?; + + // Run the benchmarks + for (num, cmd) in self.commands.iter().enumerate() { + self.results + .push(run_benchmark(num, cmd, shell_spawning_time, self.options)?); + + // Export (intermediate) results + self.export_manager + .write_results(&self.results, self.options.time_unit)?; + } + + Ok(()) + } + + pub fn print_relative_speed_comparison(&self) { + if self.options.output_style == OutputStyleOption::Disabled { + return; + } + + if self.results.len() < 2 { + return; + } + + if let Some(mut annotated_results) = relative_speed::compute(&self.results) { + annotated_results.sort_by(|l, r| relative_speed::compare_mean_time(l.result, r.result)); + + let fastest = &annotated_results[0]; + let others = &annotated_results[1..]; + + println!("{}", "Summary".bold()); + println!(" '{}' ran", fastest.result.command.cyan()); + + for item in others { + println!( + "{}{} times faster than '{}'", + format!("{:8.2}", item.relative_speed).bold().green(), + if let Some(stddev) = item.relative_speed_stddev { + format!(" ± {}", format!("{:.2}", stddev).green()) + } else { + "".into() + }, + &item.result.command.magenta() + ); + } + } else { + eprintln!( + "{}: The benchmark comparison could not be computed as some benchmark times are zero. \ + This could be caused by background interference during the initial calibration phase \ + of hyperfine, in combination with very fast commands (faster than a few milliseconds). \ + Try to re-run the benchmark on a quiet system. If it does not help, you command is \ + most likely too fast to be accurately benchmarked by hyperfine.", + "Note".bold().red() + ); + } + } +} diff --git a/src/main.rs b/src/main.rs index 9bbf636..319fab3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,7 @@ use std::env; use app::get_cli_arguments; -use benchmark::schedule::run_benchmarks_and_print_comparison; +use benchmark::scheduler::Scheduler; use command::Commands; use export::ExportManager; use options::Options; @@ -32,7 +32,13 @@ fn run() -> Result<()> { let commands = Commands::from_cli_arguments(&cli_arguments)?; let export_manager = ExportManager::from_cli_arguments(&cli_arguments)?; - run_benchmarks_and_print_comparison(&commands, &options, &export_manager) + options.validate_against_command_list(&commands)?; + + let mut scheduler = Scheduler::new(&commands, &options, &export_manager); + scheduler.run_benchmarks()?; + scheduler.print_relative_speed_comparison(); + + Ok(()) } fn main() { diff --git a/src/options.rs b/src/options.rs index 491308a..489470f 100644 --- a/src/options.rs +++ b/src/options.rs @@ -1,12 +1,16 @@ use std::process::Command; use std::{cmp, fmt}; +use anyhow::ensure; use atty::Stream; use clap::ArgMatches; +use crate::command::Commands; use crate::error::OptionsError; use crate::util::units::{Second, Unit}; +use anyhow::Result; + #[cfg(not(windows))] pub const DEFAULT_SHELL: &str = "sh"; @@ -256,6 +260,18 @@ impl Options { Ok(options) } + + pub fn validate_against_command_list(&self, commands: &Commands) -> Result<()> { + if let Some(preparation_command) = &self.preparation_command { + ensure!( + preparation_command.len() <= 1 || commands.len() == preparation_command.len(), + "The '--prepare' option has to be provided just once or N times, where N is the \ + number of benchmark commands." + ); + } + + Ok(()) + } } #[test] diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index f0418ca..de16998 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -44,6 +44,7 @@ fn fails_with_wrong_number_of_prepare_options() { .success(); hyperfine() + .arg("--runs=1") .arg("--prepare=echo a") .arg("--prepare=echo b") .arg("echo a")