From 5e0089aedb61d4883df243d0b772868531b09ea6 Mon Sep 17 00:00:00 2001 From: Trivernis Date: Fri, 3 Apr 2020 08:46:04 +0200 Subject: [PATCH] Add BenchVec as a better way to store and process benchmark durations --- .idea/discord.xml | 9 ++++ src/main.rs | 120 +++++++++++++++++++++++++++++++++------------- 2 files changed, 97 insertions(+), 32 deletions(-) create mode 100644 .idea/discord.xml diff --git a/.idea/discord.xml b/.idea/discord.xml new file mode 100644 index 0000000..59b11d1 --- /dev/null +++ b/.idea/discord.xml @@ -0,0 +1,9 @@ + + + + + + + \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index eb58caf..896267f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,35 +1,104 @@ #![feature(test)] +#![allow(dead_code)] extern crate test; use rayon::prelude::*; -use std::ops::{Add, Div}; +use std::fmt::{self, Display}; use std::time::{Duration, Instant}; use termion::{color, style}; +#[derive(Debug, Clone)] +struct BenchVec { + pub inner: Vec, +} + +/// A struct that stores a vector of Durations for benchmarks +/// and allows some statistical operations on it +impl BenchVec { + /// Creates a new empty BenchVec + pub fn new() -> Self { + Self { inner: Vec::new() } + } + + /// Creates a BenchVec from an existing vector of Durations + pub fn from_vec(vec: &Vec) -> Self { + Self { inner: vec.clone() } + } + + /// Adds an element to the BenchVec + pub fn push(&mut self, item: Duration) -> &mut Self { + self.inner.push(item); + + self + } + + /// Appends a different BenchVec to this one + pub fn append(&mut self, other: Self) -> &mut Self { + self.inner.append(&mut other.inner.clone()); + + self + } + + /// Returns the length of stored elements + pub fn len(&self) -> usize { + self.inner.len() + } + + /// Returns the sum of all stored elements + pub fn sum(&self) -> Duration { + self.inner.par_iter().sum::() + } + + /// Returns the average of all durations + pub fn average(&self) -> Duration { + self.sum() / self.inner.len() as u32 + } + + /// Returns the standard deviation of all durations + pub fn standard_deviation(&self) -> f64 { + (self.sum().as_nanos() as f64 / (self.len() as f64 - 1f64)).sqrt() + } +} + +impl Display for BenchVec { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let avg_duration = self.average(); + let standard_deviation = self.standard_deviation(); + write!( + f, + "Average Duration: {:?} (±{:.2}ns ~ {:.1}%)", + avg_duration, + standard_deviation, + (standard_deviation / avg_duration.as_nanos() as f64) * 100f64 + ) + } +} const BENCHMARK_ITERATIONS: usize = 1000; pub fn main() { bench_function("Spawn and Stop thread", || { to_test::start_stop_thread(); }); - bench_function("MPSC channel", || { + bench_function("MPSC channel 1000 u128", || { to_test::send_mpsc_channel(); }); - bench_function("MPMC channel", || { + bench_function("MPMC channel 1000 u128", || { to_test::send_mpmc_channel(); }); bench_function("Multiply to 100", || { to_test::multiply_to(100); }); - bench_function("Largest prime", || { - to_test::largest_prime(1000000); + bench_function("Largest prime until 10000000", || { + to_test::largest_prime(10000000); }); - bench_function("Largest prime parallel", || { - to_test::largest_prime_par(1000000); + bench_function("Largest prime parallel until 10000000", || { + to_test::largest_prime_par(10000000); }) } +/// Benchmarks a closure a specific number of times +/// and reports the results to the commandline fn bench_function(name: &str, func: F) { println!( "\n{}{}{}{}", @@ -38,39 +107,26 @@ fn bench_function(name: &str, func: F) { name, style::Reset ); - let (avg_duration, durations) = bench_n_times(BENCHMARK_ITERATIONS, func); - if durations.len() > 10 { - println!("Durations(10):\t {:?}...", &durations[0..10]); + let bench_durations = bench_n_times(BENCHMARK_ITERATIONS, func); + if bench_durations.len() > 10 { + println!("Durations(10):\t {:?}...", &bench_durations.inner[0..10]); } else { - println!("Durations:\t {:?}", durations); + println!("Durations:\t {:?}", bench_durations.inner); } - let standard_deviation = (durations.par_iter().sum::().as_nanos() as f64 - / (BENCHMARK_ITERATIONS as f64 - 1f64)) - .sqrt(); - println!( - "Average Duration: {:?} (±{:.2}ns ~ {:.1}%)", - avg_duration, - standard_deviation, - (standard_deviation / avg_duration.as_nanos() as f64) * 100f64 - ); + println!("{}", bench_durations); } -fn bench_n_times(n: usize, func: F) -> (Duration, Vec) { - let mut durations: Vec = Vec::new(); +/// Benchmarks a closure a given number of times and returns the +/// average duration as well as all measured durations +fn bench_n_times(n: usize, func: F) -> BenchVec { + let mut durations = BenchVec::new(); for _ in 0..n { let start = Instant::now(); func(); durations.push(start.elapsed()); } - ( - durations - .par_iter() - .cloned() - .reduce_with(|a, b| a.add(b).div(2)) - .unwrap(), - durations, - ) + durations } mod to_test { @@ -133,7 +189,7 @@ mod to_test { } pub fn send_mpsc_channel() { - let (rx, tx) = channel::(); + let (rx, tx) = channel::(); let handle = thread::spawn(move || for _ in tx {}); for i in 0..1000 { rx.send(i).unwrap(); @@ -143,7 +199,7 @@ mod to_test { } pub fn send_mpmc_channel() { - let (rx, tx) = unbounded::(); + let (rx, tx) = unbounded::(); let handle = thread::spawn(move || for _ in tx {}); for i in 0..1000 { rx.send(i).unwrap();