diff --git a/Cargo.toml b/Cargo.toml index d80856b..8fa6bcf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ahash" -version = "0.8.8" +version = "0.8.10" authors = ["Tom Kaitchuck "] license = "MIT OR Apache-2.0" description = "A non-cryptographic hash function using AES-NI for high performance" @@ -100,6 +100,7 @@ rand = "0.8.5" pcg-mwc = "0.2.1" serde_json = "1.0.59" hashbrown = "0.14.3" +smallvec = "1.13.1" [package.metadata.docs.rs] rustc-args = ["-C", "target-feature=+aes"] diff --git a/compare/Cargo.toml b/compare/Cargo.toml index 88bdf19..e638ae6 100644 --- a/compare/Cargo.toml +++ b/compare/Cargo.toml @@ -29,6 +29,9 @@ codegen-units = 1 [dependencies] ahash = { path = "../", default-features = false } +pcg-mwc = "0.2.1" +rand = "0.8.5" +rand_core = "0.6.4" [dev-dependencies] criterion = "0.3.3" diff --git a/compare/src/main.rs b/compare/src/main.rs new file mode 100644 index 0000000..b2a0b98 --- /dev/null +++ b/compare/src/main.rs @@ -0,0 +1,32 @@ +use std::io::Error; +use std::fs::File; +use std::io::Write; +use pcg_mwc::Mwc256XXA64; +use ahash::RandomState; +use std::io::BufWriter; +use std::path::Path; +use rand_core::SeedableRng; +use rand::Rng; +use std::time::Instant; + + +fn main() -> Result<(), Error> { + let mut r = Mwc256XXA64::seed_from_u64(0xe786_c22b_119c_1479); + + let path = Path::new("hash_output"); + + let mut file = BufWriter::new(File::create(path)?); + let hasher = RandomState::with_seeds(r.gen(), r.gen(), r.gen(), r.gen()); + let start = Instant::now(); + let mut sum: u64 = 0; + for i in 0..i32::MAX { + let value = hasher.hash_one(i as u64); + sum = sum.wrapping_add(value); + let value: [u8; 8] = value.to_ne_bytes(); + file.write_all(&value)?; + } + let elapsed = start.elapsed(); + println!("Sum {} Elapsed time: {}", sum, elapsed.as_millis()); + file.flush()?; + Ok(()) +} \ No newline at end of file diff --git a/src/aes_hash.rs b/src/aes_hash.rs index 7ad9af7..daf3ae4 100644 --- a/src/aes_hash.rs +++ b/src/aes_hash.rs @@ -225,8 +225,7 @@ pub(crate) struct AHasherU64 { impl Hasher for AHasherU64 { #[inline] fn finish(&self) -> u64 { - let rot = (self.pad & 63) as u32; - self.buffer.rotate_left(rot) + folded_multiply(self.buffer, self.pad) } #[inline] @@ -252,7 +251,6 @@ impl Hasher for AHasherU64 { #[inline] fn write_u64(&mut self, i: u64) { self.buffer = folded_multiply(i ^ self.buffer, MULTIPLE); - self.pad = self.pad.wrapping_add(i); } #[inline] diff --git a/src/fallback_hash.rs b/src/fallback_hash.rs index eb55479..bc5cbfe 100644 --- a/src/fallback_hash.rs +++ b/src/fallback_hash.rs @@ -56,8 +56,8 @@ impl AHasher { #[allow(dead_code)] // Is not called if non-fallback hash is used. pub(crate) fn from_random_state(rand_state: &RandomState) -> AHasher { AHasher { - buffer: rand_state.k0, - pad: rand_state.k1, + buffer: rand_state.k1, + pad: rand_state.k0, extra_keys: [rand_state.k2, rand_state.k3], } } @@ -117,7 +117,7 @@ impl AHasher { #[inline] #[cfg(feature = "specialize")] fn short_finish(&self) -> u64 { - self.buffer.wrapping_add(self.pad) + folded_multiply(self.buffer, self.pad) } } @@ -210,8 +210,8 @@ pub(crate) struct AHasherU64 { impl Hasher for AHasherU64 { #[inline] fn finish(&self) -> u64 { - let rot = (self.pad & 63) as u32; - self.buffer.rotate_left(rot) + folded_multiply(self.buffer, self.pad) + //self.buffer } #[inline] @@ -237,7 +237,6 @@ impl Hasher for AHasherU64 { #[inline] fn write_u64(&mut self, i: u64) { self.buffer = folded_multiply(i ^ self.buffer, MULTIPLE); - self.pad = self.pad.wrapping_add(i); } #[inline] diff --git a/src/hash_quality_test.rs b/src/hash_quality_test.rs index 43f2aeb..f2fab16 100644 --- a/src/hash_quality_test.rs +++ b/src/hash_quality_test.rs @@ -338,22 +338,24 @@ fn test_length_extension(hasher: impl Fn(u128, u128) -> T) { } fn test_sparse(hasher: impl Fn() -> T) { + use smallvec::SmallVec; + let mut buf = [0u8; 256]; let mut hashes = HashMap::new(); - for idx_1 in 0..256 { - for idx_2 in idx_1 + 1..256 { + for idx_1 in 0..255_u8 { + for idx_2 in idx_1 + 1..=255_u8 { for value_1 in [1, 2, 4, 8, 16, 32, 64, 128] { for value_2 in [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 15, 16, 17, 18, 20, 24, 31, 32, 33, 48, 64, 96, 127, 128, 129, 192, 254, 255, ] { - buf[idx_1] = value_1; - buf[idx_2] = value_2; + buf[idx_1 as usize] = value_1; + buf[idx_2 as usize] = value_2; let hash_value = hash_with(&buf, &mut hasher()); - let keys = hashes.entry(hash_value).or_insert(Vec::new()); - keys.push((idx_1, value_1, idx_2, value_2)); - buf[idx_1] = 0; - buf[idx_2] = 0; + let keys = hashes.entry(hash_value).or_insert(SmallVec::<[[u8; 4]; 1]>::new()); + keys.push([idx_1, value_1, idx_2, value_2]); + buf[idx_1 as usize] = 0; + buf[idx_2 as usize] = 0; } } } diff --git a/src/random_state.rs b/src/random_state.rs index 3ee629f..46a39ab 100644 --- a/src/random_state.rs +++ b/src/random_state.rs @@ -470,8 +470,8 @@ impl BuildHasherExt for RandomState { #[inline] fn hash_as_u64(&self, value: &T) -> u64 { let mut hasher = AHasherU64 { - buffer: self.k0, - pad: self.k1, + buffer: self.k1, + pad: self.k0, }; value.hash(&mut hasher); hasher.finish() diff --git a/tests/bench.rs b/tests/bench.rs index 59287ab..2d000c0 100644 --- a/tests/bench.rs +++ b/tests/bench.rs @@ -75,7 +75,7 @@ fn gen_strings() -> Vec { macro_rules! bench_inputs { ($group:ident, $hash:ident) => { // Number of iterations per batch should be high enough to hide timing overhead. - let size = BatchSize::NumIterations(2_000); + let size = BatchSize::NumIterations(50_000); let mut rng = rand::thread_rng(); $group.bench_function("u8", |b| b.iter_batched(|| rng.gen::(), |v| $hash(&v), size)); diff --git a/tests/map_tests.rs b/tests/map_tests.rs index 7849f4a..97fdbee 100644 --- a/tests/map_tests.rs +++ b/tests/map_tests.rs @@ -238,7 +238,7 @@ fn test_byte_dist() { let mut table: [bool; 256 * 8] = [false; 256 * 8]; let hasher = RandomState::with_seeds(r.gen(), r.gen(), r.gen(), r.gen()); for i in 0..128 { - let mut keys: [u8; 8] = hasher.hash_one(i as u64).to_ne_bytes(); + let mut keys: [u8; 8] = hasher.hash_one((i as u64) << 30).to_ne_bytes(); //let mut keys = r.next_u64().to_ne_bytes(); //This is a control to test assert sensitivity. for idx in 0..8 { while table[idx * 256 + keys[idx] as usize] {