Skip to content

Commit 95c19f4

Browse files
committed
Auto merge of #97 - Amanieu:ahash, r=Amanieu
Replace FxHash with AHash as the default hasher Fixes #48 cc @tkaitchuck
2 parents ed0b240 + 31fc802 commit 95c19f4

File tree

9 files changed

+106
-180
lines changed

9 files changed

+106
-180
lines changed

Cargo.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ edition = "2018"
1313
build = "build.rs"
1414

1515
[dependencies]
16+
# For the default hasher
17+
ahash = { version = "0.2", optional = true }
18+
1619
# For external trait impls
1720
rayon = { version = "1.0", optional = true }
1821
serde = { version = "1.0.25", default-features = false, optional = true }
@@ -34,7 +37,7 @@ serde_test = "1.0"
3437
doc-comment = "0.3.1"
3538

3639
[features]
37-
default = []
40+
default = ["ahash"]
3841
nightly = []
3942
rustc-internal-api = []
4043
rustc-dep-of-std = ["nightly", "core", "compiler_builtins", "alloc", "rustc-internal-api"]

README.md

Lines changed: 40 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -25,39 +25,57 @@ table implementation in the Rust standard library.
2525
## Features
2626

2727
- Drop-in replacement for the standard library `HashMap` and `HashSet` types.
28-
- Uses `FxHash` as the default hasher, which is much faster than SipHash.
29-
- Around 2x faster than `FxHashMap` and 8x faster than the standard `HashMap`.
28+
- Uses `AHash` as the default hasher, which is much faster than SipHash.
29+
- Around 2x faster than the previous standard library `HashMap`.
3030
- Lower memory usage: only 1 byte of overhead per entry instead of 8.
31-
- Compatible with `#[no_std]` (currently requires nightly for the `alloc` crate).
31+
- Compatible with `#[no_std]` (but requires a global allocator with the `alloc` crate).
3232
- Empty hash maps do not allocate any memory.
3333
- SIMD lookups to scan multiple hash entries in parallel.
3434

3535
## Performance
3636

37-
Compared to the previous implementation of `std::collections::HashMap`:
37+
Compared to the previous implementation of `std::collections::HashMap` (Rust 1.35).
38+
39+
With the hashbrown default AHash hasher:
3840

3941
```text
40-
name stdhash ns/iter hashbrown ns/iter diff ns/iter diff % speedup
41-
find_existing 23,831 2,935 -20,896 -87.68% x 8.12
42-
find_nonexisting 25,326 2,283 -23,043 -90.99% x 11.09
43-
get_remove_insert 124 25 -99 -79.84% x 4.96
44-
grow_by_insertion 197 177 -20 -10.15% x 1.11
45-
hashmap_as_queue 72 18 -54 -75.00% x 4.00
46-
new_drop 14 0 -14 -100.00% x inf
47-
new_insert_drop 78 55 -23 -29.49% x 1.42
42+
name oldstdhash ns/iter hashbrown ns/iter diff ns/iter diff % speedup
43+
insert_ahash_highbits 20,846 7,397 -13,449 -64.52% x 2.82
44+
insert_ahash_random 20,515 7,796 -12,719 -62.00% x 2.63
45+
insert_ahash_serial 21,668 7,264 -14,404 -66.48% x 2.98
46+
insert_erase_ahash_highbits 29,570 17,498 -12,072 -40.83% x 1.69
47+
insert_erase_ahash_random 39,569 17,474 -22,095 -55.84% x 2.26
48+
insert_erase_ahash_serial 32,073 17,332 -14,741 -45.96% x 1.85
49+
iter_ahash_highbits 1,572 2,087 515 32.76% x 0.75
50+
iter_ahash_random 1,609 2,074 465 28.90% x 0.78
51+
iter_ahash_serial 2,293 2,120 -173 -7.54% x 1.08
52+
lookup_ahash_highbits 3,460 4,403 943 27.25% x 0.79
53+
lookup_ahash_random 6,377 3,911 -2,466 -38.67% x 1.63
54+
lookup_ahash_serial 3,629 3,586 -43 -1.18% x 1.01
55+
lookup_fail_ahash_highbits 5,286 3,411 -1,875 -35.47% x 1.55
56+
lookup_fail_ahash_random 12,365 4,171 -8,194 -66.27% x 2.96
57+
lookup_fail_ahash_serial 4,902 3,240 -1,662 -33.90% x 1.51
4858
```
4959

50-
Compared to `rustc_hash::FxHashMap` (standard `HashMap` using `FxHash` instead of `SipHash`):
60+
With the libstd default SipHash hasher:
5161

5262
```text
53-
name fxhash ns/iter hashbrown ns/iter diff ns/iter diff % speedup
54-
find_existing 5,951 2,935 -3,016 -50.68% x 2.03
55-
find_nonexisting 4,637 2,283 -2,354 -50.77% x 2.03
56-
get_remove_insert 29 25 -4 -13.79% x 1.16
57-
grow_by_insertion 160 177 17 10.62% x 0.90
58-
hashmap_as_queue 22 18 -4 -18.18% x 1.22
59-
new_drop 9 0 -9 -100.00% x inf
60-
new_insert_drop 64 55 -9 -14.06% x 1.16
63+
name oldstdhash ns/iter hashbrown ns/iter diff ns/iter diff % speedup
64+
insert_std_highbits 32,598 20,199 -12,399 -38.04% x 1.61
65+
insert_std_random 29,824 20,760 -9,064 -30.39% x 1.44
66+
insert_std_serial 33,151 17,256 -15,895 -47.95% x 1.92
67+
insert_erase_std_highbits 74,731 48,735 -25,996 -34.79% x 1.53
68+
insert_erase_std_random 73,828 47,649 -26,179 -35.46% x 1.55
69+
insert_erase_std_serial 73,864 40,147 -33,717 -45.65% x 1.84
70+
iter_std_highbits 1,518 2,264 746 49.14% x 0.67
71+
iter_std_random 1,502 2,414 912 60.72% x 0.62
72+
iter_std_serial 6,361 2,118 -4,243 -66.70% x 3.00
73+
lookup_std_highbits 21,705 16,962 -4,743 -21.85% x 1.28
74+
lookup_std_random 21,654 17,158 -4,496 -20.76% x 1.26
75+
lookup_std_serial 18,726 14,509 -4,217 -22.52% x 1.29
76+
lookup_fail_std_highbits 25,852 17,323 -8,529 -32.99% x 1.49
77+
lookup_fail_std_random 25,913 17,760 -8,153 -31.46% x 1.46
78+
lookup_fail_std_serial 22,648 14,839 -7,809 -34.48% x 1.53
6179
```
6280

6381
## Usage
@@ -80,7 +98,7 @@ map.insert(1, "one");
8098

8199
This crate has the following Cargo features:
82100

83-
- `nightly`: Enables nightly-only features: `no_std` support and `#[may_dangle]`.
101+
- `nightly`: Enables nightly-only features: `#[may_dangle]`.
84102
- `serde`: Enables serde serialization support.
85103
- `rayon`: Enables rayon parallel iterator support.
86104

benches/bench.rs

Lines changed: 38 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// This benchmark suite contains some benchmarks along a set of dimensions:
2-
// Hasher: std default (SipHash) and crate default (FxHash).
2+
// Hasher: std default (SipHash) and crate default (AHash).
33
// Int key distribution: low bit heavy, top bit heavy, and random.
44
// Task: basic functionality: insert, insert_erase, lookup, lookup_fail, iter
55
#![feature(test)]
@@ -15,7 +15,7 @@ use std::collections::hash_map::RandomState;
1515
const SIZE: usize = 1000;
1616

1717
// The default hashmap when using this crate directly.
18-
type FxHashMap<K, V> = HashMap<K, V, DefaultHashBuilder>;
18+
type AHashMap<K, V> = HashMap<K, V, DefaultHashBuilder>;
1919
// This uses the hashmap from this crate with the default hasher of the stdlib.
2020
type StdHashMap<K, V> = HashMap<K, V, RandomState>;
2121

@@ -41,18 +41,22 @@ impl Iterator for RandomKeys {
4141
}
4242

4343
macro_rules! bench_suite {
44-
($bench_macro:ident, $bench_fx_serial:ident, $bench_std_serial:ident,
45-
$bench_fx_highbits:ident, $bench_std_highbits:ident,
46-
$bench_fx_random:ident, $bench_std_random:ident) => {
47-
$bench_macro!($bench_fx_serial, FxHashMap, 0..);
44+
($bench_macro:ident, $bench_ahash_serial:ident, $bench_std_serial:ident,
45+
$bench_ahash_highbits:ident, $bench_std_highbits:ident,
46+
$bench_ahash_random:ident, $bench_std_random:ident) => {
47+
$bench_macro!($bench_ahash_serial, AHashMap, 0..);
4848
$bench_macro!($bench_std_serial, StdHashMap, 0..);
49-
$bench_macro!($bench_fx_highbits, FxHashMap, (0..).map(usize::swap_bytes));
49+
$bench_macro!(
50+
$bench_ahash_highbits,
51+
AHashMap,
52+
(0..).map(usize::swap_bytes)
53+
);
5054
$bench_macro!(
5155
$bench_std_highbits,
5256
StdHashMap,
5357
(0..).map(usize::swap_bytes)
5458
);
55-
$bench_macro!($bench_fx_random, FxHashMap, RandomKeys::new());
59+
$bench_macro!($bench_ahash_random, AHashMap, RandomKeys::new());
5660
$bench_macro!($bench_std_random, StdHashMap, RandomKeys::new());
5761
};
5862
}
@@ -61,56 +65,60 @@ macro_rules! bench_insert {
6165
($name:ident, $maptype:ident, $keydist:expr) => {
6266
#[bench]
6367
fn $name(b: &mut Bencher) {
68+
let mut m = $maptype::with_capacity_and_hasher(SIZE, Default::default());
6469
b.iter(|| {
65-
let mut m = $maptype::default();
70+
m.clear();
6671
for i in ($keydist).take(SIZE) {
6772
m.insert(i, i);
6873
}
69-
black_box(m);
74+
black_box(&mut m);
7075
})
7176
}
7277
};
7378
}
7479

7580
bench_suite!(
7681
bench_insert,
77-
insert_fx_serial,
82+
insert_ahash_serial,
7883
insert_std_serial,
79-
insert_fx_highbits,
84+
insert_ahash_highbits,
8085
insert_std_highbits,
81-
insert_fx_random,
86+
insert_ahash_random,
8287
insert_std_random
8388
);
8489

8590
macro_rules! bench_insert_erase {
8691
($name:ident, $maptype:ident, $keydist:expr) => {
8792
#[bench]
8893
fn $name(b: &mut Bencher) {
89-
let mut m = $maptype::default();
90-
let mut add_iter = $keydist;
91-
for i in (&mut add_iter).take(SIZE) {
92-
m.insert(i, i);
94+
let mut base = $maptype::default();
95+
for i in ($keydist).take(SIZE) {
96+
base.insert(i, i);
9397
}
94-
let mut remove_iter = $keydist;
98+
let skip = $keydist.skip(SIZE);
9599
b.iter(|| {
100+
let mut m = base.clone();
101+
let mut add_iter = skip.clone();
102+
let mut remove_iter = $keydist;
96103
// While keeping the size constant,
97104
// replace the first keydist with the second.
98105
for (add, remove) in (&mut add_iter).zip(&mut remove_iter).take(SIZE) {
99106
m.insert(add, add);
100107
black_box(m.remove(&remove));
101108
}
109+
black_box(m);
102110
})
103111
}
104112
};
105113
}
106114

107115
bench_suite!(
108116
bench_insert_erase,
109-
insert_erase_fx_serial,
117+
insert_erase_ahash_serial,
110118
insert_erase_std_serial,
111-
insert_erase_fx_highbits,
119+
insert_erase_ahash_highbits,
112120
insert_erase_std_highbits,
113-
insert_erase_fx_random,
121+
insert_erase_ahash_random,
114122
insert_erase_std_random
115123
);
116124

@@ -134,11 +142,11 @@ macro_rules! bench_lookup {
134142

135143
bench_suite!(
136144
bench_lookup,
137-
lookup_fx_serial,
145+
lookup_ahash_serial,
138146
lookup_std_serial,
139-
lookup_fx_highbits,
147+
lookup_ahash_highbits,
140148
lookup_std_highbits,
141-
lookup_fx_random,
149+
lookup_ahash_random,
142150
lookup_std_random
143151
);
144152

@@ -163,11 +171,11 @@ macro_rules! bench_lookup_fail {
163171

164172
bench_suite!(
165173
bench_lookup_fail,
166-
lookup_fail_fx_serial,
174+
lookup_fail_ahash_serial,
167175
lookup_fail_std_serial,
168-
lookup_fail_fx_highbits,
176+
lookup_fail_ahash_highbits,
169177
lookup_fail_std_highbits,
170-
lookup_fail_fx_random,
178+
lookup_fail_ahash_random,
171179
lookup_fail_std_random
172180
);
173181

@@ -191,10 +199,10 @@ macro_rules! bench_iter {
191199

192200
bench_suite!(
193201
bench_iter,
194-
iter_fx_serial,
202+
iter_ahash_serial,
195203
iter_std_serial,
196-
iter_fx_highbits,
204+
iter_ahash_highbits,
197205
iter_std_highbits,
198-
iter_fx_random,
206+
iter_ahash_random,
199207
iter_std_random
200208
);

ci/run.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ fi
2121

2222
export RUSTFLAGS="$RUSTFLAGS --cfg hashbrown_deny_warnings"
2323

24+
# Make sure we can compile without the default hasher
25+
"${CARGO}" -vv check --target="${TARGET}" --no-default-features
26+
2427
"${CARGO}" -vv test --target="${TARGET}"
2528
"${CARGO}" -vv test --target="${TARGET}" --features "${FEATURES}"
2629

src/fx.rs

Lines changed: 0 additions & 119 deletions
This file was deleted.

0 commit comments

Comments
 (0)