mirror of
https://github.com/sharkdp/hyperfine.git
synced 2024-09-11 14:07:02 +03:00
parent
f75f0a10fe
commit
277807cb5a
22
Cargo.lock
generated
22
Cargo.lock
generated
@ -274,6 +274,7 @@ dependencies = [
|
||||
"csv",
|
||||
"indicatif",
|
||||
"libc",
|
||||
"nix",
|
||||
"predicates",
|
||||
"rand 0.8.5",
|
||||
"rust_decimal",
|
||||
@ -356,6 +357,27 @@ version = "2.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
|
||||
dependencies = [
|
||||
"autocfg 1.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.24.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f17df307904acd05aa8e32e97bb20f2a0df1728bbc2d771ae8f9a90463441e9"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"memoffset",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "normalize-line-endings"
|
||||
version = "0.3.0"
|
||||
|
@ -31,6 +31,9 @@ libc = "0.2"
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
winapi = { version = "0.3", features = ["processthreadsapi", "minwindef", "winnt"] }
|
||||
|
||||
[target.'cfg(target_os="linux")'.dependencies]
|
||||
nix = { version = "0.24.1", features = ["zerocopy"] }
|
||||
|
||||
[dependencies.clap]
|
||||
version = "3"
|
||||
default-features = false
|
||||
|
@ -36,10 +36,10 @@ pub trait Executor {
|
||||
fn run_command_and_measure_common(
|
||||
mut command: std::process::Command,
|
||||
command_failure_action: CmdFailureAction,
|
||||
command_output_policy: CommandOutputPolicy,
|
||||
command_output_policy: &CommandOutputPolicy,
|
||||
command_name: &str,
|
||||
) -> Result<TimerResult> {
|
||||
let (stdout, stderr) = command_output_policy.get_stdout_stderr();
|
||||
let (stdout, stderr) = command_output_policy.get_stdout_stderr()?;
|
||||
command.stdin(Stdio::null()).stdout(stdout).stderr(stderr);
|
||||
|
||||
command.env(
|
||||
@ -83,7 +83,7 @@ impl<'a> Executor for RawExecutor<'a> {
|
||||
let result = run_command_and_measure_common(
|
||||
command.get_command()?,
|
||||
command_failure_action.unwrap_or(self.options.command_failure_action),
|
||||
self.options.command_output_policy,
|
||||
&self.options.command_output_policy,
|
||||
&command.get_command_line(),
|
||||
)?;
|
||||
|
||||
@ -136,7 +136,7 @@ impl<'a> Executor for ShellExecutor<'a> {
|
||||
let mut result = run_command_and_measure_common(
|
||||
command_builder,
|
||||
command_failure_action.unwrap_or(self.options.command_failure_action),
|
||||
self.options.command_output_policy,
|
||||
&self.options.command_output_policy,
|
||||
&command.get_command_line(),
|
||||
)?;
|
||||
|
||||
|
14
src/cli.rs
14
src/cli.rs
@ -259,6 +259,20 @@ fn build_command() -> Command<'static> {
|
||||
when trying to benchmark output speed.",
|
||||
),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("output")
|
||||
.long("output")
|
||||
.conflicts_with("show-output")
|
||||
.takes_value(true)
|
||||
.value_name("WHERE")
|
||||
.help(
|
||||
"Control where the output of the benchmark is redirected.\n\n \
|
||||
null: Redirect output to /dev/null (the default).\n\n \
|
||||
pipe: Feed the output through a pipe before discarding it.\n \
|
||||
Useful if the benchmark special-cases /dev/null.\n\n \
|
||||
<FILE>: Write the output to the given file.",
|
||||
),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("command-name")
|
||||
.long("command-name")
|
||||
|
@ -1,5 +1,7 @@
|
||||
use std::fs::File;
|
||||
use std::path::PathBuf;
|
||||
use std::process::{Command, Stdio};
|
||||
use std::{cmp, fmt};
|
||||
use std::{cmp, fmt, io};
|
||||
|
||||
use anyhow::ensure;
|
||||
use atty::Stream;
|
||||
@ -109,11 +111,17 @@ impl Default for RunBounds {
|
||||
}
|
||||
|
||||
/// How to handle the output of benchmarked commands
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum CommandOutputPolicy {
|
||||
/// Discard all output
|
||||
Discard,
|
||||
|
||||
/// Feed output through a pipe before discarding it
|
||||
Pipe,
|
||||
|
||||
/// Redirect output to a file
|
||||
File(PathBuf),
|
||||
|
||||
/// Show command output on the terminal
|
||||
Forward,
|
||||
}
|
||||
@ -125,11 +133,22 @@ impl Default for CommandOutputPolicy {
|
||||
}
|
||||
|
||||
impl CommandOutputPolicy {
|
||||
pub fn get_stdout_stderr(&self) -> (Stdio, Stdio) {
|
||||
match self {
|
||||
pub fn get_stdout_stderr(&self) -> io::Result<(Stdio, Stdio)> {
|
||||
let streams = match self {
|
||||
CommandOutputPolicy::Discard => (Stdio::null(), Stdio::null()),
|
||||
|
||||
// Typically only stdout is performance-relevant, so just pipe that
|
||||
CommandOutputPolicy::Pipe => (Stdio::piped(), Stdio::null()),
|
||||
|
||||
CommandOutputPolicy::File(path) => {
|
||||
let file = File::create(&path)?;
|
||||
(file.into(), Stdio::null())
|
||||
}
|
||||
|
||||
CommandOutputPolicy::Forward => (Stdio::inherit(), Stdio::inherit()),
|
||||
}
|
||||
};
|
||||
|
||||
Ok(streams)
|
||||
}
|
||||
}
|
||||
|
||||
@ -251,6 +270,12 @@ impl Options {
|
||||
|
||||
options.command_output_policy = if matches.is_present("show-output") {
|
||||
CommandOutputPolicy::Forward
|
||||
} else if let Some(output) = matches.value_of("output") {
|
||||
match output {
|
||||
"null" => CommandOutputPolicy::Discard,
|
||||
"pipe" => CommandOutputPolicy::Pipe,
|
||||
path => CommandOutputPolicy::File(path.into()),
|
||||
}
|
||||
} else {
|
||||
CommandOutputPolicy::Discard
|
||||
};
|
||||
@ -262,7 +287,7 @@ impl Options {
|
||||
Some("color") => OutputStyleOption::Color,
|
||||
Some("none") => OutputStyleOption::Disabled,
|
||||
_ => {
|
||||
if options.command_output_policy == CommandOutputPolicy::Discard
|
||||
if options.command_output_policy != CommandOutputPolicy::Forward
|
||||
&& atty::is(Stream::Stdout)
|
||||
{
|
||||
OutputStyleOption::Full
|
||||
|
@ -6,10 +6,20 @@ mod windows_timer;
|
||||
#[cfg(not(windows))]
|
||||
mod unix_timer;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
use nix::fcntl::{splice, SpliceFFlags};
|
||||
#[cfg(target_os = "linux")]
|
||||
use std::fs::File;
|
||||
#[cfg(target_os = "linux")]
|
||||
use std::os::unix::io::AsRawFd;
|
||||
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
use std::io::Read;
|
||||
|
||||
use crate::util::units::Second;
|
||||
use wall_clock_timer::WallClockTimer;
|
||||
|
||||
use std::process::{Command, ExitStatus};
|
||||
use std::process::{ChildStdout, Command, ExitStatus};
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
@ -33,26 +43,58 @@ pub struct TimerResult {
|
||||
pub status: ExitStatus,
|
||||
}
|
||||
|
||||
/// Discard the output of a child process.
|
||||
fn discard(output: ChildStdout) {
|
||||
const CHUNK_SIZE: usize = 64 << 10;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
if let Ok(file) = File::create("/dev/null") {
|
||||
while let Ok(bytes) = splice(
|
||||
output.as_raw_fd(),
|
||||
None,
|
||||
file.as_raw_fd(),
|
||||
None,
|
||||
CHUNK_SIZE,
|
||||
SpliceFFlags::empty(),
|
||||
) {
|
||||
if bytes == 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
{
|
||||
let mut output = output;
|
||||
let mut buf = [0; CHUNK_SIZE];
|
||||
while let Ok(bytes) = output.read(&mut buf) {
|
||||
if bytes == 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Execute the given command and return a timing summary
|
||||
pub fn execute_and_measure(mut command: Command) -> Result<TimerResult> {
|
||||
let wallclock_timer = WallClockTimer::start();
|
||||
|
||||
let mut child = command.spawn()?;
|
||||
let output = child.stdout.take();
|
||||
|
||||
#[cfg(not(windows))]
|
||||
let ((time_user, time_system), status) = {
|
||||
let cpu_timer = self::unix_timer::CPUTimer::start();
|
||||
let status = command.status()?;
|
||||
(cpu_timer.stop(), status)
|
||||
};
|
||||
|
||||
let cpu_timer = self::unix_timer::CPUTimer::start();
|
||||
#[cfg(windows)]
|
||||
let ((time_user, time_system), status) = {
|
||||
let mut child = command.spawn()?;
|
||||
let cpu_timer = self::windows_timer::CPUTimer::start_for_process(&child);
|
||||
let status = child.wait()?;
|
||||
let cpu_timer = self::windows_timer::CPUTimer::start_for_process(&child);
|
||||
|
||||
(cpu_timer.stop(), status)
|
||||
};
|
||||
if let Some(output) = output {
|
||||
discard(output);
|
||||
}
|
||||
let status = child.wait()?;
|
||||
|
||||
let (time_user, time_system) = cpu_timer.stop();
|
||||
let time_real = wallclock_timer.stop();
|
||||
|
||||
Ok(TimerResult {
|
||||
|
Loading…
Reference in New Issue
Block a user