blob: 0887c68e9281ee22a7a2219111aa9428c8147d00 [file] [log] [blame]
use std::fmt;
use std::io::BufReader;
use std::marker::PhantomData;
use std::process::{Child, ChildStderr, ChildStdin, ChildStdout, Command, Stdio};
use std::time::{Duration, Instant};
use routine::Routine;
use DurationExt;
// A two-way channel to the standard streams of a child process
pub struct Program {
buffer: String,
stdin: ChildStdin,
// NB Don't move the `stdin` field, because it must be dropped first
_child: Child,
stderr: ChildStderr,
stdout: BufReader<ChildStdout>,
}
impl Program {
pub fn spawn(cmd: &mut Command) -> Program {
cmd.stderr(Stdio::piped());
cmd.stdin(Stdio::piped());
cmd.stdout(Stdio::piped());
let mut child = match cmd.spawn() {
Err(e) => panic!("`{:?}`: {}", cmd, e),
Ok(child) => child,
};
Program {
buffer: String::new(),
stderr: child.stderr.take().unwrap(),
stdin: child.stdin.take().unwrap(),
stdout: BufReader::new(child.stdout.take().unwrap()),
_child: child,
}
}
pub fn send<T>(&mut self, line: T) -> &mut Program
where
T: fmt::Display,
{
use std::io::Write;
match writeln!(&mut self.stdin, "{}", line) {
Err(e) => panic!("`write into child stdin`: {}", e),
Ok(_) => self,
}
}
pub fn recv(&mut self) -> &str {
use std::io::{BufRead, Read};
self.buffer.clear();
match self.stdout.read_line(&mut self.buffer) {
Err(e) => {
self.buffer.clear();
match self.stderr.read_to_string(&mut self.buffer) {
Err(e) => {
panic!("`read from child stderr`: {}", e);
}
Ok(_) => {
println!("stderr:\n{}", self.buffer);
}
}
panic!("`read from child stdout`: {}", e);
}
Ok(_) => &self.buffer,
}
}
fn bench(&mut self, iters: &[u64]) -> Vec<f64> {
let mut n = 0;
for iters in iters {
self.send(iters);
n += 1;
}
(0..n)
.map(|_| {
let msg = self.recv();
let msg = msg.trim();
let elapsed: u64 = msg.parse().expect("Couldn't parse program output");
elapsed as f64
})
.collect()
}
fn warm_up(&mut self, how_long_ns: Duration) -> (u64, u64) {
let mut iters = 1;
let mut total_iters = 0;
let start = Instant::now();
loop {
self.send(iters).recv();
total_iters += iters;
let elapsed = start.elapsed();
if elapsed > how_long_ns {
return (elapsed.to_nanos(), total_iters);
}
iters *= 2;
}
}
}
impl Routine<()> for Command {
fn start(&mut self, _: &()) -> Option<Program> {
Some(Program::spawn(self))
}
fn bench(&mut self, program: &mut Option<Program>, iters: &[u64], _: &()) -> Vec<f64> {
let program = program.as_mut().unwrap();
program.bench(iters)
}
fn warm_up(
&mut self,
program: &mut Option<Program>,
how_long_ns: Duration,
_: &(),
) -> (u64, u64) {
let program = program.as_mut().unwrap();
program.warm_up(how_long_ns)
}
}
pub struct CommandFactory<F, T>
where
F: FnMut(&T) -> Command + 'static,
{
f: F,
_phantom: PhantomData<T>,
}
impl<F, T> CommandFactory<F, T>
where
F: FnMut(&T) -> Command + 'static,
{
pub fn new(f: F) -> CommandFactory<F, T> {
CommandFactory {
f,
_phantom: PhantomData,
}
}
}
impl<F, T> Routine<T> for CommandFactory<F, T>
where
F: FnMut(&T) -> Command + 'static,
{
fn start(&mut self, parameter: &T) -> Option<Program> {
let mut command = (self.f)(parameter);
Some(Program::spawn(&mut command))
}
fn bench(&mut self, program: &mut Option<Program>, iters: &[u64], _: &T) -> Vec<f64> {
let program = program.as_mut().unwrap();
program.bench(iters)
}
fn warm_up(
&mut self,
program: &mut Option<Program>,
how_long_ns: Duration,
_: &T,
) -> (u64, u64) {
let program = program.as_mut().unwrap();
program.warm_up(how_long_ns)
}
}