Add library content

master
Trivernis 5 years ago
commit 677f021610

2
.gitignore vendored

@ -0,0 +1,2 @@
/target
Cargo.lock

2
.idea/.gitignore vendored

@ -0,0 +1,2 @@
# Default ignored files
/workspace.xml

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="CPP_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/examples" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/benches" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

@ -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>

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptSettings">
<option name="languageLevel" value="ES6" />
</component>
</project>

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/benchlib-rs.iml" filepath="$PROJECT_DIR$/.idea/benchlib-rs.iml" />
</modules>
</component>
</project>

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

@ -0,0 +1,11 @@
[package]
name = "benchlib-rs"
version = "0.1.0"
authors = ["Trivernis <trivernis@gmail.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
rayon = "1.3.0"
termion = "1.5.5"

@ -0,0 +1,195 @@
use std::time::{Duration, Instant};
use std::fmt::{self, Display};
use rayon::prelude::*;
use termion::{color, style};
#[derive(Debug, Clone)]
pub 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()
}
/// Compares two benchmarks by calculating the average
pub fn compare(&self, other: Self) -> DurationDifference {
let avg1 = self.average();
let avg2 = other.average();
if avg1 > avg2 {
DurationDifference {
inner: avg1 - avg2,
positive: true,
}
} else {
DurationDifference {
inner: avg2 - avg1,
positive: false,
}
}
}
}
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,
"{:?} (±{:.2}ns ~ {:.2}%)",
avg_duration,
standard_deviation,
(standard_deviation / avg_duration.as_nanos() as f64) * 100f64
)
}
}
#[derive(Debug, Clone)]
pub struct DurationDifference {
pub inner: Duration,
pub positive: bool,
}
impl DurationDifference {
pub fn new(left: &BenchVec, right: &BenchVec) -> Self {
let left_avg = left.average();
let right_avg = right.average();
if left_avg > right_avg {
Self {
inner: left_avg - right_avg,
positive: true,
}
} else {
Self {
inner: right_avg - left_avg,
positive: false,
}
}
}
}
impl Display for DurationDifference {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}{:?}",
if self.positive { "+" } else { "-" },
self.inner
)
}
}
pub struct Bencher {
measurements: Vec<BenchVec>,
iterations: usize,
}
const MAX_AUTO_ITERATIONS: usize = 1000;
impl Bencher {
pub fn new() -> Self {
Self {
measurements: Vec::new(),
iterations: 100,
}
}
/// Sets the number of iterations a benchmark will be run
/// If set to 0 it iterates until the standard deviation is below 1%
pub fn set_iterations(&mut self, iterations: usize) -> &mut Self {
self.iterations = iterations;
self
}
/// Benchmarks a closure a configured number of times.
/// The result will be printed to the console with the given name.
pub fn bench<T, F: FnMut() -> T>(&mut self, name: &str, mut func: F) -> &mut Self {
let mut durations = BenchVec::new();
println!(
"\n{}{}{}{}",
color::Fg(color::LightBlue),
style::Bold,
name,
style::Reset
);
if self.iterations == 0 {
let mut count = 0;
while count < MAX_AUTO_ITERATIONS {
let start = Instant::now();
func();
durations.push(start.elapsed());
if (durations.standard_deviation() / durations.average().as_nanos() as f64) < 0.01 && count > 1{
break;
}
count += 1;
}
} else {
for _ in 0..self.iterations {
let start = Instant::now();
func();
durations.push(start.elapsed());
}
}
println!("Result: {}", durations);
self.measurements.push(durations);
self
}
/// Compares the last two benchmarks
/// If the number of benchmarks is below 2 it doesn't do anything
pub fn compare(&mut self) -> &mut Self {
if self.measurements.len() > 1 {
let left = self.measurements.get(self.measurements.len() - 1).unwrap();
let right = self.measurements.get(self.measurements.len() - 2).unwrap();
let diff = DurationDifference::new(left, right);
println!("Difference: {}", diff);
}
self
}
}

@ -0,0 +1,40 @@
pub mod benching;
#[cfg(test)]
mod tests {
use super::benching::Bencher;
#[test]
fn bencher_works() {
let mut bencher = Bencher::new();
let mut executed = false;
bencher.bench("lol", || executed = true);
assert!(executed)
}
#[test]
fn bench_iterations() {
let mut bencher = Bencher::new();
let mut count = 0;
bencher.set_iterations(243);
bencher.bench("lol", || count += 1);
assert_eq!(count, 243);
}
#[test]
fn bench_auto() {
let mut bencher = Bencher::new();
let mut count = 0;
bencher.set_iterations(0);
bencher.bench("lol", || count += 1);
assert!(count > 1);
}
#[test]
fn bench_difference() {
let mut bencher = Bencher::new();
bencher.bench("lol", || 3*4);
bencher.bench("lol2", || 35*4);
bencher.compare();
}
}
Loading…
Cancel
Save