mirror of
https://github.com/sharkdp/hyperfine.git
synced 2026-11-01 08:50:19 +00:00
Initial support for user and system time
This commit is contained in:
1 parent
35a513917d
commit
ef2bef588c
6 files changed
+143
-50
No files matched your search
Generated
+2
-1
@@ -82,11 +82,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hyperfine"
|
name = "hyperfine"
|
||||||
version = "0.2.0"
|
version = "0.3.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"clap 2.29.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"clap 2.29.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"indicatif 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"indicatif 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"statistical 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"statistical 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -7,12 +7,13 @@ license = "MIT/Apache-2.0"
|
|||||||
name = "hyperfine"
|
name = "hyperfine"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
repository = "https://github.com/sharkdp/hyperfine"
|
repository = "https://github.com/sharkdp/hyperfine"
|
||||||
version = "0.2.0"
|
version = "0.3.0"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
ansi_term = "0.10"
|
ansi_term = "0.10"
|
||||||
indicatif = "0.9"
|
indicatif = "0.9"
|
||||||
statistical = "0.1"
|
statistical = "0.1"
|
||||||
|
libc = "0.2"
|
||||||
|
|
||||||
[dependencies.clap]
|
[dependencies.clap]
|
||||||
version = "2"
|
version = "2"
|
||||||
|
|||||||
+88
-48
@@ -2,28 +2,33 @@ use std::io;
|
|||||||
use std::process::{Command, Stdio};
|
use std::process::{Command, Stdio};
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
use ansi_term::Colour::{Cyan, Green, Purple, White, Yellow};
|
use ansi_term::Colour::{Blue, Cyan, Green, Purple, White, Yellow};
|
||||||
use statistical::{mean, standard_deviation};
|
use statistical::{mean, standard_deviation};
|
||||||
|
|
||||||
use hyperfine::internal::{get_progress_bar, max, min, CmdFailureAction, HyperfineOptions, Second,
|
use hyperfine::internal::{get_progress_bar, max, min, CmdFailureAction, HyperfineOptions, Second,
|
||||||
Warnings, MIN_EXECUTION_TIME};
|
Warnings, MIN_EXECUTION_TIME};
|
||||||
use hyperfine::format::{format_duration, format_duration_unit};
|
use hyperfine::format::{format_duration, format_duration_unit};
|
||||||
|
use hyperfine::cputime::{get_cpu_interval, get_cpu_times};
|
||||||
|
|
||||||
/// Results from timing a single shell command
|
/// Results from timing a single shell command
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct TimingResult {
|
pub struct TimingResult {
|
||||||
/// Execution time in seconds
|
/// Wall clock time
|
||||||
pub execution_time: Second,
|
pub time_real: Second,
|
||||||
|
|
||||||
/// True if the command finished with exit code zero
|
/// Time spent in user mode
|
||||||
pub success: bool,
|
pub time_user: Second,
|
||||||
|
|
||||||
|
/// Time spent in kernel mode
|
||||||
|
pub time_system: Second,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TimingResult {
|
/// Correct for shell spawning time
|
||||||
fn new(execution_time: Second, success: bool) -> TimingResult {
|
fn subtract_shell_spawning_time(time: Second, shell_spawning_time: Second) -> Second {
|
||||||
TimingResult {
|
if time < shell_spawning_time {
|
||||||
execution_time,
|
0.0
|
||||||
success,
|
} else {
|
||||||
}
|
time - shell_spawning_time
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,9 +36,10 @@ impl TimingResult {
|
|||||||
pub fn time_shell_command(
|
pub fn time_shell_command(
|
||||||
shell_cmd: &str,
|
shell_cmd: &str,
|
||||||
failure_action: CmdFailureAction,
|
failure_action: CmdFailureAction,
|
||||||
shell_spawning_time: Option<Second>,
|
shell_spawning_time: Option<TimingResult>,
|
||||||
) -> io::Result<TimingResult> {
|
) -> io::Result<(TimingResult, bool)> {
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
|
let start_cpu = get_cpu_times();
|
||||||
|
|
||||||
let status = Command::new("sh")
|
let status = Command::new("sh")
|
||||||
.arg("-c")
|
.arg("-c")
|
||||||
@@ -43,6 +49,9 @@ pub fn time_shell_command(
|
|||||||
.stderr(Stdio::null())
|
.stderr(Stdio::null())
|
||||||
.status()?;
|
.status()?;
|
||||||
|
|
||||||
|
let end_cpu = get_cpu_times();
|
||||||
|
let duration = start.elapsed();
|
||||||
|
|
||||||
if failure_action == CmdFailureAction::RaiseError && !status.success() {
|
if failure_action == CmdFailureAction::RaiseError && !status.success() {
|
||||||
return Err(io::Error::new(
|
return Err(io::Error::new(
|
||||||
io::ErrorKind::Other,
|
io::ErrorKind::Other,
|
||||||
@@ -51,33 +60,40 @@ pub fn time_shell_command(
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let duration = start.elapsed();
|
// Real time
|
||||||
|
let mut time_real = duration.as_secs() as f64 + (duration.subsec_nanos() as f64) * 1e-9;
|
||||||
|
|
||||||
let execution_time = duration.as_secs() as f64 + (duration.subsec_nanos() as f64) * 1e-9;
|
// User and system time
|
||||||
|
let cpu_interval = get_cpu_interval(&start_cpu, &end_cpu);
|
||||||
|
let mut time_user = cpu_interval.user;
|
||||||
|
let mut time_system = cpu_interval.system;
|
||||||
|
|
||||||
// Correct for shell spawning time
|
// Correct for shell spawning time
|
||||||
let execution_time_corrected = if let Some(spawning_time) = shell_spawning_time {
|
if let Some(spawning_time) = shell_spawning_time {
|
||||||
if execution_time < spawning_time {
|
time_real = subtract_shell_spawning_time(time_real, spawning_time.time_real);
|
||||||
0.0
|
time_user = subtract_shell_spawning_time(cpu_interval.user, spawning_time.time_user);
|
||||||
} else {
|
time_system = subtract_shell_spawning_time(cpu_interval.system, spawning_time.time_system);
|
||||||
execution_time - spawning_time
|
}
|
||||||
}
|
|
||||||
} else {
|
|
||||||
execution_time
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(TimingResult::new(
|
Ok((
|
||||||
execution_time_corrected,
|
TimingResult {
|
||||||
|
time_real,
|
||||||
|
time_user,
|
||||||
|
time_system,
|
||||||
|
},
|
||||||
status.success(),
|
status.success(),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Measure the average shell spawning time
|
/// Measure the average shell spawning time
|
||||||
pub fn mean_shell_spawning_time() -> io::Result<Second> {
|
pub fn mean_shell_spawning_time() -> io::Result<TimingResult> {
|
||||||
const COUNT: u64 = 200;
|
const COUNT: u64 = 200;
|
||||||
let progress_bar = get_progress_bar(COUNT, "Measuring shell spawning time");
|
let progress_bar = get_progress_bar(COUNT, "Measuring shell spawning time");
|
||||||
|
|
||||||
let mut times: Vec<Second> = vec![];
|
let mut times_real: Vec<Second> = vec![];
|
||||||
|
let mut times_user: Vec<Second> = vec![];
|
||||||
|
let mut times_system: Vec<Second> = vec![];
|
||||||
|
|
||||||
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("", CmdFailureAction::RaiseError, None);
|
let res = time_shell_command("", CmdFailureAction::RaiseError, None);
|
||||||
@@ -90,22 +106,28 @@ pub fn mean_shell_spawning_time() -> io::Result<Second> {
|
|||||||
Make sure you can run 'sh -c \"\"'.",
|
Make sure you can run 'sh -c \"\"'.",
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
Ok(r) => {
|
Ok((r, _)) => {
|
||||||
times.push(r.execution_time);
|
times_real.push(r.time_real);
|
||||||
|
times_user.push(r.time_user);
|
||||||
|
times_system.push(r.time_system);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
progress_bar.inc(1);
|
progress_bar.inc(1);
|
||||||
}
|
}
|
||||||
progress_bar.finish_and_clear();
|
progress_bar.finish_and_clear();
|
||||||
|
|
||||||
Ok(mean(×))
|
Ok(TimingResult {
|
||||||
|
time_real: mean(×_real),
|
||||||
|
time_user: mean(×_user),
|
||||||
|
time_system: mean(×_system),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Run the benchmark for a single shell command
|
/// Run the benchmark for a single shell command
|
||||||
pub fn run_benchmark(
|
pub fn run_benchmark(
|
||||||
num: usize,
|
num: usize,
|
||||||
cmd: &str,
|
cmd: &str,
|
||||||
shell_spawning_time: Second,
|
shell_spawning_time: TimingResult,
|
||||||
options: &HyperfineOptions,
|
options: &HyperfineOptions,
|
||||||
) -> io::Result<()> {
|
) -> io::Result<()> {
|
||||||
println!(
|
println!(
|
||||||
@@ -116,7 +138,9 @@ pub fn run_benchmark(
|
|||||||
);
|
);
|
||||||
println!();
|
println!();
|
||||||
|
|
||||||
let mut execution_times: Vec<Second> = vec![];
|
let mut times_real: Vec<Second> = vec![];
|
||||||
|
let mut times_user: Vec<Second> = vec![];
|
||||||
|
let mut times_system: Vec<Second> = vec![];
|
||||||
let mut all_succeeded = true;
|
let mut all_succeeded = true;
|
||||||
|
|
||||||
// Warmup phase
|
// Warmup phase
|
||||||
@@ -142,11 +166,12 @@ pub fn run_benchmark(
|
|||||||
run_preparation_command();
|
run_preparation_command();
|
||||||
|
|
||||||
// Initial timing run
|
// Initial timing run
|
||||||
let res = time_shell_command(cmd, options.failure_action, Some(shell_spawning_time))?;
|
let (res, success) =
|
||||||
|
time_shell_command(cmd, options.failure_action, Some(shell_spawning_time))?;
|
||||||
|
|
||||||
// Determine number of benchmark runs
|
// Determine number of benchmark runs
|
||||||
let runs_in_min_time =
|
let runs_in_min_time =
|
||||||
(options.min_time_sec / (res.execution_time + shell_spawning_time)) as u64;
|
(options.min_time_sec / (res.time_real + shell_spawning_time.time_real)) as u64;
|
||||||
|
|
||||||
let count = if runs_in_min_time >= options.min_runs {
|
let count = if runs_in_min_time >= options.min_runs {
|
||||||
runs_in_min_time
|
runs_in_min_time
|
||||||
@@ -157,8 +182,11 @@ pub fn run_benchmark(
|
|||||||
let count_remaining = count - 1;
|
let count_remaining = count - 1;
|
||||||
|
|
||||||
// Save the first result
|
// Save the first result
|
||||||
execution_times.push(res.execution_time);
|
times_real.push(res.time_real);
|
||||||
all_succeeded = all_succeeded && res.success;
|
times_user.push(res.time_user);
|
||||||
|
times_system.push(res.time_system);
|
||||||
|
|
||||||
|
all_succeeded = all_succeeded && success;
|
||||||
|
|
||||||
// Re-configure the progress bar
|
// Re-configure the progress bar
|
||||||
progress_bar.set_length(count_remaining);
|
progress_bar.set_length(count_remaining);
|
||||||
@@ -168,25 +196,32 @@ pub fn run_benchmark(
|
|||||||
run_preparation_command();
|
run_preparation_command();
|
||||||
|
|
||||||
let msg = {
|
let msg = {
|
||||||
let mean = format_duration(mean(&execution_times), None);
|
let mean = format_duration(mean(×_real), None);
|
||||||
format!("Current estimate: {}", Green.paint(mean))
|
format!("Current estimate: {}", Green.paint(mean))
|
||||||
};
|
};
|
||||||
progress_bar.set_message(&msg);
|
progress_bar.set_message(&msg);
|
||||||
|
|
||||||
let res = time_shell_command(cmd, options.failure_action, Some(shell_spawning_time))?;
|
let (res, success) =
|
||||||
|
time_shell_command(cmd, options.failure_action, Some(shell_spawning_time))?;
|
||||||
|
|
||||||
execution_times.push(res.execution_time);
|
times_real.push(res.time_real);
|
||||||
all_succeeded = all_succeeded && res.success;
|
times_user.push(res.time_user);
|
||||||
|
times_system.push(res.time_system);
|
||||||
|
|
||||||
|
all_succeeded = all_succeeded && success;
|
||||||
|
|
||||||
progress_bar.inc(1);
|
progress_bar.inc(1);
|
||||||
}
|
}
|
||||||
progress_bar.finish_and_clear();
|
progress_bar.finish_and_clear();
|
||||||
|
|
||||||
// Compute statistical quantities
|
// Compute statistical quantities
|
||||||
let t_mean = mean(&execution_times);
|
let t_mean = mean(×_real);
|
||||||
let t_stddev = standard_deviation(&execution_times, Some(t_mean));
|
let t_stddev = standard_deviation(×_real, Some(t_mean));
|
||||||
let t_min = min(&execution_times);
|
let t_min = min(×_real);
|
||||||
let t_max = max(&execution_times);
|
let t_max = max(×_real);
|
||||||
|
|
||||||
|
let user_mean = mean(×_user);
|
||||||
|
let system_mean = mean(×_system);
|
||||||
|
|
||||||
// Formatting and console output
|
// Formatting and console output
|
||||||
let (mean_str, unit_mean) = format_duration_unit(t_mean, None);
|
let (mean_str, unit_mean) = format_duration_unit(t_mean, None);
|
||||||
@@ -194,12 +229,17 @@ pub fn run_benchmark(
|
|||||||
let min_str = format_duration(t_min, Some(unit_mean));
|
let min_str = format_duration(t_min, Some(unit_mean));
|
||||||
let max_str = format_duration(t_max, Some(unit_mean));
|
let max_str = format_duration(t_max, Some(unit_mean));
|
||||||
|
|
||||||
|
let (user_str, user_unit) = format_duration_unit(user_mean, None);
|
||||||
|
let system_str = format_duration(system_mean, Some(user_unit));
|
||||||
|
|
||||||
println!(
|
println!(
|
||||||
" Time ({} ± {}): {} ± {}",
|
" Time ({} ± {}): {} ± {} [User: {}, System: {}]",
|
||||||
Green.bold().paint("mean"),
|
Green.bold().paint("mean"),
|
||||||
Green.paint("σ"),
|
Green.paint("σ"),
|
||||||
Green.bold().paint(mean_str),
|
Green.bold().paint(mean_str),
|
||||||
Green.paint(stddev_str)
|
Green.paint(stddev_str),
|
||||||
|
Blue.paint(user_str),
|
||||||
|
Blue.paint(system_str)
|
||||||
);
|
);
|
||||||
println!(" ");
|
println!(" ");
|
||||||
|
|
||||||
@@ -215,7 +255,7 @@ pub fn run_benchmark(
|
|||||||
let mut warnings = vec![];
|
let mut warnings = vec![];
|
||||||
|
|
||||||
// Check execution time
|
// Check execution time
|
||||||
if execution_times.iter().any(|&t| t < MIN_EXECUTION_TIME) {
|
if times_real.iter().any(|&t| t < MIN_EXECUTION_TIME) {
|
||||||
warnings.push(Warnings::FastExecutionTime);
|
warnings.push(Warnings::FastExecutionTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
use libc::{getrusage, rusage, RUSAGE_CHILDREN};
|
||||||
|
use std::mem;
|
||||||
|
|
||||||
|
use hyperfine::internal::Second;
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct CPUTimes {
|
||||||
|
/// Total amount of time spent executing in user mode
|
||||||
|
user_usec: i64,
|
||||||
|
|
||||||
|
/// Total amount of time spent executing in kernel mode
|
||||||
|
system_usec: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct CPUInterval {
|
||||||
|
/// Total amount of time spent executing in user mode
|
||||||
|
pub user: Second,
|
||||||
|
|
||||||
|
/// Total amount of time spent executing in kernel mode
|
||||||
|
pub system: Second,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read CPU execution times (user and system time) from kernel
|
||||||
|
pub fn get_cpu_times() -> CPUTimes {
|
||||||
|
let result: rusage = unsafe {
|
||||||
|
let mut buf = mem::zeroed();
|
||||||
|
let success = getrusage(RUSAGE_CHILDREN, &mut buf);
|
||||||
|
assert_eq!(0, success);
|
||||||
|
buf
|
||||||
|
};
|
||||||
|
|
||||||
|
const USEC_PER_SEC: i64 = 1000 * 1000;
|
||||||
|
|
||||||
|
CPUTimes {
|
||||||
|
user_usec: i64::from(result.ru_utime.tv_sec) * USEC_PER_SEC
|
||||||
|
+ i64::from(result.ru_utime.tv_usec),
|
||||||
|
system_usec: i64::from(result.ru_stime.tv_sec) * USEC_PER_SEC
|
||||||
|
+ i64::from(result.ru_stime.tv_usec),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compute the time intervals in between two CPUTimes snapshots
|
||||||
|
pub fn get_cpu_interval(start: &CPUTimes, end: &CPUTimes) -> CPUInterval {
|
||||||
|
CPUInterval {
|
||||||
|
user: ((end.user_usec - start.user_usec) as f64) * 1e-6,
|
||||||
|
system: ((end.system_usec - start.system_usec) as f64) * 1e-6,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
pub mod benchmark;
|
pub mod benchmark;
|
||||||
pub mod format;
|
pub mod format;
|
||||||
pub mod internal;
|
pub mod internal;
|
||||||
|
pub mod cputime;
|
||||||
@@ -2,6 +2,7 @@ extern crate ansi_term;
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate clap;
|
extern crate clap;
|
||||||
extern crate indicatif;
|
extern crate indicatif;
|
||||||
|
extern crate libc;
|
||||||
extern crate statistical;
|
extern crate statistical;
|
||||||
|
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
|
|||||||
Reference in new issue
Block a user