mirror of
https://github.com/sharkdp/hyperfine.git
synced 2026-11-02 09:01:25 +00:00
Allow forwarding benchmark's stdout to hyperfine
This change makes it possible to forward the stdout of the command the user tries to benchmark to hyperfine's stdout using the `--print-stdout` flag. This should be irrelevant for most use-cases, however it makes it possible to get some crude benchmarks of terminal emulators similar to what's currently often done with `time cat file`.
This commit is contained in:
1 parent
1011eef7f3
commit
4be435436e
5 files changed
+63
-21
No files matched your search
@@ -118,6 +118,14 @@ fn build_app() -> App<'static, 'static> {
|
|||||||
.value_name("FILE")
|
.value_name("FILE")
|
||||||
.help("Export the timing results as a Markdown table to the given 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.")
|
.help_message("Print this help message.")
|
||||||
.version_message("Show version information.")
|
.version_message("Show version information.")
|
||||||
}
|
}
|
||||||
+38
-11
@@ -1,4 +1,5 @@
|
|||||||
use std::io;
|
use std::io;
|
||||||
|
use std::process::Stdio;
|
||||||
|
|
||||||
use colored::*;
|
use colored::*;
|
||||||
use statistical::{mean, standard_deviation};
|
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
|
/// Run the given shell command and measure the execution time
|
||||||
pub fn time_shell_command(
|
pub fn time_shell_command(
|
||||||
command: &Command,
|
command: &Command,
|
||||||
|
print_stdout: bool,
|
||||||
failure_action: CmdFailureAction,
|
failure_action: CmdFailureAction,
|
||||||
shell_spawning_time: Option<TimingResult>,
|
shell_spawning_time: Option<TimingResult>,
|
||||||
) -> io::Result<(TimingResult, bool)> {
|
) -> io::Result<(TimingResult, bool)> {
|
||||||
let wallclock_timer = WallClockTimer::start();
|
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_user = result.user_time;
|
||||||
let mut time_system = result.system_time;
|
let mut time_system = result.system_time;
|
||||||
@@ -76,7 +83,10 @@ pub fn time_shell_command(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Measure the average shell spawning time
|
/// Measure the average shell spawning time
|
||||||
pub fn mean_shell_spawning_time(style: &OutputStyleOption) -> io::Result<TimingResult> {
|
pub fn mean_shell_spawning_time(
|
||||||
|
style: &OutputStyleOption,
|
||||||
|
print_stdout: bool,
|
||||||
|
) -> io::Result<TimingResult> {
|
||||||
const COUNT: u64 = 200;
|
const COUNT: u64 = 200;
|
||||||
let progress_bar = get_progress_bar(COUNT, "Measuring shell spawning time", style);
|
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<TimingR
|
|||||||
|
|
||||||
for _ in 0..COUNT {
|
for _ in 0..COUNT {
|
||||||
// Just run the shell without any command
|
// Just run the shell without any command
|
||||||
let res = time_shell_command(&Command::new(""), CmdFailureAction::RaiseError, None);
|
let res = time_shell_command(
|
||||||
|
&Command::new(""),
|
||||||
|
print_stdout,
|
||||||
|
CmdFailureAction::RaiseError,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
@@ -114,10 +129,14 @@ pub fn mean_shell_spawning_time(style: &OutputStyleOption) -> io::Result<TimingR
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Run the command specified by `--prepare`.
|
/// Run the command specified by `--prepare`.
|
||||||
fn run_preparation_command(command: &Option<String>) -> io::Result<TimingResult> {
|
fn run_preparation_command(
|
||||||
|
command: &Option<String>,
|
||||||
|
print_stdout: bool,
|
||||||
|
) -> io::Result<TimingResult> {
|
||||||
if let &Some(ref preparation_command) = command {
|
if let &Some(ref preparation_command) = command {
|
||||||
let res = time_shell_command(
|
let res = time_shell_command(
|
||||||
&Command::new(preparation_command),
|
&Command::new(preparation_command),
|
||||||
|
print_stdout,
|
||||||
CmdFailureAction::RaiseError,
|
CmdFailureAction::RaiseError,
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
@@ -164,7 +183,7 @@ pub fn run_benchmark(
|
|||||||
);
|
);
|
||||||
|
|
||||||
for _ in 0..options.warmup_count {
|
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.inc(1);
|
||||||
}
|
}
|
||||||
progress_bar.finish_and_clear();
|
progress_bar.finish_and_clear();
|
||||||
@@ -178,11 +197,15 @@ pub fn run_benchmark(
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Run init / cleanup command
|
// 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
|
// Initial timing run
|
||||||
let (res, success) =
|
let (res, success) = time_shell_command(
|
||||||
time_shell_command(cmd, options.failure_action, Some(shell_spawning_time))?;
|
cmd,
|
||||||
|
options.print_stdout,
|
||||||
|
options.failure_action,
|
||||||
|
Some(shell_spawning_time),
|
||||||
|
)?;
|
||||||
|
|
||||||
// Determine number of benchmark runs
|
// Determine number of benchmark runs
|
||||||
let runs_in_min_time = (options.min_time_sec
|
let runs_in_min_time = (options.min_time_sec
|
||||||
@@ -209,7 +232,7 @@ pub fn run_benchmark(
|
|||||||
|
|
||||||
// Gather statistics
|
// Gather statistics
|
||||||
for _ in 0..count_remaining {
|
for _ in 0..count_remaining {
|
||||||
run_preparation_command(&options.preparation_command)?;
|
run_preparation_command(&options.preparation_command, options.print_stdout)?;
|
||||||
|
|
||||||
let msg = {
|
let msg = {
|
||||||
let mean = format_duration(mean(×_real), None);
|
let mean = format_duration(mean(×_real), None);
|
||||||
@@ -217,8 +240,12 @@ pub fn run_benchmark(
|
|||||||
};
|
};
|
||||||
progress_bar.set_message(&msg);
|
progress_bar.set_message(&msg);
|
||||||
|
|
||||||
let (res, success) =
|
let (res, success) = time_shell_command(
|
||||||
time_shell_command(cmd, options.failure_action, Some(shell_spawning_time))?;
|
cmd,
|
||||||
|
options.print_stdout,
|
||||||
|
options.failure_action,
|
||||||
|
Some(shell_spawning_time),
|
||||||
|
)?;
|
||||||
|
|
||||||
times_real.push(res.time_real);
|
times_real.push(res.time_real);
|
||||||
times_user.push(res.time_user);
|
times_user.push(res.time_user);
|
||||||
|
|||||||
@@ -19,8 +19,8 @@ pub struct ExecuteResult {
|
|||||||
|
|
||||||
/// Execute the given command and return a timing summary
|
/// Execute the given command and return a timing summary
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
pub fn execute_and_time(command: &str) -> io::Result<ExecuteResult> {
|
pub fn execute_and_time(stdout: Stdio, command: &str) -> io::Result<ExecuteResult> {
|
||||||
let mut child = run_shell_command(command)?;
|
let mut child = run_shell_command(stdout, command)?;
|
||||||
let cpu_timer = get_cpu_timer(&child);
|
let cpu_timer = get_cpu_timer(&child);
|
||||||
let status = child.wait()?;
|
let status = child.wait()?;
|
||||||
|
|
||||||
@@ -34,10 +34,10 @@ pub fn execute_and_time(command: &str) -> io::Result<ExecuteResult> {
|
|||||||
|
|
||||||
/// Execute the given command and return a timing summary
|
/// Execute the given command and return a timing summary
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
pub fn execute_and_time(command: &str) -> io::Result<ExecuteResult> {
|
pub fn execute_and_time(stdout: Stdio, command: &str) -> io::Result<ExecuteResult> {
|
||||||
let cpu_timer = get_cpu_timer();
|
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();
|
let (user_time, system_time) = cpu_timer.stop();
|
||||||
|
|
||||||
@@ -50,24 +50,24 @@ pub fn execute_and_time(command: &str) -> io::Result<ExecuteResult> {
|
|||||||
|
|
||||||
/// Run a standard shell command
|
/// Run a standard shell command
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
fn run_shell_command(command: &str) -> io::Result<std::process::ExitStatus> {
|
fn run_shell_command(stdout: Stdio, command: &str) -> io::Result<std::process::ExitStatus> {
|
||||||
Command::new("sh")
|
Command::new("sh")
|
||||||
.arg("-c")
|
.arg("-c")
|
||||||
.arg(command)
|
.arg(command)
|
||||||
.stdin(Stdio::null())
|
.stdin(Stdio::null())
|
||||||
.stdout(Stdio::null())
|
.stdout(stdout)
|
||||||
.stderr(Stdio::null())
|
.stderr(Stdio::null())
|
||||||
.status()
|
.status()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Run a Windows shell command using cmd.exe
|
/// Run a Windows shell command using cmd.exe
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
fn run_shell_command(command: &str) -> io::Result<std::process::Child> {
|
fn run_shell_command(print_stdout: bool, command: &str) -> io::Result<std::process::Child> {
|
||||||
Command::new("cmd")
|
Command::new("cmd")
|
||||||
.arg("/C")
|
.arg("/C")
|
||||||
.arg(command)
|
.arg(command)
|
||||||
.stdin(Stdio::null())
|
.stdin(Stdio::null())
|
||||||
.stdout(Stdio::null())
|
.stdout(stdout)
|
||||||
.stderr(Stdio::null())
|
.stderr(Stdio::null())
|
||||||
.spawn()
|
.spawn()
|
||||||
}
|
}
|
||||||
@@ -89,6 +89,9 @@ pub struct HyperfineOptions {
|
|||||||
|
|
||||||
/// What color mode to use for output
|
/// What color mode to use for output
|
||||||
pub output_style: OutputStyleOption,
|
pub output_style: OutputStyleOption,
|
||||||
|
|
||||||
|
/// Forward benchmark's stdout to hyperfine's stdout
|
||||||
|
pub print_stdout: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for HyperfineOptions {
|
impl Default for HyperfineOptions {
|
||||||
@@ -100,6 +103,7 @@ impl Default for HyperfineOptions {
|
|||||||
failure_action: CmdFailureAction::RaiseError,
|
failure_action: CmdFailureAction::RaiseError,
|
||||||
preparation_command: None,
|
preparation_command: None,
|
||||||
output_style: OutputStyleOption::Full,
|
output_style: OutputStyleOption::Full,
|
||||||
|
print_stdout: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-2
@@ -56,7 +56,8 @@ pub fn error(message: &str) -> ! {
|
|||||||
|
|
||||||
/// Runs the benchmark for the given commands
|
/// Runs the benchmark for the given commands
|
||||||
fn run(commands: &Vec<Command>, options: &HyperfineOptions) -> io::Result<Vec<BenchmarkResult>> {
|
fn run(commands: &Vec<Command>, options: &HyperfineOptions) -> io::Result<Vec<BenchmarkResult>> {
|
||||||
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![];
|
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.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") {
|
options.output_style = match matches.value_of("style") {
|
||||||
Some("full") => OutputStyleOption::Full,
|
Some("full") => OutputStyleOption::Full,
|
||||||
Some("basic") => OutputStyleOption::Basic,
|
Some("basic") => OutputStyleOption::Basic,
|
||||||
Some("nocolor") => OutputStyleOption::NoColor,
|
Some("nocolor") => OutputStyleOption::NoColor,
|
||||||
_ => if atty::is(Stream::Stdout) {
|
_ => if !options.print_stdout && atty::is(Stream::Stdout) {
|
||||||
OutputStyleOption::Full
|
OutputStyleOption::Full
|
||||||
} else {
|
} else {
|
||||||
OutputStyleOption::Basic
|
OutputStyleOption::Basic
|
||||||
|
|||||||
Reference in new issue
Block a user