Skip to content

Commit

Permalink
Use trait for bench result processors
Browse files Browse the repository at this point in the history
  • Loading branch information
ogxd committed Nov 21, 2023
1 parent bbd613c commit ecbdd39
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 54 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/bench.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
- uses: actions/checkout@v4

- name: Benchmark
run: cargo bench --bench throughput
run: cargo bench --bench throughput --features bench-plot

- name: Commit & Push Plots
uses: stefanzweifel/git-auto-commit-action@v5
Expand All @@ -43,7 +43,7 @@ jobs:
fetch-depth: 2

- name: Benchmark
run: cargo bench --bench throughput
run: cargo bench --bench throughput --features bench-plot

- name: Commit & Push Plots
uses: stefanzweifel/git-auto-commit-action@v5
Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ avx2 = []
# Only relevant for throughput benchmarks
bench-csv = []
bench-md = []
bench-plot = []

[dependencies]
rand = "0.8"
Expand Down
30 changes: 19 additions & 11 deletions benches/throughput/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,56 +27,64 @@ fn main() {
// Fill with random bytes
rng.fill(slice);

let mut processor = ResultProcessor::default();
let mut processor: Box<dyn ResultProcessor> = if cfg!(feature = "bench-csv") {
Box::new(OutputCsv::default())
} else if cfg!(feature = "bench-md") {
Box::new(OutputMd::default())
} else if cfg!(feature = "bench-plot") {
Box::new(OutputPlot::default())
} else {
Box::new(OutputSimple::default())
};

// GxHash
benchmark(&mut processor, slice, "gxhash", |data: &[u8], seed: i64| -> u64 {
benchmark(processor.as_mut(), slice, "gxhash", |data: &[u8], seed: i64| -> u64 {
gxhash64(data, seed)
});

// GxHash-AVX2
if cfg!(feature = "avx2") {
benchmark(&mut processor, slice, "gxhash-avx2", |data: &[u8], seed: i64| -> u64 {
benchmark(processor.as_mut(), slice, "gxhash-avx2", |data: &[u8], seed: i64| -> u64 {
gxhash64(data, seed)
});
}

// XxHash (twox-hash)
benchmark(&mut processor, slice, "xxhash", |data: &[u8], seed: u64| -> u64 {
benchmark(processor.as_mut(), slice, "xxhash", |data: &[u8], seed: u64| -> u64 {
twox_hash::xxh3::hash64_with_seed(data, seed)
});

// AHash
let ahash_hasher = ahash::RandomState::with_seeds(0, 0, 0, 0);
benchmark(&mut processor, slice, "ahash", |data: &[u8], _: i32| -> u64 {
benchmark(processor.as_mut(), slice, "ahash", |data: &[u8], _: i32| -> u64 {
ahash_hasher.hash_one(data)
});

// T1ha0
benchmark(&mut processor, slice, "t1ha0", |data: &[u8], seed: u64| -> u64 {
benchmark(processor.as_mut(), slice, "t1ha0", |data: &[u8], seed: u64| -> u64 {
t1ha::t1ha0(data, seed)
});

// SeaHash
benchmark(&mut processor, slice, "seahash", |data: &[u8], seed: u64| -> u64 {
benchmark(processor.as_mut(), slice, "seahash", |data: &[u8], seed: u64| -> u64 {
seahash::hash_seeded(data, seed, 0, 0, 0)
});

// MetroHash
benchmark(&mut processor, slice, "metrohash", |data: &[u8], seed: i32| -> u64 {
benchmark(processor.as_mut(), slice, "metrohash", |data: &[u8], seed: i32| -> u64 {
let mut metrohash_hasher = metrohash::MetroHash64::with_seed(seed as u64);
metrohash_hasher.write(data);
metrohash_hasher.finish()
});

// HighwayHash
benchmark(&mut processor, slice, "highwayhash", |data: &[u8], _: i32| -> u64 {
benchmark(processor.as_mut(), slice, "highwayhash", |data: &[u8], _: i32| -> u64 {
use highway::{HighwayHasher, HighwayHash};
HighwayHasher::default().hash64(data)
});

// FNV-1a
benchmark(&mut processor, slice, "fnv-1a", |data: &[u8], seed: u64| -> u64 {
benchmark(processor.as_mut(), slice, "fnv-1a", |data: &[u8], seed: u64| -> u64 {
let mut fnv_hasher = fnv::FnvHasher::with_key(seed);
fnv_hasher.write(data);
fnv_hasher.finish()
Expand All @@ -88,7 +96,7 @@ fn main() {
unsafe { dealloc(ptr, layout) };
}

fn benchmark<F, S>(processor: &mut ResultProcessor, data: &[u8], name: &str, delegate: F)
fn benchmark<F, S>(processor: &mut dyn ResultProcessor, data: &[u8], name: &str, delegate: F)
where F: Fn(&[u8], S) -> u64, S: Default + TryFrom<u128> + TryInto<usize>
{
processor.on_start(name);
Expand Down
85 changes: 44 additions & 41 deletions benches/throughput/result_processor.rs
Original file line number Diff line number Diff line change
@@ -1,65 +1,69 @@
use gxhash::GxHashMap;
use plotters::prelude::*;

#[cfg(feature = "bench-csv")]
pub trait ResultProcessor {
fn on_start(&mut self, name: &str);
fn on_result(&mut self, input_size: usize, throughput: f64);
fn on_end(&mut self);
fn finish(&self);
}

#[derive(Default)]
pub struct ResultProcessor {
pub struct OutputCsv {
header_written: bool,
row_data: Vec<String>,
header_data: Vec<String>,
}

#[cfg(feature = "bench-csv")]
impl ResultProcessor {
pub fn on_start(&mut self, name: &str) {
impl ResultProcessor for OutputCsv {
fn on_start(&mut self, name: &str) {
self.row_data.push(name.to_string());
if !self.header_written {
self.header_data.push("Throughput (MiB/s)".to_string());
}
}

pub fn on_result(&mut self, input_size: usize, throughput: f64) {
fn on_result(&mut self, input_size: usize, throughput: f64) {
self.row_data.push(format!("{:.2}", throughput));
if !self.header_written {
self.header_data.push(input_size.to_string());
}
}

pub fn on_end(&mut self) {
fn on_end(&mut self) {
if !self.header_written {
println!("{}", self.header_data.join(", "));
self.header_written = true;
}
println!("{}", self.row_data.join(", "));
self.row_data.clear();
}

fn finish(&self) {}
}

#[cfg(feature = "bench-md")]
#[derive(Default)]
pub struct ResultProcessor {
pub struct OutputMd {
header_written: bool,
row_data: Vec<String>,
header_data: Vec<String>,
}

#[cfg(feature = "bench-md")]
impl ResultProcessor {
pub fn on_start(&mut self, name: &str) {
impl ResultProcessor for OutputMd {
fn on_start(&mut self, name: &str) {
self.row_data.push(name.to_string());
if !self.header_written {
self.header_data.push("Throughput (MiB/s)".to_string());
}
}

pub fn on_result(&mut self, input_size: usize, throughput: f64) {
fn on_result(&mut self, input_size: usize, throughput: f64) {
self.row_data.push(format!("{:.2}", throughput));
if !self.header_written {
self.header_data.push(input_size.to_string());
}
}

pub fn on_end(&mut self) {
fn on_end(&mut self) {
if !self.header_written {
println!("| {} |", self.header_data.join(" | "));
let separator: Vec<String> = self.header_data.iter().map(|_| "---".to_string()).collect();
Expand All @@ -69,53 +73,52 @@ impl ResultProcessor {
println!("| {} |", self.row_data.join(" | "));
self.row_data.clear();
}

fn finish(&self) {}
}

// #[cfg(all(not(feature = "bench-csv"), not(feature = "bench-md")))]
// #[derive(Default)]
// pub struct ResultProcessor;
#[derive(Default)]
pub struct OutputSimple;

// #[cfg(all(not(feature = "bench-csv"), not(feature = "bench-md")))]
// impl ResultProcessor {
// pub fn on_start(&mut self, name: &str) {
// println!("{}", name);
// }
impl ResultProcessor for OutputSimple {
fn on_start(&mut self, name: &str) {
println!("{}", name);
}

// pub fn on_result(&mut self, input_size: usize, throughput: f64) {
// println!(" | {} > {:.2}", input_size, throughput);
// }
fn on_result(&mut self, input_size: usize, throughput: f64) {
println!(" | {} > {:.2}", input_size, throughput);
}

// pub fn on_end(&mut self) {
// println!();
// }
// }
fn on_end(&mut self) {
println!();
}

fn finish(&self) {
println!("Finished");
}
}

//#[cfg(feature = "bench-plot")]
#[derive(Default)]
pub struct ResultProcessor {
pub struct OutputPlot {
series: Vec<(String, Vec<(usize, f64)>)>
}

//#[cfg(feature = "bench-plot")]
impl ResultProcessor {

pub fn on_start(&mut self, name: &str) {
impl ResultProcessor for OutputPlot {
fn on_start(&mut self, name: &str) {
println!("Started '{}'...", name);
self.series.push((name.to_string(), Vec::new()));
}

pub fn on_result(&mut self, input_size: usize, throughput: f64) {
fn on_result(&mut self, input_size: usize, throughput: f64) {
let len = self.series.len();
let serie = self.series.get_mut(len - 1).unwrap();

serie.1.push((input_size, throughput));
}

pub fn on_end(&mut self) {

}
fn on_end(&mut self) {}

pub fn finish(&self) {
fn finish(&self) {
let arch = std::env::consts::ARCH;
let file_name = format!("benches/throughput/{}.svg", arch);

Expand Down

0 comments on commit ecbdd39

Please sign in to comment.