diff options
-rw-r--r-- | 2018/src/benchmark.rs | 78 | ||||
-rw-r--r-- | 2018/src/main.rs | 18 |
2 files changed, 82 insertions, 14 deletions
diff --git a/2018/src/benchmark.rs b/2018/src/benchmark.rs new file mode 100644 index 0000000..76de9e4 --- /dev/null +++ b/2018/src/benchmark.rs @@ -0,0 +1,78 @@ +use std::fmt; +use std::time::{Instant, Duration}; + +const MIN_DUR: f64 = 1.0; +const MIN_TIMES: usize = 4; + +pub struct Bench { + pub executions: usize, + pub duration: f64, + pub duration_stddev: f64, +} + +impl fmt::Display for Bench { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{} secs ± {} ({}%)", + self.duration, self.duration_stddev, self.duration_stddev / self.duration * 100.0) + } +} + +fn duration_secs(d: Duration) -> f64 { + d.as_secs() as f64 + d.subsec_nanos() as f64 / 1.0e9 +} + +// Returns estimate of single-iteration time +fn warmup<A, F: Fn() -> A>(func: F) -> f64 { + let mut ntimes = 1; + let mut time_taken = 0.0; + + while time_taken < 0.5 { + let start = Instant::now(); + for _ in 0..ntimes { + func(); + } + let end = Instant::now(); + time_taken = duration_secs(end - start); + + ntimes *= 2; + } + + time_taken / ntimes as f64 +} + +pub fn benchmark<A, F: Fn() -> A>(func: F) -> Bench { + let estimate = warmup(&func); + let ntimes_guess = if MIN_TIMES as f64 * estimate >= MIN_DUR { + MIN_TIMES + } else { + (MIN_DUR / estimate).ceil() as usize + }; + + let nparts = if ntimes_guess < 10 { + ntimes_guess + } else { + std::cmp::max(10, ntimes_guess / 10) + }; + let nperpart = (ntimes_guess + nparts - 1) / nparts; + + let ntimes = nparts * nperpart; + + // eprintln!("BENCH: estimate={} ntimes_guess={} nparts={} nperpart={} ntimes={}", estimate, ntimes_guess, nparts, nperpart, ntimes); + + let mut times = vec![0.0; nparts]; + + for i in 0..nparts { + let start = Instant::now(); + for _ in 0..nperpart { + func(); + } + let end = Instant::now(); + + times[i] = duration_secs(end - start); + } + + let mean = times.iter().map(|&t| t).sum::<f64>() / ntimes as f64; + let stddev = times.iter().map(|t| (t - mean) * (t - mean)).sum::<f64>() / (ntimes - 1) as f64; + + Bench { executions: 0, duration: mean, duration_stddev: stddev } +} diff --git a/2018/src/main.rs b/2018/src/main.rs index 447005e..3e44615 100644 --- a/2018/src/main.rs +++ b/2018/src/main.rs @@ -2,7 +2,6 @@ use std::io; use std::io::{BufRead, BufReader, Error, ErrorKind, Read}; use std::fs::File; use std::process::exit; -use std::time::Instant; use argparse::{ArgumentParser, StoreTrue, Store}; mod day1; @@ -19,6 +18,7 @@ mod day11; mod day12; mod day13; mod day14; +mod benchmark; static NUM_DAYS: i32 = 14; @@ -50,23 +50,13 @@ fn file_for_day(day: i32) -> io::Result<BufReader<File>> { } fn benchmark_day(day: i32) -> io::Result<f64> { - let num_iters = 100; - let mut input = Vec::new(); file_for_day(day)?.read_to_end(&mut input)?; - let start = Instant::now(); - for _i in 0..num_iters { - day_switch(day, BufReader::new(&input[..]))?; - } - let end = Instant::now(); - - let dur = (end - start) / num_iters; - let secs = dur.as_secs() as f64 + dur.subsec_micros() as f64 / 1000000.0; - - println!("Day {}: {} secs", day, secs); + let res = benchmark::benchmark(|| day_switch(day, BufReader::new(&input[..])).unwrap()); + println!("Day {}: {}", day, res); - Ok(secs) + Ok(res.duration) } fn benchmark_all_days() -> io::Result<()> { |