Added options to specify the max/exact numbers of runs.

Fixes #77.
This commit is contained in:
Diogo Sousa 2018-09-09 14:10:36 +01:00 committed by David Peter
parent fe041df718
commit d78c33b864
5 changed files with 110 additions and 28 deletions

View File

@ -54,6 +54,22 @@ fn build_app() -> App<'static, 'static> {
.value_name("NUM") .value_name("NUM")
.help("Perform at least NUM runs for each command (default: 10)."), .help("Perform at least NUM runs for each command (default: 10)."),
) )
.arg(
Arg::with_name("max-runs")
.long("max-runs")
.short("M")
.takes_value(true)
.value_name("NUM")
.help("Perform at most NUM runs for each command."),
)
.arg(
Arg::with_name("runs")
.long("runs")
.short("r")
.takes_value(true)
.value_name("NUM")
.help("Perform exactly NUM runs for each command."),
)
.arg( .arg(
Arg::with_name("prepare") Arg::with_name("prepare")
.long("prepare") .long("prepare")

View File

@ -1,3 +1,4 @@
use std::cmp;
use std::io; use std::io;
use std::process::Stdio; use std::process::Stdio;
@ -187,7 +188,7 @@ pub fn run_benchmark(
// Set up progress bar (and spinner for initial measurement) // Set up progress bar (and spinner for initial measurement)
let progress_bar = get_progress_bar( let progress_bar = get_progress_bar(
options.min_runs, options.runs.min,
"Initial time measurement", "Initial time measurement",
&options.output_style, &options.output_style,
); );
@ -215,11 +216,10 @@ pub fn run_benchmark(
/ (res.time_real + prepare_res.time_real + shell_spawning_time.time_real)) / (res.time_real + prepare_res.time_real + shell_spawning_time.time_real))
as u64; as u64;
let count = if runs_in_min_time >= options.min_runs { let count = cmp::min(
runs_in_min_time cmp::max(runs_in_min_time, options.runs.min),
} else { options.runs.max
options.min_runs );
};
let count_remaining = count - 1; let count_remaining = count - 1;

View File

@ -9,16 +9,6 @@ pub enum ParameterScanError {
TooLarge, TooLarge,
} }
impl ParameterScanError {
fn __description(&self) -> &str {
match *self {
ParameterScanError::ParseIntError(ref e) => e.description(),
ParameterScanError::EmptyRange => "Empty parameter range",
ParameterScanError::TooLarge => "Parameter range is too large",
}
}
}
impl From<num::ParseIntError> for ParameterScanError { impl From<num::ParseIntError> for ParameterScanError {
fn from(e: num::ParseIntError) -> ParameterScanError { fn from(e: num::ParseIntError) -> ParameterScanError {
ParameterScanError::ParseIntError(e) ParameterScanError::ParseIntError(e)
@ -27,12 +17,37 @@ impl From<num::ParseIntError> for ParameterScanError {
impl fmt::Display for ParameterScanError { impl fmt::Display for ParameterScanError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.__description()) write!(f, "{}", self.description())
} }
} }
impl Error for ParameterScanError { impl Error for ParameterScanError {
fn description(&self) -> &str { fn description(&self) -> &str {
self.__description() match *self {
ParameterScanError::ParseIntError(ref e) => e.description(),
ParameterScanError::EmptyRange => "Empty parameter range",
ParameterScanError::TooLarge => "Parameter range is too large",
}
}
}
#[derive(Debug)]
pub enum OptionsError {
RunsBelowTwo,
EmptyRunsRange,
}
impl fmt::Display for OptionsError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.description())
}
}
impl Error for OptionsError {
fn description(&self) -> &str {
match *self {
OptionsError::EmptyRunsRange => "Empty runs range",
OptionsError::RunsBelowTwo => "Number of runs below two",
}
} }
} }

View File

@ -73,13 +73,31 @@ pub enum OutputStyleOption {
NoColor, NoColor,
} }
/// Number of runs for a benchmark
pub struct Runs {
/// Minimum number of benchmark runs
pub min: u64,
/// Maximum number of benchmark runs
pub max: u64,
}
impl Default for Runs {
fn default() -> Runs {
Runs {
min: 10,
max: ::std::u64::MAX,
}
}
}
/// A set of options for hyperfine /// A set of options for hyperfine
pub struct HyperfineOptions { pub struct HyperfineOptions {
/// Number of warmup runs /// Number of warmup runs
pub warmup_count: u64, pub warmup_count: u64,
/// Minimum number of benchmark runs /// Number of benchmark runs
pub min_runs: u64, pub runs: Runs,
/// Minimum benchmarking time /// Minimum benchmarking time
pub min_time_sec: Second, pub min_time_sec: Second,
@ -101,7 +119,7 @@ impl Default for HyperfineOptions {
fn default() -> HyperfineOptions { fn default() -> HyperfineOptions {
HyperfineOptions { HyperfineOptions {
warmup_count: 0, warmup_count: 0,
min_runs: 10, runs: Runs::default(),
min_time_sec: 3.0, min_time_sec: 3.0,
failure_action: CmdFailureAction::RaiseError, failure_action: CmdFailureAction::RaiseError,
preparation_command: None, preparation_command: None,

View File

@ -42,7 +42,7 @@ mod hyperfine;
use hyperfine::app::get_arg_matches; use hyperfine::app::get_arg_matches;
use hyperfine::benchmark::{mean_shell_spawning_time, run_benchmark}; use hyperfine::benchmark::{mean_shell_spawning_time, run_benchmark};
use hyperfine::error::ParameterScanError; use hyperfine::error::{ParameterScanError, OptionsError};
use hyperfine::export::{ExportManager, ExportType}; use hyperfine::export::{ExportManager, ExportType};
use hyperfine::internal::write_benchmark_comparison; use hyperfine::internal::write_benchmark_comparison;
use hyperfine::types::{ use hyperfine::types::{
@ -95,7 +95,10 @@ fn main() {
let export_manager = build_export_manager(&matches); let export_manager = build_export_manager(&matches);
let commands = build_commands(&matches); let commands = build_commands(&matches);
let res = run(&commands, &options); let res = match options {
Ok(opts) => run(&commands, &opts),
Err(e) => error(e.description()),
};
match res { match res {
Ok(timing_results) => { Ok(timing_results) => {
@ -113,7 +116,7 @@ fn main() {
} }
/// Build the HyperfineOptions that correspond to the given ArgMatches /// Build the HyperfineOptions that correspond to the given ArgMatches
fn build_hyperfine_options(matches: &ArgMatches) -> HyperfineOptions { fn build_hyperfine_options(matches: &ArgMatches) -> Result<HyperfineOptions, OptionsError> {
let mut options = HyperfineOptions::default(); let mut options = HyperfineOptions::default();
let str_to_u64 = |n| u64::from_str_radix(n, 10).ok(); let str_to_u64 = |n| u64::from_str_radix(n, 10).ok();
@ -122,11 +125,41 @@ fn build_hyperfine_options(matches: &ArgMatches) -> HyperfineOptions {
.and_then(&str_to_u64) .and_then(&str_to_u64)
.unwrap_or(0); .unwrap_or(0);
if let Some(min_runs) = matches.value_of("min-runs").and_then(&str_to_u64) { let mut min_runs = matches.value_of("min-runs").and_then(&str_to_u64);
// we need at least two runs to compute a variance let mut max_runs = matches.value_of("max-runs").and_then(&str_to_u64);
options.min_runs = cmp::max(2, min_runs);
if let Some(runs) = matches.value_of("runs").and_then(&str_to_u64) {
min_runs = Some(runs);
max_runs = Some(runs);
} }
match (min_runs, max_runs) {
(Some(min), _) if min < 2 => {
// We need at least two runs to compute a variance.
return Err(OptionsError::RunsBelowTwo);
}
(Some(min), None) => {
options.runs.min = min;
}
(_, Some(max)) if max < 2 => {
// We need at least two runs to compute a variance.
return Err(OptionsError::RunsBelowTwo);
}
(None, Some(max)) => {
// Since the minimum was not explicit we lower it if max is below the default min.
options.runs.min = cmp::min(options.runs.min, max);
options.runs.max = max;
}
(Some(min), Some(max)) if min > max => {
return Err(OptionsError::EmptyRunsRange);
}
(Some(min), Some(max)) => {
options.runs.min = min;
options.runs.max = max;
}
(None, None) => {}
};
options.preparation_command = matches.value_of("prepare").map(String::from); options.preparation_command = matches.value_of("prepare").map(String::from);
options.show_output = matches.is_present("show-output"); options.show_output = matches.is_present("show-output");
@ -155,7 +188,7 @@ fn build_hyperfine_options(matches: &ArgMatches) -> HyperfineOptions {
options.failure_action = CmdFailureAction::Ignore; options.failure_action = CmdFailureAction::Ignore;
} }
options Ok(options)
} }
/// Build the ExportManager that will export the results specified /// Build the ExportManager that will export the results specified