From 9753eed85b2a6550b219e1afa019030246bd43dd Mon Sep 17 00:00:00 2001 From: Steve Pentland Date: Sun, 4 Feb 2018 20:19:24 -0500 Subject: [PATCH] Initial working version with child proc HANDLE. Still very ugly, and more work needs to be done to make it all pretty again. Additionally, cleaned up all the cfg attrs to shor version --- Cargo.toml | 6 ++--- src/hyperfine/benchmark.rs | 42 +++++++++++++++++++++++++++++---- src/hyperfine/shell.rs | 16 +++++++++---- src/hyperfine/timer/cputimer.rs | 25 +++++++++++++------- src/main.rs | 6 ++--- 5 files changed, 72 insertions(+), 23 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9645199..7354282 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,11 +15,11 @@ indicatif = "0.9" statistical = "0.1" atty = "0.2.2" -[target.'cfg(not(target_os = "windows"))'.dependencies] +[target.'cfg(not(windows))'.dependencies] libc = "0.2" -[target.'cfg(target_os = "windows")'.dependencies] -winapi = { version = "0.3.4", features = [ "processthreadsapi", "minwindef"] } +[target.'cfg(windows)'.dependencies] +winapi = { version = "0.3.4", features = ["processthreadsapi", "minwindef", "winnt"] } [dependencies.clap] version = "2" diff --git a/src/hyperfine/benchmark.rs b/src/hyperfine/benchmark.rs index a136a66..d2fe47f 100644 --- a/src/hyperfine/benchmark.rs +++ b/src/hyperfine/benchmark.rs @@ -1,4 +1,5 @@ use std::io; +use std::process::ExitStatus; use colored::*; use statistical::{mean, standard_deviation}; @@ -10,9 +11,10 @@ use hyperfine::format::{format_duration, format_duration_unit}; use hyperfine::outlier_detection::{modified_zscores, OUTLIER_THRESHOLD}; use hyperfine::timer::Timer; use hyperfine::timer::wallclocktimer::WallClockTimer; -use hyperfine::timer::cputimer::CPUTimer; +use hyperfine::timer::cputimer::get_process_times; use hyperfine::shell::run_shell_command; + /// Results from timing a single shell command #[derive(Debug, Copy, Clone)] pub struct TimingResult { @@ -42,12 +44,13 @@ pub fn time_shell_command( shell_spawning_time: Option, ) -> io::Result<(TimingResult, bool)> { let wallclock_timer = WallClockTimer::start(); - let cpu_timer = CPUTimer::start(); + // let cpu_timer = CPUTimer::start(); - let status = run_shell_command(shell_cmd)?; + let (mut time_user, mut time_system, status) = execute_and_time(shell_cmd)?; + // let status = run_shell_command(shell_cmd)?; let mut time_real = wallclock_timer.stop(); - let (mut time_user, mut time_system) = cpu_timer.stop(); + // let (mut time_user, mut time_system) = cpu_timer.stop(); if failure_action == CmdFailureAction::RaiseError && !status.success() { return Err(io::Error::new( @@ -291,3 +294,34 @@ pub fn run_benchmark( Ok(()) } + +#[cfg(windows)] +fn execute_and_time(command: &str) -> io::Result<(f64, f64, ExitStatus)> { + use std::os::windows::io::AsRawHandle; + + let mut child = run_shell_command(command)?; + + let result = child.wait()?; + let handle = child.as_raw_handle(); + + let (user_usec, system_usec) = get_process_times(handle); + + // Temp, bypassing timer and the stop method + let user = user_usec as f64 * 1e-6; + let system = system_usec as f64 * 1e-6; + + Ok((user, system, result)) +} + +#[cfg(not(windows))] +fn execute_and_time(command: &str) -> io::Result<(f64, f64, ExitStatus)> { + use hyperfine::timer::cputimer::CPUTimer; + + let cpu_timer = CPUTimer::start(); + + let status = run_shell_command(command)?; + + let (mut time_user, mut time_system) = cpu_timer.stop(); + + Ok((time_user, time_system, status)) +} diff --git a/src/hyperfine/shell.rs b/src/hyperfine/shell.rs index 4545e72..9e18d15 100644 --- a/src/hyperfine/shell.rs +++ b/src/hyperfine/shell.rs @@ -1,8 +1,14 @@ use std::io; -use std::process::{Command, ExitStatus, Stdio}; +use std::process::{Command, Stdio}; + +#[cfg(not(windows))] +use std::process::ExitStatus; + +#[cfg(windows)] +use std::process::Child; /// Run a standard shell command -#[cfg(not(target_os = "windows"))] +#[cfg(not(windows))] pub fn run_shell_command(command: &str) -> io::Result { Command::new("sh") .arg("-c") @@ -14,13 +20,13 @@ pub fn run_shell_command(command: &str) -> io::Result { } /// Run a Windows shell command using cmd.exe -#[cfg(target_os = "windows")] -pub fn run_shell_command(command: &str) -> io::Result { +#[cfg(windows)] +pub fn run_shell_command(command: &str) -> io::Result { Command::new("cmd") .arg("/C") .arg(command) .stdin(Stdio::null()) .stdout(Stdio::null()) .stderr(Stdio::null()) - .status() + .spawn() } diff --git a/src/hyperfine/timer/cputimer.rs b/src/hyperfine/timer/cputimer.rs index 7f5086c..851d286 100644 --- a/src/hyperfine/timer/cputimer.rs +++ b/src/hyperfine/timer/cputimer.rs @@ -1,6 +1,9 @@ use hyperfine::internal::Second; use hyperfine::timer::Timer; +#[cfg(windows)] +use std::os::windows::io::RawHandle; + #[derive(Debug, Copy, Clone)] struct CPUTimes { /// Total amount of time spent executing in user mode @@ -20,7 +23,7 @@ struct CPUInterval { } /// Read CPU execution times ('user' and 'system') -#[cfg(not(target_os = "windows"))] +#[cfg(not(windows))] fn get_cpu_times() -> CPUTimes { use libc::{getrusage, rusage, RUSAGE_CHILDREN}; use std::mem; @@ -45,8 +48,17 @@ fn get_cpu_times() -> CPUTimes { /// Read CPU execution times (dummy for now) #[cfg(windows)] fn get_cpu_times() -> CPUTimes { - use winapi::um::processthreadsapi::{GetCurrentProcess, GetProcessTimes}; + CPUTimes { + user_usec : 0, + system_usec: 0, + } +} + +#[cfg(windows)] +pub fn get_process_times(handle: RawHandle) -> (i64, i64) { + use winapi::um::processthreadsapi::GetProcessTimes; use winapi::shared::minwindef::FILETIME; + use winapi::um::winnt::HANDLE; // Winapi reports times as per 100 nanosecond const HUNDRED_NS_PER_MS: i64 = 10; @@ -69,9 +81,8 @@ fn get_cpu_times() -> CPUTimes { }; let (user_usec, system_usec) = unsafe { - let handle = GetCurrentProcess(); let res = GetProcessTimes( - handle, + handle as HANDLE, &mut _ctime, &mut _etime, &mut kernel_time, @@ -81,6 +92,7 @@ fn get_cpu_times() -> CPUTimes { // GetProcessTimes will exit with non-zero if success as per: https://msdn.microsoft.com/en-us/library/windows/desktop/ms683223(v=vs.85).aspx if res != 0 { // Extract times as laid out here: https://support.microsoft.com/en-us/help/188768/info-working-with-the-filetime-structure + // Both user_time and kernel_time are spans that the proces spent in either. let user: i64 = (((user_time.dwHighDateTime as i64) << 32) + user_time.dwLowDateTime as i64) / HUNDRED_NS_PER_MS; let kernel: i64 = (((kernel_time.dwHighDateTime as i64) << 32) + kernel_time.dwLowDateTime as i64) / HUNDRED_NS_PER_MS; (user, kernel) @@ -89,10 +101,7 @@ fn get_cpu_times() -> CPUTimes { } }; - CPUTimes { - user_usec, - system_usec, - } + (user_usec,system_usec) } /// Compute the time intervals in between two `CPUTimes` snapshots diff --git a/src/main.rs b/src/main.rs index 65e3b43..27e2542 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,10 +5,10 @@ extern crate colored; extern crate indicatif; extern crate statistical; -#[cfg(not(target_os = "windows"))] +#[cfg(not(windows))] extern crate libc; -#[cfg(target_os = "windows")] +#[cfg(windows)] extern crate winapi; #[cfg(test)] @@ -149,7 +149,7 @@ fn main() { OutputStyleOption::Basic }, }; - + if options.output_style == OutputStyleOption::Basic { colored::control::set_override(false); }