extern crate humantime;
use std::f64;
use std::fmt::{self,Display,Formatter};
use std::time::*;
const ITER_SCALE_FACTOR: f64 = 1.1;
const BENCH_TIME_LIMIT_SECS: u64 = 1;
#[derive(Debug, PartialEq, Clone)]
pub struct Stats {
pub ns_per_iter: f64,
pub goodness_of_fit: f64,
pub iterations: usize,
pub samples: usize,
}
impl Display for Stats {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
if self.ns_per_iter.is_nan() {
write!(f, "Only generated {} sample(s) - we can't fit a regression line to that! \
Try making your benchmark faster.", self.samples)
} else {
let per_iter: humantime::Duration = Duration::from_nanos(self.ns_per_iter as u64).into();
let per_iter = format!("{}", per_iter);
write!(f, "{:>11} (R²={:.3}, {} iterations in {} samples)",
per_iter, self.goodness_of_fit, self.iterations, self.samples)
}
}
}
pub fn bench<F, O>(f: F) -> Stats where F: Fn() -> O {
bench_env((), |_| f() )
}
pub fn bench_env<F, I, O>(env: I, f: F) -> Stats where F: Fn(&mut I) -> O, I: Clone {
let mut data = Vec::new();
let bench_start = Instant::now();
while bench_start.elapsed() < Duration::from_secs(BENCH_TIME_LIMIT_SECS) {
let iters = ITER_SCALE_FACTOR.powi(data.len() as i32).round() as usize;
let mut xs = vec![env.clone();iters]; let iter_start = Instant::now(); for i in 0..iters {
let ref mut x = xs[i]; pretend_to_use(f(x)); }
let time = iter_start.elapsed();
data.push((iters, time));
}
data.remove(0);
let (grad, r2) = regression(&data[..]);
Stats {
ns_per_iter: grad,
goodness_of_fit: r2,
iterations: data.iter().map(|&(x,_)| x).sum(),
samples: data.len(),
}
}
fn regression(data: &[(usize, Duration)]) -> (f64, f64) {
if data.len() < 2 {
return (f64::NAN, f64::NAN);
}
let data: Vec<(u64, u64)> = data.iter().map(|&(x,y)| (x as u64, as_nanos(y))).collect();
let n = data.len() as f64;
let nxbar = data.iter().map(|&(x,_)| x ).sum::<u64>(); let nybar = data.iter().map(|&(_,y)| y ).sum::<u64>(); let nxxbar = data.iter().map(|&(x,_)| x*x).sum::<u64>(); let nyybar = data.iter().map(|&(_,y)| y*y).sum::<u64>(); let nxybar = data.iter().map(|&(x,y)| x*y).sum::<u64>();
let ncovar = nxybar as f64 - ((nxbar * nybar) as f64 / n);
let nxvar = nxxbar as f64 - ((nxbar * nxbar) as f64 / n);
let nyvar = nyybar as f64 - ((nybar * nybar) as f64 / n);
let gradient = ncovar / nxvar;
let r2 = (ncovar * ncovar) / (nxvar * nyvar);
(gradient, r2)
}
fn as_nanos(x: Duration) -> u64 {
x.as_secs().checked_mul(1_000_000_000).expect("overflow: Duration was longer than 584 years")
.checked_add(x.subsec_nanos() as u64).unwrap()
}
fn pretend_to_use<T>(dummy: T) -> T {
unsafe {
let ret = ::std::ptr::read_volatile(&dummy);
::std::mem::forget(dummy);
ret
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::thread;
use std::time::Duration;
fn fib(n: usize) -> usize {
let mut i = 0; let mut sum = 0; let mut last = 0; let mut curr = 1usize;
while i < n - 1 {
sum = curr.wrapping_add(last);
last = curr;
curr = sum;
i += 1;
}
sum
}
#[test] #[ignore]
fn doctests_again() {
println!();
println!("fib 200: {}", bench(|| fib(200) ));
println!("fib 500: {}", bench(|| fib(500) ));
println!("reverse: {}", bench_env(vec![0;100], |xs| xs.reverse()));
println!("sort: {}", bench_env(vec![0;100], |xs| xs.sort()));
println!("fib 1: {}", bench(|| fib(500) ));
println!("fib 2: {}", bench(|| { fib(500); } ));
println!("fib 3: {}", bench_env(0, |x| { *x = fib(500); } ));
}
#[test]
fn very_quick() {
println!();
println!("very quick: {}", bench(|| {}));
}
#[test]
fn very_slow() {
println!();
println!("very slow: {}", bench(|| thread::sleep(Duration::from_millis(400))));
}
#[test]
fn test_sleep() {
println!();
println!("sleep 1 ms: {}", bench(|| thread::sleep(Duration::from_millis(1))));
}
#[test]
fn noop() {
println!();
println!("noop base: {}", bench( | | {}));
println!("noop 0: {}", bench_env(vec![0u64;0], |_| {}));
println!("noop 16: {}", bench_env(vec![0u64;16], |_| {}));
println!("noop 64: {}", bench_env(vec![0u64;64], |_| {}));
println!("noop 256: {}", bench_env(vec![0u64;256], |_| {}));
println!("noop 512: {}", bench_env(vec![0u64;512], |_| {}));
}
#[test]
fn ret_value() {
println!();
println!("no ret 32: {}", bench_env(vec![0u64;32], |x| { x.clone() }));
println!("return 32: {}", bench_env(vec![0u64;32], |x| x.clone()));
println!("no ret 256: {}", bench_env(vec![0u64;256], |x| { x.clone() }));
println!("return 256: {}", bench_env(vec![0u64;256], |x| x.clone()));
println!("no ret 1024: {}", bench_env(vec![0u64;1024], |x| { x.clone() }));
println!("return 1024: {}", bench_env(vec![0u64;1024], |x| x.clone()));
println!("no ret 4096: {}", bench_env(vec![0u64;4096], |x| { x.clone() }));
println!("return 4096: {}", bench_env(vec![0u64;4096], |x| x.clone()));
println!("no ret 50000: {}", bench_env(vec![0u64;50000], |x| { x.clone() }));
println!("return 50000: {}", bench_env(vec![0u64;50000], |x| x.clone()));
}
}