diff --git a/src/hyperfine/app.rs b/src/hyperfine/app.rs index 9a759b8..b399d12 100644 --- a/src/hyperfine/app.rs +++ b/src/hyperfine/app.rs @@ -118,6 +118,14 @@ fn build_app() -> App<'static, 'static> { .value_name("FILE") .help("Export the timing results as a Markdown table to the given FILE."), ) + .arg( + Arg::with_name("print-stdout") + .long("print-stdout") + .conflicts_with("style") + .help("Print the stdout of the benchmark instead of suppressing it. \ + This will increase the time it takes for benchmarks to run, \ + so it should only be used when trying to benchmark output speed."), + ) .help_message("Print this help message.") .version_message("Show version information.") } diff --git a/src/hyperfine/benchmark.rs b/src/hyperfine/benchmark.rs index b617d45..c4b89dd 100644 --- a/src/hyperfine/benchmark.rs +++ b/src/hyperfine/benchmark.rs @@ -1,4 +1,5 @@ use std::io; +use std::process::Stdio; use colored::*; use statistical::{mean, standard_deviation}; @@ -38,12 +39,18 @@ fn subtract_shell_spawning_time(time: Second, shell_spawning_time: Second) -> Se /// Run the given shell command and measure the execution time pub fn time_shell_command( command: &Command, + print_stdout: bool, failure_action: CmdFailureAction, shell_spawning_time: Option, ) -> io::Result<(TimingResult, bool)> { let wallclock_timer = WallClockTimer::start(); - let result = execute_and_time(&command.get_shell_command())?; + let stdout = if print_stdout { + Stdio::inherit() + } else { + Stdio::null() + }; + let result = execute_and_time(stdout, &command.get_shell_command())?; let mut time_user = result.user_time; let mut time_system = result.system_time; @@ -76,7 +83,10 @@ pub fn time_shell_command( } /// Measure the average shell spawning time -pub fn mean_shell_spawning_time(style: &OutputStyleOption) -> io::Result { +pub fn mean_shell_spawning_time( + style: &OutputStyleOption, + print_stdout: bool, +) -> io::Result { const COUNT: u64 = 200; let progress_bar = get_progress_bar(COUNT, "Measuring shell spawning time", style); @@ -86,7 +96,12 @@ pub fn mean_shell_spawning_time(style: &OutputStyleOption) -> io::Result { @@ -114,10 +129,14 @@ pub fn mean_shell_spawning_time(style: &OutputStyleOption) -> io::Result) -> io::Result { +fn run_preparation_command( + command: &Option, + print_stdout: bool, +) -> io::Result { if let &Some(ref preparation_command) = command { let res = time_shell_command( &Command::new(preparation_command), + print_stdout, CmdFailureAction::RaiseError, None, ); @@ -164,7 +183,7 @@ pub fn run_benchmark( ); for _ in 0..options.warmup_count { - let _ = time_shell_command(cmd, options.failure_action, None)?; + let _ = time_shell_command(cmd, options.print_stdout, options.failure_action, None)?; progress_bar.inc(1); } progress_bar.finish_and_clear(); @@ -178,11 +197,15 @@ pub fn run_benchmark( ); // Run init / cleanup command - let prepare_res = run_preparation_command(&options.preparation_command)?; + let prepare_res = run_preparation_command(&options.preparation_command, options.print_stdout)?; // Initial timing run - let (res, success) = - time_shell_command(cmd, options.failure_action, Some(shell_spawning_time))?; + let (res, success) = time_shell_command( + cmd, + options.print_stdout, + options.failure_action, + Some(shell_spawning_time), + )?; // Determine number of benchmark runs let runs_in_min_time = (options.min_time_sec @@ -209,7 +232,7 @@ pub fn run_benchmark( // Gather statistics for _ in 0..count_remaining { - run_preparation_command(&options.preparation_command)?; + run_preparation_command(&options.preparation_command, options.print_stdout)?; let msg = { let mean = format_duration(mean(×_real), None); @@ -217,8 +240,12 @@ pub fn run_benchmark( }; progress_bar.set_message(&msg); - let (res, success) = - time_shell_command(cmd, options.failure_action, Some(shell_spawning_time))?; + let (res, success) = time_shell_command( + cmd, + options.print_stdout, + options.failure_action, + Some(shell_spawning_time), + )?; times_real.push(res.time_real); times_user.push(res.time_user); diff --git a/src/hyperfine/shell.rs b/src/hyperfine/shell.rs index ab52b4b..09a8070 100644 --- a/src/hyperfine/shell.rs +++ b/src/hyperfine/shell.rs @@ -19,8 +19,8 @@ pub struct ExecuteResult { /// Execute the given command and return a timing summary #[cfg(windows)] -pub fn execute_and_time(command: &str) -> io::Result { - let mut child = run_shell_command(command)?; +pub fn execute_and_time(stdout: Stdio, command: &str) -> io::Result { + let mut child = run_shell_command(stdout, command)?; let cpu_timer = get_cpu_timer(&child); let status = child.wait()?; @@ -34,10 +34,10 @@ pub fn execute_and_time(command: &str) -> io::Result { /// Execute the given command and return a timing summary #[cfg(not(windows))] -pub fn execute_and_time(command: &str) -> io::Result { +pub fn execute_and_time(stdout: Stdio, command: &str) -> io::Result { let cpu_timer = get_cpu_timer(); - let status = run_shell_command(command)?; + let status = run_shell_command(stdout, command)?; let (user_time, system_time) = cpu_timer.stop(); @@ -50,24 +50,24 @@ pub fn execute_and_time(command: &str) -> io::Result { /// Run a standard shell command #[cfg(not(windows))] -fn run_shell_command(command: &str) -> io::Result { +fn run_shell_command(stdout: Stdio, command: &str) -> io::Result { Command::new("sh") .arg("-c") .arg(command) .stdin(Stdio::null()) - .stdout(Stdio::null()) + .stdout(stdout) .stderr(Stdio::null()) .status() } /// Run a Windows shell command using cmd.exe #[cfg(windows)] -fn run_shell_command(command: &str) -> io::Result { +fn run_shell_command(print_stdout: bool, command: &str) -> io::Result { Command::new("cmd") .arg("/C") .arg(command) .stdin(Stdio::null()) - .stdout(Stdio::null()) + .stdout(stdout) .stderr(Stdio::null()) .spawn() } diff --git a/src/hyperfine/types.rs b/src/hyperfine/types.rs index 74126ff..3032c51 100644 --- a/src/hyperfine/types.rs +++ b/src/hyperfine/types.rs @@ -89,6 +89,9 @@ pub struct HyperfineOptions { /// What color mode to use for output pub output_style: OutputStyleOption, + + /// Forward benchmark's stdout to hyperfine's stdout + pub print_stdout: bool, } impl Default for HyperfineOptions { @@ -100,6 +103,7 @@ impl Default for HyperfineOptions { failure_action: CmdFailureAction::RaiseError, preparation_command: None, output_style: OutputStyleOption::Full, + print_stdout: false, } } } diff --git a/src/main.rs b/src/main.rs index 6e15032..b109fa0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -56,7 +56,8 @@ pub fn error(message: &str) -> ! { /// Runs the benchmark for the given commands fn run(commands: &Vec, options: &HyperfineOptions) -> io::Result> { - let shell_spawning_time = mean_shell_spawning_time(&options.output_style)?; + let shell_spawning_time = + mean_shell_spawning_time(&options.output_style, options.print_stdout)?; let mut timing_results = vec![]; @@ -128,11 +129,13 @@ fn build_hyperfine_options(matches: &ArgMatches) -> HyperfineOptions { options.preparation_command = matches.value_of("prepare").map(String::from); + options.print_stdout = matches.is_present("print-stdout"); + options.output_style = match matches.value_of("style") { Some("full") => OutputStyleOption::Full, Some("basic") => OutputStyleOption::Basic, Some("nocolor") => OutputStyleOption::NoColor, - _ => if atty::is(Stream::Stdout) { + _ => if !options.print_stdout && atty::is(Stream::Stdout) { OutputStyleOption::Full } else { OutputStyleOption::Basic