Add BenchVec as a better way to store and process benchmark durations

master
Trivernis 5 years ago
parent 551b500f4e
commit 5e0089aedb

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DiscordProjectSettings">
<option name="show" value="true" />
</component>
<component name="ProjectNotificationSettings">
<option name="askShowProject" value="false" />
</component>
</project>

@ -1,35 +1,104 @@
#![feature(test)] #![feature(test)]
#![allow(dead_code)]
extern crate test; extern crate test;
use rayon::prelude::*; use rayon::prelude::*;
use std::ops::{Add, Div}; use std::fmt::{self, Display};
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use termion::{color, style}; use termion::{color, style};
#[derive(Debug, Clone)]
struct BenchVec {
pub inner: Vec<Duration>,
}
/// 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<Duration>) -> 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::<Duration>()
}
/// 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; const BENCHMARK_ITERATIONS: usize = 1000;
pub fn main() { pub fn main() {
bench_function("Spawn and Stop thread", || { bench_function("Spawn and Stop thread", || {
to_test::start_stop_thread(); to_test::start_stop_thread();
}); });
bench_function("MPSC channel", || { bench_function("MPSC channel 1000 u128", || {
to_test::send_mpsc_channel(); to_test::send_mpsc_channel();
}); });
bench_function("MPMC channel", || { bench_function("MPMC channel 1000 u128", || {
to_test::send_mpmc_channel(); to_test::send_mpmc_channel();
}); });
bench_function("Multiply to 100", || { bench_function("Multiply to 100", || {
to_test::multiply_to(100); to_test::multiply_to(100);
}); });
bench_function("Largest prime", || { bench_function("Largest prime until 10000000", || {
to_test::largest_prime(1000000); to_test::largest_prime(10000000);
}); });
bench_function("Largest prime parallel", || { bench_function("Largest prime parallel until 10000000", || {
to_test::largest_prime_par(1000000); to_test::largest_prime_par(10000000);
}) })
} }
/// Benchmarks a closure a specific number of times
/// and reports the results to the commandline
fn bench_function<F: Fn()>(name: &str, func: F) { fn bench_function<F: Fn()>(name: &str, func: F) {
println!( println!(
"\n{}{}{}{}", "\n{}{}{}{}",
@ -38,39 +107,26 @@ fn bench_function<F: Fn()>(name: &str, func: F) {
name, name,
style::Reset style::Reset
); );
let (avg_duration, durations) = bench_n_times(BENCHMARK_ITERATIONS, func); let bench_durations = bench_n_times(BENCHMARK_ITERATIONS, func);
if durations.len() > 10 { if bench_durations.len() > 10 {
println!("Durations(10):\t {:?}...", &durations[0..10]); println!("Durations(10):\t {:?}...", &bench_durations.inner[0..10]);
} else { } else {
println!("Durations:\t {:?}", durations); println!("Durations:\t {:?}", bench_durations.inner);
} }
let standard_deviation = (durations.par_iter().sum::<Duration>().as_nanos() as f64 println!("{}", bench_durations);
/ (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
);
} }
fn bench_n_times<F: Fn()>(n: usize, func: F) -> (Duration, Vec<Duration>) { /// Benchmarks a closure a given number of times and returns the
let mut durations: Vec<Duration> = Vec::new(); /// average duration as well as all measured durations
fn bench_n_times<F: Fn()>(n: usize, func: F) -> BenchVec {
let mut durations = BenchVec::new();
for _ in 0..n { for _ in 0..n {
let start = Instant::now(); let start = Instant::now();
func(); func();
durations.push(start.elapsed()); durations.push(start.elapsed());
} }
( durations
durations
.par_iter()
.cloned()
.reduce_with(|a, b| a.add(b).div(2))
.unwrap(),
durations,
)
} }
mod to_test { mod to_test {
@ -133,7 +189,7 @@ mod to_test {
} }
pub fn send_mpsc_channel() { pub fn send_mpsc_channel() {
let (rx, tx) = channel::<usize>(); let (rx, tx) = channel::<u128>();
let handle = thread::spawn(move || for _ in tx {}); let handle = thread::spawn(move || for _ in tx {});
for i in 0..1000 { for i in 0..1000 {
rx.send(i).unwrap(); rx.send(i).unwrap();
@ -143,7 +199,7 @@ mod to_test {
} }
pub fn send_mpmc_channel() { pub fn send_mpmc_channel() {
let (rx, tx) = unbounded::<usize>(); let (rx, tx) = unbounded::<u128>();
let handle = thread::spawn(move || for _ in tx {}); let handle = thread::spawn(move || for _ in tx {});
for i in 0..1000 { for i in 0..1000 {
rx.send(i).unwrap(); rx.send(i).unwrap();

Loading…
Cancel
Save