use indicatif::ProgressBar;
use std::any::type_name;
use std::marker::PhantomData;
use std::thread;
use std::time::{Duration, Instant};
use std::fmt::Debug;
use num::integer::Roots;
use owo_colors::OwoColorize;
#[macro_export] macro_rules! quickbench {
($struct:ty, $threads:expr, $run:expr) => {
BenchMarker::<$struct>::new($threads, $run)
};
($name:ident, $struct:ty, $threads:expr, $run:expr) => {
let mut $name = BenchMarker::<$struct>::new($threads, $run);
$name.start();
println!("{}", $name);
};
}
type DC = DisplayCfg;
pub const DEFAULT:[DisplayCfg; 9] =
[DC::SysInfo, DC::Space,
DC::Mean, DC::Median, DC::Deviation, DC::Space,
DC::AbsMin, DC::AbsMax, DC::AbsDiff];
pub struct BenchMarker<T:Bench> {
phantom:PhantomData<T>,
time_table: Vec<Duration>,
pub max_threads: usize,
pub max_runcount: usize,
runtime:Duration,
pub display_config:Vec<DisplayCfg>
}
impl<T:Bench> BenchMarker<T> {
pub fn new(max_threads: usize, max_runcount: usize) -> Self {
Self {
phantom:PhantomData,
time_table: vec![],
max_threads,
max_runcount,
runtime:Duration::ZERO,
display_config:DEFAULT.to_vec()
}
}
pub fn start(&mut self) {
let bar = ProgressBar::new((self.max_runcount * self.max_threads) as u64);
bar.set_position(1);
let runtime = Instant::now();
thread::scope(|s| {
for _ in 0..self.max_runcount {
let mut scope_table = vec![];
for _ in 0..self.max_threads {
scope_table.push(s.spawn(|| {
let mut item = <T as Bench>::generate();
let time = Instant::now();
item.test();
time.elapsed()
}));
bar.inc(1);
}
for _ in 0..self.max_threads {
self.time_table.push(scope_table.pop().expect("Error joining scopes").join().expect("Error joining scopes"));
}
}
});
self.time_table.sort();
self.runtime += runtime.elapsed();
bar.finish();
}
}
impl<T:Bench + Debug> BenchMarker<T> {
pub fn debug(&mut self) {
let mut item = <T as Bench>::generate();
let name = type_name::<T>().split("::").last().unwrap_or("[parse_err]");
println!("Test Results for {}", name.green());
println!("{:?}", item.red());
let time = Instant::now();
item.test();
println!("{:?}\n{:?}", item.blue(), time.elapsed().yellow());
}
}
impl<T:Bench> std::fmt::Display for BenchMarker<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let name = type_name::<T>().split("::").last().unwrap_or("[parse_err]");
let mut sum = Duration::ZERO;
self.time_table.iter().for_each(|i| sum += *i);
let mean = sum / self.time_table.len() as u32;
let mut sum_table:Vec<i128> = vec![];
self.time_table.iter().for_each(|i| {sum_table.push(i.as_nanos() as i128 - mean.as_nanos() as i128)});
let sum:i128 = sum_table.iter().sum();
let i_dev = sum.pow(2) / self.time_table.len() as i128;
let deviation = Duration::from_nanos(i_dev.sqrt().try_into().unwrap_or(0));
let q1 = self.time_table[self.time_table.len()/4];
let median = self.time_table[self.time_table.len()/2];
let q3 = self.time_table[self.time_table.len()-(self.time_table.len()/4)];
let iqr = q3 - q1;
let first = *self.time_table.first().unwrap_or(&Duration::ZERO);
let q_min = q1-(iqr + (iqr/2));
let last = *self.time_table.last().unwrap_or(&Duration::ZERO);
let q_max = q3-(iqr + (iqr/2));
let range = last - first;
writeln!(f, "Benchmark Results for {}", name.green())?;
for item in self.display_config.iter() {
match item {
DisplayCfg::SysInfo => {
writeln!(f, "{}", format!(" threads used: {:?}", self.max_threads).cyan())?;
writeln!(f, "{}", format!(" total tests ran: {:?}", self.max_threads * self.max_runcount).cyan())?;
writeln!(f, "{}", format!(" total runtime: {:?}\n", self.runtime).cyan())?;
},
DisplayCfg::Mean => {
writeln!(f, " mean: {:?}", mean.yellow())?;
},
DisplayCfg::Median => {
writeln!(f, " median: {:?}", median)?;
},
DisplayCfg::Quartiles => {
writeln!(f, " Q1: {:?}", q1)?;
writeln!(f, " Q2: {:?}", median)?;
writeln!(f, " Q3: {:?}", q3)?;
},
DisplayCfg::Deviation => {
writeln!(f, " deviation: {:?}\n", deviation.magenta())?;
},
DisplayCfg::AbsMin => {
writeln!(f, " min: {:?}", first)?;
},
DisplayCfg::QuartileMin => {
writeln!(f, " quartile min: {:?}", q_min)?;
},
DisplayCfg::AbsMax => {
writeln!(f, " max: {:?}", last)?;
},
DisplayCfg::QuartileMax => {
writeln!(f, " quartile max: {:?}", q_max)?;
},
DisplayCfg::AbsDiff => {
writeln!(f, " diff: {:?}", range.magenta())?;
},
DisplayCfg::Space => {
writeln!(f)?;
},
}
}
writeln!(f)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum DisplayCfg {
SysInfo,
Mean,
Median,
Quartiles,
Deviation,
AbsMin,
QuartileMin,
AbsMax,
QuartileMax,
AbsDiff,
Space
}
pub trait Bench {
fn generate() -> Self;
fn test(&mut self);
}