From 103a493e2259e396923e44f2f289f18fb2bc4f52 Mon Sep 17 00:00:00 2001 From: Olivier Giniaux Date: Sat, 4 Nov 2023 15:42:10 +0100 Subject: [PATCH] Add csv and markdown exports in benchmarks --- Cargo.toml | 9 +- .../{throughput2.rs => throughput/main.rs} | 39 ++++---- benches/throughput/result_processor.rs | 88 +++++++++++++++++++ ...{throughput.rs => throughput_criterion.rs} | 0 src/gxhash/platform/arm_128.rs | 3 +- src/gxhash/platform/x86_128.rs | 3 +- src/gxhash/platform/x86_256.rs | 3 +- 7 files changed, 115 insertions(+), 30 deletions(-) rename benches/{throughput2.rs => throughput/main.rs} (76%) create mode 100644 benches/throughput/result_processor.rs rename benches/{throughput.rs => throughput_criterion.rs} (100%) diff --git a/Cargo.toml b/Cargo.toml index c3875ca..486989f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "gxhash" author = "Olivier Giniaux" -version = "0.1.0" +version = "1.0.0" edition = "2021" [features] @@ -9,6 +9,9 @@ edition = "2021" # Please not however that the 256-bit GxHash and the 128-bit GxHash don't generate the same hashes for a same input. # Requires AVX2 and VAES (X86). avx2 = [] +# Only relevant for throughput benchmarks +bench-csv = [] +bench-md = [] [dependencies] rand = "0.8" @@ -24,11 +27,11 @@ twox-hash = "1.6.3" highway = "1.1.0" [[bench]] -name = "throughput2" +name = "throughput" harness = false [[bench]] -name = "throughput" +name = "throughput_criterion" harness = false [[bench]] diff --git a/benches/throughput2.rs b/benches/throughput/main.rs similarity index 76% rename from benches/throughput2.rs rename to benches/throughput/main.rs index bc9b2be..357333e 100644 --- a/benches/throughput2.rs +++ b/benches/throughput/main.rs @@ -1,3 +1,9 @@ +include!("../fnv.rs"); + +mod result_processor; + +use result_processor::*; + use std::hint::black_box; use std::time::{Instant, Duration}; use std::alloc::{alloc, dealloc, Layout}; @@ -6,7 +12,6 @@ use std::slice; use rand::Rng; use gxhash::*; -mod fnv; const ITERATIONS: u32 = 1000; const MAX_RUN_DURATION: Duration = Duration::from_millis(500); @@ -23,57 +28,49 @@ fn main() { // Fill with random bytes rng.fill(slice); - print!("Input size (bytes), "); - for i in 2.. { - let len = usize::pow(2, i); - if len > slice.len() { - break; - } - print!("{}, ", len); - } - println!(); + let mut processor = ResultProcessor::default(); // GxHash let algo_name = if cfg!(feature = "avx2") { "gxhash-avx2" } else { "gxhash" }; - benchmark(slice, algo_name, |data: &[u8], seed: i32| -> u64 { + benchmark(&mut processor, slice, algo_name, |data: &[u8], seed: i32| -> u64 { gxhash64(data, seed) }); // AHash let ahash_hasher = ahash::RandomState::with_seeds(0, 0, 0, 0); - benchmark(slice, "ahash", |data: &[u8], _: i32| -> u64 { + benchmark(&mut processor, slice, "ahash", |data: &[u8], _: i32| -> u64 { ahash_hasher.hash_one(data) }); // T1ha0 - benchmark(slice, "t1ha0", |data: &[u8], seed: i32| -> u64 { + benchmark(&mut processor, slice, "t1ha0", |data: &[u8], seed: i32| -> u64 { t1ha::t1ha0(data, seed as u64) }); // XxHash (twox-hash) - benchmark(slice, "xxhash", |data: &[u8], seed: i32| -> u64 { + benchmark(&mut processor, slice, "xxhash", |data: &[u8], seed: i32| -> u64 { twox_hash::xxh3::hash64_with_seed(data, seed as u64) }); // HighwayHash - benchmark(slice, "highwayhash", |data: &[u8], _: i32| -> u64 { + benchmark(&mut processor, slice, "highwayhash", |data: &[u8], _: i32| -> u64 { use highway::{HighwayHasher, HighwayHash}; HighwayHasher::default().hash64(data) }); // FNV-1a - benchmark(slice, "fnv-1a", |data: &[u8], seed: i32| -> u64 { - fnv::fnv_hash(data, seed as u64) + benchmark(&mut processor, slice, "fnv-1a", |data: &[u8], seed: i32| -> u64 { + fnv_hash(data, seed as u64) }); // Free benchmark data unsafe { dealloc(ptr, layout) }; } -fn benchmark(data: &[u8], name: &str, delegate: F) +fn benchmark(processor: &mut ResultProcessor, data: &[u8], name: &str, delegate: F) where F: Fn(&[u8], i32) -> u64 { - print!("{}, ", name); + processor.on_start(name); for i in 2.. { let len = usize::pow(2, i); if len > data.len() { @@ -100,9 +97,9 @@ fn benchmark(data: &[u8], name: &str, delegate: F) } let throughput = (len as f64) / (1024f64 * 1024f64 * (total_duration.as_secs_f64() / runs as f64 / ITERATIONS as f64)); - print!("{:.2}, ", throughput); + processor.on_result(len, throughput); } - println!(); + processor.on_end(); } #[inline(never)] diff --git a/benches/throughput/result_processor.rs b/benches/throughput/result_processor.rs new file mode 100644 index 0000000..88a4faf --- /dev/null +++ b/benches/throughput/result_processor.rs @@ -0,0 +1,88 @@ +#[cfg(feature = "bench-csv")] +#[derive(Default)] +pub struct ResultProcessor { + header_written: bool, + row_data: Vec, + header_data: Vec, +} + +#[cfg(feature = "bench-csv")] +impl ResultProcessor { + pub 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) { + 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) { + if !self.header_written { + println!("{}", self.header_data.join(", ")); + self.header_written = true; + } + println!("{}", self.row_data.join(", ")); + self.row_data.clear(); + } +} + +#[cfg(feature = "bench-md")] +#[derive(Default)] +pub struct ResultProcessor { + header_written: bool, + row_data: Vec, + header_data: Vec, +} + +#[cfg(feature = "bench-md")] +impl ResultProcessor { + pub 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) { + 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) { + if !self.header_written { + println!("| {} |", self.header_data.join(" | ")); + let separator: Vec = self.header_data.iter().map(|_| "---".to_string()).collect(); + println!("|{}|", separator.join("|")); + self.header_written = true; + } + println!("| {} |", self.row_data.join(" | ")); + self.row_data.clear(); + } +} + +#[cfg(all(not(feature = "bench-csv"),not(feature = "bench-md")))] +#[derive(Default)] +pub struct ResultProcessor; + +#[cfg(all(not(feature = "bench-csv"),not(feature = "bench-md")))] +impl ResultProcessor { + pub 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); + } + + pub fn on_end(&mut self) { + println!(); + } +} \ No newline at end of file diff --git a/benches/throughput.rs b/benches/throughput_criterion.rs similarity index 100% rename from benches/throughput.rs rename to benches/throughput_criterion.rs diff --git a/src/gxhash/platform/arm_128.rs b/src/gxhash/platform/arm_128.rs index 754a762..134d2c7 100644 --- a/src/gxhash/platform/arm_128.rs +++ b/src/gxhash/platform/arm_128.rs @@ -110,8 +110,7 @@ pub unsafe fn finalize(hash: State, seed: i32) -> State { // 3 rounds of AES let mut hash = ReinterpretUnion{ int8: hash }.uint8; - //hash = aes_encrypt(hash, ReinterpretUnion{ int32: vdupq_n_s32(seed) }.uint8); - hash = vaddq_u8(hash, ReinterpretUnion{ int32: vdupq_n_s32(seed) }.uint8); + hash = aes_encrypt(hash, ReinterpretUnion{ int32: vdupq_n_s32(seed) }.uint8); hash = aes_encrypt(hash, ReinterpretUnion{ uint32: keys_1 }.uint8); hash = aes_encrypt(hash, ReinterpretUnion{ uint32: keys_2 }.uint8); hash = aes_encrypt_last(hash, ReinterpretUnion{ uint32: keys_3 }.uint8); diff --git a/src/gxhash/platform/x86_128.rs b/src/gxhash/platform/x86_128.rs index 996e750..727ddca 100644 --- a/src/gxhash/platform/x86_128.rs +++ b/src/gxhash/platform/x86_128.rs @@ -82,8 +82,7 @@ pub unsafe fn finalize(hash: State, seed: i32) -> State { let keys_3 = _mm_set_epi32(0xC78B122B, 0x5544B1B7, 0x689D2B7D, 0xD0012E32); // 4 rounds of AES - //let mut hash = _mm_aesenc_si128(hash, _mm_set1_epi32(seed)); - let mut hash = _mm_add_epi8(hash, _mm_set1_epi32(seed)); + let mut hash = _mm_aesenc_si128(hash, _mm_set1_epi32(seed)); hash = _mm_aesenc_si128(hash, keys_1); hash = _mm_aesenc_si128(hash, keys_2); hash = _mm_aesenclast_si128(hash, keys_3); diff --git a/src/gxhash/platform/x86_256.rs b/src/gxhash/platform/x86_256.rs index 14bd217..adee595 100644 --- a/src/gxhash/platform/x86_256.rs +++ b/src/gxhash/platform/x86_256.rs @@ -82,8 +82,7 @@ pub unsafe fn finalize(hash: State, seed: i32) -> State { let keys_3 = _mm256_set_epi32(0xC78B122B, 0x5544B1B7, 0x689D2B7D, 0xD0012E32, 0xE2784542, 0x4155EE07, 0xC897CCE2, 0x780BF2C2); // 4 rounds of AES - //let mut hash = _mm256_aesenc_epi128(hash, _mm256_set1_epi32(seed)); - let mut hash = _mm256_add_epi8(hash, _mm256_set1_epi32(seed)); + let mut hash = _mm256_aesenc_epi128(hash, _mm256_set1_epi32(seed)); hash = _mm256_aesenc_epi128(hash, keys_1); hash = _mm256_aesenc_epi128(hash, keys_2); hash = _mm256_aesenclast_epi128(hash, keys_3);