Merge pull request #6321 from paritytech/plain_hasher
Optimized PlainHasher hashing. Trie insertions are >15% faster
This commit is contained in:
commit
c6b3faca64
8
Cargo.lock
generated
8
Cargo.lock
generated
@ -548,6 +548,7 @@ dependencies = [
|
||||
"bigint 4.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"heapsize 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"plain_hasher 0.1.0",
|
||||
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
@ -2214,6 +2215,13 @@ name = "pkg-config"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "plain_hasher"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "podio"
|
||||
version = "0.1.5"
|
||||
|
@ -13,6 +13,7 @@ rustc-hex = "1.0"
|
||||
rand = "0.3.12"
|
||||
libc = "0.2"
|
||||
heapsize = { version = "0.4", optional = true }
|
||||
plain_hasher = { path = "../plain_hasher" }
|
||||
|
||||
[features]
|
||||
x64asm_arithmetic=[]
|
||||
|
@ -16,6 +16,7 @@ use std::collections::{HashMap, HashSet};
|
||||
use rand::{Rand, Rng};
|
||||
use rand::os::OsRng;
|
||||
use rustc_hex::{FromHex, FromHexError};
|
||||
use plain_hasher::PlainHasher;
|
||||
use bigint::U256;
|
||||
use libc::{c_void, memcmp};
|
||||
|
||||
@ -446,41 +447,6 @@ impl_hash!(H2048, 256);
|
||||
known_heap_size!(0, H32, H64, H128, H160, H256, H264, H512, H520, H1024, H2048);
|
||||
// Specialized HashMap and HashSet
|
||||
|
||||
/// Hasher that just takes 8 bytes of the provided value.
|
||||
/// May only be used for keys which are 32 bytes.
|
||||
pub struct PlainHasher {
|
||||
prefix: [u8; 8],
|
||||
_marker: [u64; 0], // for alignment
|
||||
}
|
||||
|
||||
impl Default for PlainHasher {
|
||||
#[inline]
|
||||
fn default() -> PlainHasher {
|
||||
PlainHasher {
|
||||
prefix: [0; 8],
|
||||
_marker: [0; 0],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Hasher for PlainHasher {
|
||||
#[inline]
|
||||
fn finish(&self) -> u64 {
|
||||
unsafe { ::std::mem::transmute(self.prefix) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write(&mut self, bytes: &[u8]) {
|
||||
debug_assert!(bytes.len() == 32);
|
||||
|
||||
for quarter in bytes.chunks(8) {
|
||||
for (x, y) in self.prefix.iter_mut().zip(quarter) {
|
||||
*x ^= *y
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Specialized version of `HashMap` with H256 keys and fast hashing function.
|
||||
pub type H256FastMap<T> = HashMap<H256, T, BuildHasherDefault<PlainHasher>>;
|
||||
/// Specialized version of `HashSet` with H256 keys and fast hashing function.
|
||||
|
@ -14,6 +14,7 @@ extern crate rand;
|
||||
extern crate rustc_hex;
|
||||
extern crate bigint;
|
||||
extern crate libc;
|
||||
extern crate plain_hasher;
|
||||
|
||||
#[cfg(feature="heapsizeof")]
|
||||
#[macro_use]
|
||||
|
12
util/plain_hasher/Cargo.toml
Normal file
12
util/plain_hasher/Cargo.toml
Normal file
@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "plain_hasher"
|
||||
description = "Hasher for 32-bit keys."
|
||||
version = "0.1.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
license = "MIT"
|
||||
keywords = ["hash", "hasher"]
|
||||
categories = ["no-std"]
|
||||
homepage = "https://github.com/paritytech/plain_hasher"
|
||||
|
||||
[dependencies]
|
||||
crunchy = "0.1.6"
|
33
util/plain_hasher/benches/bench.rs
Normal file
33
util/plain_hasher/benches/bench.rs
Normal file
@ -0,0 +1,33 @@
|
||||
#![feature(test)]
|
||||
|
||||
extern crate test;
|
||||
extern crate plain_hasher;
|
||||
|
||||
use std::hash::Hasher;
|
||||
use std::collections::hash_map::DefaultHasher;
|
||||
use test::{Bencher, black_box};
|
||||
use plain_hasher::PlainHasher;
|
||||
|
||||
#[bench]
|
||||
fn write_plain_hasher(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
let n: u8 = black_box(100);
|
||||
(0..n).fold(PlainHasher::default(), |mut old, new| {
|
||||
let bb = black_box([new; 32]);
|
||||
old.write(&bb as &[u8]);
|
||||
old
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn write_default_hasher(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
let n: u8 = black_box(100);
|
||||
(0..n).fold(DefaultHasher::default(), |mut old, new| {
|
||||
let bb = black_box([new; 32]);
|
||||
old.write(&bb as &[u8]);
|
||||
old
|
||||
});
|
||||
});
|
||||
}
|
55
util/plain_hasher/src/lib.rs
Normal file
55
util/plain_hasher/src/lib.rs
Normal file
@ -0,0 +1,55 @@
|
||||
#![no_std]
|
||||
#[macro_use]
|
||||
extern crate crunchy;
|
||||
|
||||
use core::{hash, mem};
|
||||
|
||||
/// Hasher that just takes 8 bytes of the provided value.
|
||||
/// May only be used for keys which are 32 bytes.
|
||||
#[derive(Default)]
|
||||
pub struct PlainHasher {
|
||||
prefix: u64,
|
||||
}
|
||||
|
||||
impl hash::Hasher for PlainHasher {
|
||||
#[inline]
|
||||
fn finish(&self) -> u64 {
|
||||
self.prefix
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[allow(unused_assignments)]
|
||||
fn write(&mut self, bytes: &[u8]) {
|
||||
debug_assert!(bytes.len() == 32);
|
||||
|
||||
unsafe {
|
||||
let mut bytes_ptr = bytes.as_ptr();
|
||||
let prefix_u8: &mut [u8; 8] = mem::transmute(&mut self.prefix);
|
||||
let mut prefix_ptr = prefix_u8.as_mut_ptr();
|
||||
|
||||
unroll! {
|
||||
for _i in 0..8 {
|
||||
*prefix_ptr ^= (*bytes_ptr ^ *bytes_ptr.offset(8)) ^ (*bytes_ptr.offset(16) ^ *bytes_ptr.offset(24));
|
||||
|
||||
bytes_ptr = bytes_ptr.offset(1);
|
||||
prefix_ptr = prefix_ptr.offset(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use core::hash::Hasher;
|
||||
use super::PlainHasher;
|
||||
|
||||
#[test]
|
||||
fn it_works() {
|
||||
let mut bytes = [32u8; 32];
|
||||
bytes[0] = 15;
|
||||
let mut hasher = PlainHasher::default();
|
||||
hasher.write(&bytes);
|
||||
assert_eq!(hasher.prefix, 47);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user