mirror of
https://github.com/sharkdp/hyperfine.git
synced 2024-09-19 02:08:07 +03:00
Makes --comand-name
take arguments from --parameter-list
.
This commit is contained in:
parent
a0445321f5
commit
538dd06c16
@ -117,7 +117,7 @@ pub fn mean_shell_spawning_time(
|
||||
// Just run the shell without any command
|
||||
let res = time_shell_command(
|
||||
shell,
|
||||
&Command::new(""),
|
||||
&Command::new(None, ""),
|
||||
show_output,
|
||||
CmdFailureAction::RaiseError,
|
||||
None,
|
||||
@ -229,20 +229,13 @@ pub fn run_benchmark(
|
||||
shell_spawning_time: TimingResult,
|
||||
options: &HyperfineOptions,
|
||||
) -> io::Result<BenchmarkResult> {
|
||||
let shell_cmd = cmd.get_shell_command();
|
||||
let command_name = if let Some(names) = &options.names {
|
||||
names.get(num).unwrap_or(&shell_cmd)
|
||||
} else {
|
||||
&shell_cmd
|
||||
};
|
||||
let command_name = command_name.to_string();
|
||||
|
||||
let command_name = cmd.get_name();
|
||||
if options.output_style != OutputStyleOption::Disabled {
|
||||
println!(
|
||||
"{}{}: {}",
|
||||
"Benchmark ".bold(),
|
||||
(num + 1).to_string().bold(),
|
||||
&command_name
|
||||
command_name,
|
||||
);
|
||||
}
|
||||
|
||||
@ -259,7 +252,7 @@ pub fn run_benchmark(
|
||||
} else {
|
||||
&values[num]
|
||||
};
|
||||
Command::new_parametrized(preparation_command, cmd.get_parameters().clone())
|
||||
Command::new_parametrized(None, preparation_command, cmd.get_parameters().clone())
|
||||
});
|
||||
|
||||
// Warmup phase
|
||||
@ -450,7 +443,7 @@ pub fn run_benchmark(
|
||||
|
||||
// Run cleanup command
|
||||
let cleanup_cmd = options.cleanup_command.as_ref().map(|cleanup_command| {
|
||||
Command::new_parametrized(cleanup_command, cmd.get_parameters().clone())
|
||||
Command::new_parametrized(None, cleanup_command, cmd.get_parameters().clone())
|
||||
});
|
||||
run_cleanup_command(&options.shell, &cleanup_cmd, options.show_output)?;
|
||||
|
||||
|
@ -83,6 +83,7 @@ fn build_parameterized_commands<'a, T: Numeric>(
|
||||
param_min: T,
|
||||
param_max: T,
|
||||
step: T,
|
||||
command_names: Vec<&'a str>,
|
||||
command_strings: Vec<&'a str>,
|
||||
param_name: &'a str,
|
||||
) -> Result<Vec<Command<'a>>, ParameterScanError> {
|
||||
@ -90,9 +91,20 @@ fn build_parameterized_commands<'a, T: Numeric>(
|
||||
let param_range = RangeStep::new(param_min, param_max, step);
|
||||
let mut commands = vec![];
|
||||
|
||||
let mut i = 0;
|
||||
let name_count = command_names.len();
|
||||
for value in param_range {
|
||||
for cmd in &command_strings {
|
||||
// Sets the command name by index (remainder) if exists.
|
||||
let name = if name_count > 0 {
|
||||
Some(command_names[i % name_count])
|
||||
} else {
|
||||
None
|
||||
};
|
||||
i += 1;
|
||||
|
||||
commands.push(Command::new_parametrized(
|
||||
name,
|
||||
cmd,
|
||||
vec![(param_name, ParameterValue::Numeric(value.into()))],
|
||||
));
|
||||
@ -102,10 +114,12 @@ fn build_parameterized_commands<'a, T: Numeric>(
|
||||
}
|
||||
|
||||
pub fn get_parameterized_commands<'a>(
|
||||
command_names: Option<Values<'a>>,
|
||||
command_strings: Values<'a>,
|
||||
mut vals: clap::Values<'a>,
|
||||
step: Option<&str>,
|
||||
) -> Result<Vec<Command<'a>>, ParameterScanError> {
|
||||
let command_names = command_names.map_or(vec![], |names| names.collect::<Vec<&str>>());
|
||||
let command_strings = command_strings.collect::<Vec<&str>>();
|
||||
let param_name = vals.next().unwrap();
|
||||
let param_min = vals.next().unwrap();
|
||||
@ -121,6 +135,7 @@ pub fn get_parameterized_commands<'a>(
|
||||
param_min,
|
||||
param_max,
|
||||
step,
|
||||
command_names,
|
||||
command_strings,
|
||||
param_name,
|
||||
);
|
||||
@ -135,7 +150,14 @@ pub fn get_parameterized_commands<'a>(
|
||||
}
|
||||
|
||||
let step = Decimal::from_str(step.unwrap())?;
|
||||
build_parameterized_commands(param_min, param_max, step, command_strings, param_name)
|
||||
build_parameterized_commands(
|
||||
param_min,
|
||||
param_max,
|
||||
step,
|
||||
command_names,
|
||||
command_strings,
|
||||
param_name,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -163,8 +185,9 @@ fn test_decimal_range() {
|
||||
#[test]
|
||||
fn test_get_parameterized_commands_int() {
|
||||
let commands =
|
||||
build_parameterized_commands(1i32, 7i32, 3i32, vec!["echo {val}"], "val").unwrap();
|
||||
build_parameterized_commands(1i32, 7i32, 3i32, vec![], vec!["echo {val}"], "val").unwrap();
|
||||
assert_eq!(commands.len(), 3);
|
||||
assert_eq!(commands[2].get_name(), "echo 7");
|
||||
assert_eq!(commands[2].get_shell_command(), "echo 7");
|
||||
}
|
||||
|
||||
@ -174,9 +197,54 @@ fn test_get_parameterized_commands_decimal() {
|
||||
let param_max = Decimal::from_str("1").unwrap();
|
||||
let step = Decimal::from_str("0.33").unwrap();
|
||||
|
||||
let commands =
|
||||
build_parameterized_commands(param_min, param_max, step, vec!["echo {val}"], "val")
|
||||
.unwrap();
|
||||
let commands = build_parameterized_commands(
|
||||
param_min,
|
||||
param_max,
|
||||
step,
|
||||
vec![],
|
||||
vec!["echo {val}"],
|
||||
"val",
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(commands.len(), 4);
|
||||
assert_eq!(commands[3].get_name(), "echo 0.99");
|
||||
assert_eq!(commands[3].get_shell_command(), "echo 0.99");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_parameterized_command_names() {
|
||||
let commands = build_parameterized_commands(
|
||||
1i32,
|
||||
3i32,
|
||||
1i32,
|
||||
vec!["name-{val}"],
|
||||
vec!["echo {val}"],
|
||||
"val",
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(commands.len(), 3);
|
||||
let command_names = commands
|
||||
.iter()
|
||||
.map(|c| c.get_name())
|
||||
.collect::<Vec<String>>();
|
||||
assert_eq!(command_names, vec!["name-1", "name-2", "name-3"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_specified_command_names() {
|
||||
let commands = build_parameterized_commands(
|
||||
1i32,
|
||||
3i32,
|
||||
1i32,
|
||||
vec!["name-a", "name-b", "name-c"],
|
||||
vec!["echo {val}"],
|
||||
"val",
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(commands.len(), 3);
|
||||
let command_names = commands
|
||||
.iter()
|
||||
.map(|c| c.get_name())
|
||||
.collect::<Vec<String>>();
|
||||
assert_eq!(command_names, vec!["name-a", "name-b", "name-c"]);
|
||||
}
|
||||
|
@ -58,6 +58,9 @@ impl<'a> ToString for ParameterValue {
|
||||
/// A command that should be benchmarked.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Command<'a> {
|
||||
/// The command name (without parameter substitution)
|
||||
name: Option<&'a str>,
|
||||
|
||||
/// The command that should be executed (without parameter substitution)
|
||||
expression: &'a str,
|
||||
|
||||
@ -66,24 +69,42 @@ pub struct Command<'a> {
|
||||
}
|
||||
|
||||
impl<'a> Command<'a> {
|
||||
pub fn new(expression: &'a str) -> Command<'a> {
|
||||
pub fn new(name: Option<&'a str>, expression: &'a str) -> Command<'a> {
|
||||
Command {
|
||||
name,
|
||||
expression,
|
||||
parameters: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_parametrized(
|
||||
name: Option<&'a str>,
|
||||
expression: &'a str,
|
||||
parameters: Vec<(&'a str, ParameterValue)>,
|
||||
) -> Command<'a> {
|
||||
Command {
|
||||
name,
|
||||
expression,
|
||||
parameters,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_name(&self) -> String {
|
||||
self.name.map_or_else(
|
||||
|| self.get_shell_command(),
|
||||
|name| self.replace_parameters_in(name),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_shell_command(&self) -> String {
|
||||
self.replace_parameters_in(self.expression)
|
||||
}
|
||||
|
||||
pub fn get_parameters(&self) -> &Vec<(&'a str, ParameterValue)> {
|
||||
&self.parameters
|
||||
}
|
||||
|
||||
fn replace_parameters_in(&self, original: &str) -> String {
|
||||
let mut result = String::new();
|
||||
let mut replacements = BTreeMap::<String, String>::new();
|
||||
for (param_name, param_value) in &self.parameters {
|
||||
@ -92,7 +113,7 @@ impl<'a> Command<'a> {
|
||||
param_value.to_string(),
|
||||
);
|
||||
}
|
||||
let mut remaining = self.expression;
|
||||
let mut remaining = original;
|
||||
// Manually replace consecutive occurrences to avoid double-replacing: e.g.,
|
||||
//
|
||||
// hyperfine -L foo 'a,{bar}' -L bar 'baz,quux' 'echo {foo} {bar}'
|
||||
@ -111,15 +132,12 @@ impl<'a> Command<'a> {
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
pub fn get_parameters(&self) -> &Vec<(&'a str, ParameterValue)> {
|
||||
&self.parameters
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_shell_command_nonoverlapping() {
|
||||
let cmd = Command::new_parametrized(
|
||||
None,
|
||||
"echo {foo} {bar}",
|
||||
vec![
|
||||
("foo", ParameterValue::Text("{bar} baz".into())),
|
||||
@ -129,6 +147,19 @@ fn test_get_shell_command_nonoverlapping() {
|
||||
assert_eq!(cmd.get_shell_command(), "echo {bar} baz quux");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_parameterized_command_name() {
|
||||
let cmd = Command::new_parametrized(
|
||||
Some("name-{bar}-{foo}"),
|
||||
"echo {foo} {bar}",
|
||||
vec![
|
||||
("foo", ParameterValue::Text("baz".into())),
|
||||
("bar", ParameterValue::Text("quux".into())),
|
||||
],
|
||||
);
|
||||
assert_eq!(cmd.get_name(), "name-quux-baz");
|
||||
}
|
||||
|
||||
impl<'a> fmt::Display for Command<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.get_shell_command())
|
||||
|
84
src/main.rs
84
src/main.rs
@ -74,7 +74,7 @@ fn main() {
|
||||
let commands = build_commands(&matches);
|
||||
let export_manager = match build_export_manager(&matches) {
|
||||
Ok(export_manager) => export_manager,
|
||||
Err(ref e) => error(&e.to_string())
|
||||
Err(ref e) => error(&e.to_string()),
|
||||
};
|
||||
|
||||
let res = match options {
|
||||
@ -139,16 +139,6 @@ fn build_hyperfine_options<'a>(
|
||||
(None, None) => {}
|
||||
};
|
||||
|
||||
options.names = matches
|
||||
.values_of("command-name")
|
||||
.map(|values| values.map(String::from).collect::<Vec<String>>());
|
||||
if let Some(ref names) = options.names {
|
||||
let command_strings = matches.values_of("command").unwrap();
|
||||
if names.len() > command_strings.len() {
|
||||
return Err(OptionsError::TooManyCommandNames(command_strings.len()));
|
||||
}
|
||||
}
|
||||
|
||||
options.preparation_command = matches
|
||||
.values_of("prepare")
|
||||
.map(|values| values.map(String::from).collect::<Vec<String>>());
|
||||
@ -224,15 +214,18 @@ fn build_export_manager(matches: &ArgMatches<'_>) -> io::Result<ExportManager> {
|
||||
|
||||
/// Build the commands to benchmark
|
||||
fn build_commands<'a>(matches: &'a ArgMatches<'_>) -> Vec<Command<'a>> {
|
||||
let command_names = matches.values_of("command-name");
|
||||
let command_strings = matches.values_of("command").unwrap();
|
||||
|
||||
if let Some(args) = matches.values_of("parameter-scan") {
|
||||
let step_size = matches.value_of("parameter-step-size");
|
||||
match get_parameterized_commands(command_strings, args, step_size) {
|
||||
match get_parameterized_commands(command_names, command_strings, args, step_size) {
|
||||
Ok(commands) => commands,
|
||||
Err(e) => error(&e.to_string()),
|
||||
}
|
||||
} else if let Some(args) = matches.values_of("parameter-list") {
|
||||
let command_names = command_names.map_or(vec![], |names| names.collect::<Vec<&str>>());
|
||||
|
||||
let args: Vec<_> = args.collect();
|
||||
let param_names_and_values: Vec<(&str, Vec<String>)> = args
|
||||
.chunks_exact(2)
|
||||
@ -262,9 +255,19 @@ fn build_commands<'a>(matches: &'a ArgMatches<'_>) -> Vec<Command<'a>> {
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
let mut i = 0;
|
||||
let name_count = command_names.len();
|
||||
let mut commands = Vec::with_capacity(param_space_size);
|
||||
let mut index = vec![0usize; dimensions.len()];
|
||||
'outer: loop {
|
||||
// Sets the command name by index (remainder) if exists.
|
||||
let name = if name_count > 0 {
|
||||
Some(command_names[i % name_count])
|
||||
} else {
|
||||
None
|
||||
};
|
||||
i += 1;
|
||||
|
||||
let (command_index, params_indices) = index.split_first().unwrap();
|
||||
let parameters = param_names_and_values
|
||||
.iter()
|
||||
@ -272,6 +275,7 @@ fn build_commands<'a>(matches: &'a ArgMatches<'_>) -> Vec<Command<'a>> {
|
||||
.map(|((name, values), i)| (*name, ParameterValue::Text(values[*i].clone())))
|
||||
.collect();
|
||||
commands.push(Command::new_parametrized(
|
||||
name,
|
||||
command_list[*command_index],
|
||||
parameters,
|
||||
));
|
||||
@ -290,7 +294,18 @@ fn build_commands<'a>(matches: &'a ArgMatches<'_>) -> Vec<Command<'a>> {
|
||||
|
||||
commands
|
||||
} else {
|
||||
command_strings.map(Command::new).collect()
|
||||
let command_names = command_names.map_or(vec![], |names| names.collect::<Vec<&str>>());
|
||||
if command_names.len() > command_strings.len() {
|
||||
let err = OptionsError::TooManyCommandNames(command_strings.len());
|
||||
error(&err.to_string());
|
||||
}
|
||||
|
||||
let command_list = command_strings.collect::<Vec<&str>>();
|
||||
let mut commands = Vec::with_capacity(command_list.len());
|
||||
for (i, s) in command_list.iter().enumerate() {
|
||||
commands.push(Command::new(command_names.get(i).copied(), &s));
|
||||
}
|
||||
commands
|
||||
}
|
||||
}
|
||||
|
||||
@ -328,7 +343,7 @@ fn test_build_commands_cross_product() {
|
||||
let cmd = |cmd: usize, foo: &str, bar: &str| {
|
||||
let expression = ["echo {foo} {bar}", "printf '%s\n' {foo} {bar}"][cmd];
|
||||
let params = vec![("foo", pv(foo)), ("bar", pv(bar))];
|
||||
Command::new_parametrized(expression, params)
|
||||
Command::new_parametrized(None, expression, params)
|
||||
};
|
||||
let expected = vec![
|
||||
cmd(0, "a", "z"),
|
||||
@ -342,3 +357,44 @@ fn test_build_commands_cross_product() {
|
||||
];
|
||||
assert_eq!(result, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_build_parameter_list_commands() {
|
||||
let matches = get_arg_matches(vec![
|
||||
"hyperfine",
|
||||
"echo {foo}",
|
||||
"--parameter-list",
|
||||
"foo",
|
||||
"1,2",
|
||||
"--command-name",
|
||||
"name-{foo}",
|
||||
]);
|
||||
let commands = build_commands(&matches);
|
||||
assert_eq!(commands.len(), 2);
|
||||
assert_eq!(commands[0].get_name(), "name-1");
|
||||
assert_eq!(commands[1].get_name(), "name-2");
|
||||
assert_eq!(commands[0].get_shell_command(), "echo 1");
|
||||
assert_eq!(commands[1].get_shell_command(), "echo 2");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_build_parameter_range_commands() {
|
||||
let matches = get_arg_matches(vec![
|
||||
"hyperfine",
|
||||
"echo {val}",
|
||||
"--parameter-scan",
|
||||
"val",
|
||||
"1",
|
||||
"2",
|
||||
"--parameter-step-size",
|
||||
"1",
|
||||
"--command-name",
|
||||
"name-{val}",
|
||||
]);
|
||||
let commands = build_commands(&matches);
|
||||
assert_eq!(commands.len(), 2);
|
||||
assert_eq!(commands[0].get_name(), "name-1");
|
||||
assert_eq!(commands[1].get_name(), "name-2");
|
||||
assert_eq!(commands[0].get_shell_command(), "echo 1");
|
||||
assert_eq!(commands[1].get_shell_command(), "echo 2");
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user