#![allow(dead_code, unreachable_pub)] use spreadsheet_ods::{OdsError, WorkBook}; use std::fmt::{Display, Formatter}; use std::fs; use std::hint::black_box; use std::io::{stdout, Write}; use std::path::Path; use std::time::Instant; pub fn init_test() -> Result<(), OdsError> { fs::create_dir_all("test_out")?; Ok(()) } pub fn test_write_ods>(book: &mut WorkBook, ods_path: P) -> Result<(), OdsError> { fs::create_dir_all("test_out")?; spreadsheet_ods::write_ods(book, ods_path) } pub fn test_write_odsbuf(book: &mut WorkBook) -> Result, OdsError> { fs::create_dir_all("test_out")?; spreadsheet_ods::write_ods_buf(book, Vec::new()) } #[derive(Clone, Copy, Debug)] pub enum Unit { Nanosecond, Microsecond, Millisecond, Second, } impl Unit { pub fn conv(&self, nanos: f64) -> f64 { match self { Unit::Nanosecond => nanos, Unit::Microsecond => nanos / 1000.0, Unit::Millisecond => nanos / 1000000.0, Unit::Second => nanos / 1000000000.0, } } } impl Display for Unit { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { let v = match self { Unit::Nanosecond => "ns", Unit::Microsecond => "µs", Unit::Millisecond => "ms", Unit::Second => "s", }; write!(f, "{}", v) } } #[derive(Clone, Debug)] pub struct Timing { pub name: String, pub skip: usize, pub runs: usize, pub divider: u64, pub unit: Unit, /// samples in ns. already divided by divider. pub samples: Vec, pub extra: Vec, } impl Timing { pub fn name>(mut self, name: S) -> Self { self.name = name.into(); self } pub fn skip(mut self, skip: usize) -> Self { self.skip = skip; self } pub fn runs(mut self, runs: usize) -> Self { self.runs = runs; self } pub fn divider(mut self, divider: u64) -> Self { self.divider = divider; self } pub fn unit(mut self, unit: Unit) -> Self { self.unit = unit; self } pub fn run_pp( &mut self, mut fun: impl FnMut() -> Result, mut sum: impl FnMut(Result, &mut Vec, &mut Vec) -> Result, ) -> Result { assert!(self.runs > 0); assert!(self.divider > 0); print!("run {} ", self.name); let mut bench = move || { let now = Instant::now(); let result = fun(); (now.elapsed(), result) }; let mut sub_sample = Vec::new(); let mut sub_extra = Vec::new(); let mut n = 0; let mut result = loop { let (elapsed, result) = black_box(bench()); n += 1; if n >= self.skip { let sample = elapsed.as_nanos() as f64 / self.divider as f64; sub_sample.push(sample); } if n >= self.skip + self.runs { break result; } let d = 10usize.pow(n.ilog10()); if n % d == 0 { print!("."); } let _ = stdout().flush(); }; result = sum(result, &mut sub_sample, &mut sub_extra); self.samples.extend(sub_sample); self.extra.extend(sub_extra); println!(); result } pub fn run_nf(&mut self, mut fun: impl FnMut()) { let _ = self.run_pp::<(), ()>( || { fun(); Ok(()) }, |v, _s, _x| v, ); } pub fn run(&mut self, fun: impl FnMut() -> Result) -> Result { self.run_pp(fun, |v, _s, _x| v) } pub fn n(&self) -> usize { self.samples.len() } pub fn sum(&self) -> f64 { self.samples.iter().sum() } pub fn mean(&self) -> f64 { self.samples.iter().sum::() / self.samples.len() as f64 } pub fn median(&self) -> (f64, f64, f64) { let mut s = self.samples.clone(); s.sort_by(|v, w| v.total_cmp(w)); let m0 = s.len() * 1 / 10; let m5 = s.len() / 2; let m9 = s.len() * 9 / 10; (s[m0], s[m5], s[m9]) } pub fn lin_dev(&self) -> f64 { let mean = self.mean(); let lin_sum = self.samples.iter().map(|v| (*v - mean).abs()).sum::(); lin_sum / self.samples.len() as f64 } pub fn std_dev(&self) -> f64 { let mean = self.mean(); let std_sum = self .samples .iter() .map(|v| (*v - mean) * (*v - mean)) .sum::(); (std_sum / self.samples.len() as f64).sqrt() } } impl Default for Timing { fn default() -> Self { Self { name: "".to_string(), skip: 0, runs: 1, divider: 1, unit: Unit::Nanosecond, samples: vec![], extra: vec![], } } } impl Display for Timing { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { writeln!(f,)?; writeln!(f, "{}", self.name)?; writeln!(f,)?; writeln!( f, "| n | sum | 1/10 | median | 9/10 | mean | lin_dev | std_dev |" )?; writeln!(f, "|:---|:---|:---|:---|:---|:---|:---|:---|")?; let n = self.n(); let sum = self.sum(); let (m0, m5, m9) = self.median(); let mean = self.mean(); let lin = self.lin_dev(); let std = self.std_dev(); writeln!( f, "| {} | {:.2}{} | {:.2}{} | {:.2}{} | {:.2}{} | {:.2}{} | {:.2}{} | {:.2}{} |", n, self.unit.conv(sum), self.unit, self.unit.conv(m0), self.unit, self.unit.conv(m5), self.unit, self.unit.conv(m9), self.unit, self.unit.conv(mean), self.unit, self.unit.conv(lin), self.unit, self.unit.conv(std), self.unit, )?; writeln!(f,)?; if f.alternate() { writeln!(f,)?; writeln!(f, "{}", self.name)?; writeln!(f,)?; for i in 0..self.samples.len() { write!(f, "| {} ", i)?; } writeln!(f, "|")?; for _ in 0..self.samples.len() { write!(f, "|:---")?; } writeln!(f, "|")?; for e in &self.samples { write!(f, "| {:.2} ", e)?; } writeln!(f, "|")?; } Ok(()) } }