diff --git a/src/hyperfine/benchmark.rs b/src/hyperfine/benchmark.rs index 0ff01fb..1adc7a1 100644 --- a/src/hyperfine/benchmark.rs +++ b/src/hyperfine/benchmark.rs @@ -2,7 +2,7 @@ use std::io; use std::process::{Command, Stdio}; use std::time::Instant; -use ansi_term::Colour::{Cyan, Green, White, Yellow, Purple}; +use ansi_term::Colour::{Cyan, Green, Purple, White, Yellow}; use statistical::{mean, standard_deviation}; use hyperfine::internal::{get_progress_bar, max, min, HyperfineOptions, Second, Warnings, @@ -133,6 +133,14 @@ pub fn run_benchmark( // Set up progress bar (and spinner for initial measurement) let bar = get_progress_bar(options.min_runs, "Initial time measurement"); + // Run setup / cleanup command + let run_setup_command = || { + if let Some(ref setup_command) = options.setup_command { + let _ = time_shell_command(setup_command, options.ignore_failure, None); + } + }; + run_setup_command(); + // Initial timing run let res = time_shell_command(cmd, options.ignore_failure, Some(shell_spawning_time))?; @@ -157,6 +165,8 @@ pub fn run_benchmark( // Gather statistics for _ in 0..count_remaining { + run_setup_command(); + let msg = { let mean = format_duration(mean(&execution_times), Unit::Auto); format!("Current estimate: {}", Green.paint(mean)) diff --git a/src/hyperfine/internal.rs b/src/hyperfine/internal.rs index c592e20..e10da29 100644 --- a/src/hyperfine/internal.rs +++ b/src/hyperfine/internal.rs @@ -21,6 +21,9 @@ pub struct HyperfineOptions { /// Whether or not to ignore non-zero exit codes pub ignore_failure: bool, + + /// Command to run before each benchmark run + pub setup_command: Option, } impl Default for HyperfineOptions { @@ -30,6 +33,7 @@ impl Default for HyperfineOptions { min_runs: 10, min_time_sec: 3.0, ignore_failure: false, + setup_command: None, } } } diff --git a/src/main.rs b/src/main.rs index 3741b0e..5b60c4d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -35,6 +35,9 @@ fn run(commands: &Vec<&str>, options: &HyperfineOptions) -> io::Result<()> { } fn main() { + // Process command line options + let mut options = HyperfineOptions::default(); + let matches = App::new("hyperfine") .version(crate_version!()) .setting(AppSettings::ColoredHelp) @@ -47,13 +50,33 @@ fn main() { .multiple(true) .empty_values(false), ) + .arg( + Arg::with_name("ignore-failure") + .long("ignore-failure") + .short("i") + .help("Ignore non-zero exit codes"), + ) + .arg( + Arg::with_name("setup") + .long("setup") + .short("S") + .takes_value(true) + .value_name("CMD") + .help( + "Execute CMD before each benchmark run. This is useful for \ + clearing disk caches, for example.", + ), + ) .arg( Arg::with_name("warmup") .long("warmup") .short("w") .takes_value(true) .value_name("NUM") - .help("Perform NUM warmup runs before the actual benchmark"), + .help( + "Perform NUM warmup runs before the actual benchmark. This can be used \ + to fill (disk) caches for I/O-heavy programs.", + ), ) .arg( Arg::with_name("min-runs") @@ -61,20 +84,18 @@ fn main() { .short("m") .takes_value(true) .value_name("NUM") - .help("Perform at least NUM runs for each command"), - ) - .arg( - Arg::with_name("ignore-failure") - .long("ignore-failure") - .short("i") - .help("Ignore non-zero exit codes"), + .help(&format!( + "Perform at least NUM runs for each command (default: {}).", + options.min_runs + )), ) .get_matches(); - let str_to_u64 = |n| u64::from_str_radix(n, 10).ok(); + options.ignore_failure = matches.is_present("ignore-failure"); - // Process command line options - let mut options = HyperfineOptions::default(); + options.setup_command = matches.value_of("setup").map(String::from); + + let str_to_u64 = |n| u64::from_str_radix(n, 10).ok(); options.warmup_count = matches .value_of("warmup") @@ -86,8 +107,6 @@ fn main() { options.min_runs = cmp::max(2, min_runs); } - options.ignore_failure = matches.is_present("ignore-failure"); - let commands = matches.values_of("command").unwrap().collect(); let res = run(&commands, &options);