Add export manager and CSV export capability

This commit is contained in:
Steve Pentland authored and David Peter committed 2018-03-19 07:53:10 +01:00
1 parent ab4dbe998e
commit a91912f322
7 files changed
+260 -3

No files matched your search

+7
View File
@@ -11,6 +11,7 @@ use hyperfine::outlier_detection::{modified_zscores, OUTLIER_THRESHOLD};
use hyperfine::timer::{TimerStart, TimerStop};
use hyperfine::timer::wallclocktimer::WallClockTimer;
use hyperfine::shell::execute_and_time;
use hyperfine::export::{ExportEntry, ExportManager};
/// Results from timing a single shell command
#[derive(Debug, Default, Copy, Clone)]
@@ -136,6 +137,7 @@ pub fn run_benchmark(
cmd: &str,
shell_spawning_time: TimingResult,
options: &HyperfineOptions,
exporter: &mut Option<Box<ExportManager>>,
) -> io::Result<()> {
println!(
"{}{}: {}",
@@ -265,6 +267,11 @@ pub fn run_benchmark(
// Warnings
let mut warnings = vec![];
if let &mut Some(ref mut mgr) = exporter {
let entry = ExportEntry::new(cmd.to_string(), t_mean, t_stddev, user_mean, system_mean);
mgr.add_result(entry);
}
// Check execution time
if times_real.iter().any(|&t| t < MIN_EXECUTION_TIME) {
warnings.push(Warnings::FastExecutionTime);
+32
View File
@@ -0,0 +1,32 @@
use super::{ExportEntry, ResultExporter};
use csv::WriterBuilder;
use std::io::Result;
pub struct CsvExporter {
results: Vec<ExportEntry>,
out_file: String,
}
impl ResultExporter for CsvExporter {
fn add_entry(&mut self, entry: ExportEntry) {
self.results.push(entry);
}
fn write(&self) -> Result<()> {
let mut writer = WriterBuilder::new().from_path(&self.out_file)?;
for res in &self.results {
writer.serialize(res)?;
}
Ok(())
}
}
impl CsvExporter {
pub fn new(file_name: String) -> Self {
CsvExporter {
results: Vec::new(),
out_file: file_name,
}
}
}
+96
View File
@@ -0,0 +1,96 @@
mod csv;
use self::csv::CsvExporter;
use std::io::Result;
/// The ExportEntry is the main set of values that will
/// be exported to files when requested.
#[derive(Debug, Default, Clone, Serialize)]
pub struct ExportEntry {
/// The command that was run
command: String,
/// The mean run time
mean: f64,
/// The standard deviation of all run times
stddev: f64,
/// Time spend in user space
user: f64,
/// Time spent in system space
system: f64,
}
impl ExportEntry {
/// Create a new ExportEntry with the given values
pub fn new(command: String, mean: f64, stddev: f64, user: f64, system: f64) -> Self {
ExportEntry {
command,
mean,
stddev,
user,
system,
}
}
}
/// The ResultExportType enum is used to denote the desired form
/// of exporter to use for a given file.
pub enum ResultExportType {
/// Export to a csv file with the provided name
Csv(String),
}
/// A ResultExporter is responsible for writing all results to the
/// appropriate file
pub trait ResultExporter {
/// Add an additional entry to the exporter for writing
fn add_entry(&mut self, entry: ExportEntry);
/// Write all entries to the target destination
fn write(&self) -> Result<()>;
}
/// The ExportManager handles coordination of multiple ResultExporters
pub trait ExportManager {
/// Add a new exporter to this manager for the given type
fn add_exporter(&mut self, for_type: &ResultExportType);
/// Add a new result to all exporters contained in the manager
fn add_result(&mut self, result: ExportEntry);
/// Trigger writes from all exporters
fn write_results(&self) -> Result<()>;
}
/// Create a new ExportManager
pub fn create_export_manager() -> Box<ExportManager> {
Box::new(Exporter {
exporters: Vec::new(),
})
}
/// The Exporter is the internal implementation of the ExportManager
struct Exporter {
exporters: Vec<Box<ResultExporter>>,
}
impl ExportManager for Exporter {
fn add_exporter(&mut self, for_type: &ResultExportType) {
match for_type {
&ResultExportType::Csv(ref file_name) => self.exporters
.push(Box::from(CsvExporter::new(file_name.clone()))),
};
}
fn add_result(&mut self, result: ExportEntry) {
for exp in self.exporters.iter_mut() {
exp.add_entry(result.clone());
}
}
fn write_results(&self) -> Result<()> {
for exp in &self.exporters {
exp.write()?;
}
Ok(())
}
}
+1
View File
@@ -5,3 +5,4 @@ pub mod outlier_detection;
pub mod warnings;
pub mod timer;
pub mod shell;
pub mod export;
+36 -3
View File
@@ -6,7 +6,12 @@ extern crate cfg_if;
#[macro_use]
extern crate clap;
extern crate colored;
extern crate csv;
extern crate indicatif;
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate statistical;
cfg_if! {
@@ -33,6 +38,7 @@ mod hyperfine;
use hyperfine::internal::{CmdFailureAction, HyperfineOptions, OutputStyleOption};
use hyperfine::benchmark::{mean_shell_spawning_time, run_benchmark};
use hyperfine::export::{create_export_manager, ExportManager, ResultExportType};
/// Print error message to stderr and terminate
pub fn error(message: &str) -> ! {
@@ -41,12 +47,16 @@ pub fn error(message: &str) -> ! {
}
/// Runs the benchmark for the given commands
fn run(commands: &Vec<&str>, options: &HyperfineOptions) -> io::Result<()> {
fn run(
commands: &Vec<&str>,
options: &HyperfineOptions,
exporter: &mut Option<Box<ExportManager>>,
) -> io::Result<()> {
let shell_spawning_time = mean_shell_spawning_time(&options.output_style)?;
// Run the benchmarks
for (num, cmd) in commands.iter().enumerate() {
run_benchmark(num, cmd, shell_spawning_time, options)?;
run_benchmark(num, cmd, shell_spawning_time, options, exporter)?;
}
Ok(())
@@ -129,6 +139,13 @@ fn main() {
.short("i")
.help("Ignore non-zero exit codes."),
)
.arg(
Arg::with_name("export-csv")
.long("export-csv")
.takes_value(true)
.value_name("FILE")
.help("Export the timing results to the given file in csv format."),
)
.help_message("Print this help message.")
.version_message("Show version information.")
.get_matches();
@@ -158,6 +175,18 @@ fn main() {
},
};
// Initial impl since we're only doing csv files, expand once we have multiple
// export types. Simplest probably to do the same for each, but check for
// None manager on later additions (JSON, Markdown, etc.)
let mut export_manager = match matches.value_of("export-csv") {
Some(filename) => {
let mut export_manager = create_export_manager();
export_manager.add_exporter(&ResultExportType::Csv(filename.to_string()));
Some(export_manager)
}
None => None,
};
// We default Windows to NoColor if full had been specified.
if cfg!(windows) && options.output_style == OutputStyleOption::Full {
options.output_style = OutputStyleOption::NoColor;
@@ -172,9 +201,13 @@ fn main() {
}
let commands = matches.values_of("command").unwrap().collect();
let res = run(&commands, &options);
let res = run(&commands, &options, &mut export_manager);
if let Err(e) = res {
error(e.description());
}
if let Some(exporter) = export_manager {
exporter.write_results().unwrap();
}
}