-
Notifications
You must be signed in to change notification settings - Fork 294
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Auto merge of #83 - edre:bench, r=Amanieu
Add the std hasher and different key distributions to benchmarks. Also remove the i32/i64 duplication, as those differences were in the noise compared to the other dimensions. I would have liked to reduce some of the macro duplication, but it looks like that's still not easy. rust-lang/rust#29599
- Loading branch information
Showing
1 changed file
with
200 additions
and
116 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,150 +1,234 @@ | ||
// This benchmark suite contains some benchmarks along a set of dimensions: | ||
// Hasher: std default (SipHash) and crate default (FxHash). | ||
// Int key distribution: low bit heavy, top bit heavy, and random. | ||
// Task: basic functionality: insert, insert_erase, lookup, lookup_fail, iter | ||
#![feature(test)] | ||
|
||
extern crate test; | ||
|
||
use std::hash::Hash; | ||
use test::{black_box, Bencher}; | ||
|
||
use hashbrown::hash_map::DefaultHashBuilder; | ||
use hashbrown::HashMap; | ||
//use rustc_hash::FxHashMap as HashMap; | ||
//use std::collections::HashMap; | ||
use std::collections::hash_map::RandomState; | ||
|
||
fn new_map<K: Eq + Hash, V>() -> HashMap<K, V> { | ||
HashMap::default() | ||
} | ||
|
||
#[bench] | ||
fn insert_i32(b: &mut Bencher) { | ||
b.iter(|| { | ||
let mut m: HashMap<i32, i32> = new_map(); | ||
for i in 1..1001 { | ||
m.insert(i, i); | ||
} | ||
black_box(m); | ||
}) | ||
} | ||
const SIZE: usize = 1000; | ||
|
||
#[bench] | ||
fn insert_i64(b: &mut Bencher) { | ||
b.iter(|| { | ||
let mut m: HashMap<i64, i64> = new_map(); | ||
for i in 1..1001 { | ||
m.insert(i, i); | ||
} | ||
black_box(m); | ||
}) | ||
} | ||
// The default hashmap when using this crate directly. | ||
type FxHashMap<K, V> = HashMap<K, V, DefaultHashBuilder>; | ||
// This uses the hashmap from this crate with the default hasher of the stdlib. | ||
type StdHashMap<K, V> = HashMap<K, V, RandomState>; | ||
|
||
#[bench] | ||
fn insert_erase_i32(b: &mut Bencher) { | ||
b.iter(|| { | ||
let mut m: HashMap<i32, i32> = new_map(); | ||
for i in 1..1001 { | ||
m.insert(i, i); | ||
} | ||
black_box(&mut m); | ||
for i in 1..1001 { | ||
m.remove(&i); | ||
} | ||
black_box(m); | ||
}) | ||
// A random key iterator. | ||
#[derive(Clone, Copy)] | ||
struct RandomKeys { | ||
remaining: usize, | ||
state: usize, | ||
} | ||
|
||
#[bench] | ||
fn insert_erase_i64(b: &mut Bencher) { | ||
b.iter(|| { | ||
let mut m: HashMap<i64, i64> = new_map(); | ||
for i in 1..1001 { | ||
m.insert(i, i); | ||
} | ||
black_box(&mut m); | ||
for i in 1..1001 { | ||
m.remove(&i); | ||
impl RandomKeys { | ||
fn new(size: usize) -> Self { | ||
RandomKeys { | ||
remaining: size, | ||
state: 1, | ||
} | ||
black_box(m); | ||
}) | ||
} | ||
|
||
#[bench] | ||
fn lookup_i32(b: &mut Bencher) { | ||
let mut m: HashMap<i32, i32> = new_map(); | ||
for i in 1..1001 { | ||
m.insert(i, i); | ||
} | ||
|
||
b.iter(|| { | ||
for i in 1..1001 { | ||
black_box(m.get(&i)); | ||
// Produce a different set of random values. | ||
fn new2(size: usize) -> Self { | ||
RandomKeys { | ||
remaining: size, | ||
state: 2, | ||
} | ||
}) | ||
} | ||
} | ||
|
||
#[bench] | ||
fn lookup_i64(b: &mut Bencher) { | ||
let mut m: HashMap<i64, i64> = new_map(); | ||
for i in 1..1001 { | ||
m.insert(i, i); | ||
impl Iterator for RandomKeys { | ||
type Item = usize; | ||
fn next(&mut self) -> Option<usize> { | ||
if self.remaining == 0 { | ||
None | ||
} else { | ||
self.remaining -= 1; | ||
// Multiply by some 32 bit prime. | ||
self.state = self.state.wrapping_mul(3787392781); | ||
// Mix in to the bottom bits which are constant mod powers of 2. | ||
Some(self.state ^ (self.state >> 4)) | ||
} | ||
} | ||
} | ||
|
||
b.iter(|| { | ||
for i in 1..1001 { | ||
black_box(m.get(&i)); | ||
macro_rules! bench_insert { | ||
($name:ident, $maptype:ident, $keydist:expr) => { | ||
#[bench] | ||
fn $name(b: &mut Bencher) { | ||
b.iter(|| { | ||
let mut m = $maptype::default(); | ||
for i in $keydist { | ||
m.insert(i, i); | ||
} | ||
black_box(m); | ||
}) | ||
} | ||
}) | ||
}; | ||
} | ||
|
||
#[bench] | ||
fn lookup_fail_i32(b: &mut Bencher) { | ||
let mut m: HashMap<i32, i32> = new_map(); | ||
for i in 1..1001 { | ||
m.insert(i, i); | ||
} | ||
|
||
b.iter(|| { | ||
for i in 1001..2001 { | ||
black_box(m.get(&i)); | ||
bench_insert!(insert_fx_serial, FxHashMap, 0..SIZE); | ||
bench_insert!(insert_std_serial, StdHashMap, 0..SIZE); | ||
bench_insert!( | ||
insert_fx_highbits, | ||
FxHashMap, | ||
(0..SIZE).map(usize::swap_bytes) | ||
); | ||
bench_insert!( | ||
insert_std_highbits, | ||
StdHashMap, | ||
(0..SIZE).map(usize::swap_bytes) | ||
); | ||
bench_insert!(insert_fx_random, FxHashMap, RandomKeys::new(SIZE)); | ||
bench_insert!(insert_std_random, StdHashMap, RandomKeys::new(SIZE)); | ||
|
||
macro_rules! bench_insert_erase { | ||
($name:ident, $maptype:ident, $keydist:expr) => { | ||
#[bench] | ||
fn $name(b: &mut Bencher) { | ||
b.iter(|| { | ||
let mut m = $maptype::default(); | ||
for i in $keydist { | ||
m.insert(i, i); | ||
} | ||
black_box(&mut m); | ||
for i in $keydist { | ||
m.remove(&i); | ||
} | ||
black_box(m); | ||
}) | ||
} | ||
}) | ||
}; | ||
} | ||
|
||
#[bench] | ||
fn lookup_fail_i64(b: &mut Bencher) { | ||
let mut m: HashMap<i64, i64> = new_map(); | ||
for i in 1..1001 { | ||
m.insert(i, i); | ||
} | ||
|
||
b.iter(|| { | ||
for i in 1001..2001 { | ||
black_box(m.get(&i)); | ||
bench_insert_erase!(insert_erase_fx_serial, FxHashMap, 0..SIZE); | ||
bench_insert_erase!(insert_erase_std_serial, StdHashMap, 0..SIZE); | ||
bench_insert_erase!( | ||
insert_erase_fx_highbits, | ||
FxHashMap, | ||
(0..SIZE).map(usize::swap_bytes) | ||
); | ||
bench_insert_erase!( | ||
insert_erase_std_highbits, | ||
StdHashMap, | ||
(0..SIZE).map(usize::swap_bytes) | ||
); | ||
bench_insert_erase!(insert_erase_fx_random, FxHashMap, RandomKeys::new(SIZE)); | ||
bench_insert_erase!(insert_erase_std_random, StdHashMap, RandomKeys::new(SIZE)); | ||
|
||
macro_rules! bench_lookup { | ||
($name:ident, $maptype:ident, $keydist:expr) => { | ||
#[bench] | ||
fn $name(b: &mut Bencher) { | ||
let mut m = $maptype::default(); | ||
for i in $keydist { | ||
m.insert(i, i); | ||
} | ||
|
||
b.iter(|| { | ||
for i in $keydist { | ||
black_box(m.get(&i)); | ||
} | ||
}) | ||
} | ||
}) | ||
}; | ||
} | ||
|
||
#[bench] | ||
fn iter_i32(b: &mut Bencher) { | ||
let mut m: HashMap<i32, i32> = new_map(); | ||
for i in 1..1001 { | ||
m.insert(i, i); | ||
} | ||
|
||
b.iter(|| { | ||
for i in &m { | ||
black_box(i); | ||
bench_lookup!(lookup_fx_serial, FxHashMap, 0..SIZE); | ||
bench_lookup!(lookup_std_serial, StdHashMap, 0..SIZE); | ||
bench_lookup!( | ||
lookup_fx_highbits, | ||
FxHashMap, | ||
(0..SIZE).map(usize::swap_bytes) | ||
); | ||
bench_lookup!( | ||
lookup_std_highbits, | ||
StdHashMap, | ||
(0..SIZE).map(usize::swap_bytes) | ||
); | ||
bench_lookup!(lookup_fx_random, FxHashMap, RandomKeys::new(SIZE)); | ||
bench_lookup!(lookup_std_random, StdHashMap, RandomKeys::new(SIZE)); | ||
|
||
macro_rules! bench_lookup_fail { | ||
($name:ident, $maptype:ident, $keydist:expr, $keydist2:expr) => { | ||
#[bench] | ||
fn $name(b: &mut Bencher) { | ||
let mut m = $maptype::default(); | ||
for i in $keydist { | ||
m.insert(i, i); | ||
} | ||
|
||
b.iter(|| { | ||
for i in $keydist2 { | ||
black_box(m.get(&i)); | ||
} | ||
}) | ||
} | ||
}) | ||
}; | ||
} | ||
|
||
#[bench] | ||
fn iter_i64(b: &mut Bencher) { | ||
let mut m: HashMap<i64, i64> = new_map(); | ||
for i in 1..1001 { | ||
m.insert(i, i); | ||
} | ||
|
||
b.iter(|| { | ||
for i in &m { | ||
black_box(i); | ||
bench_lookup_fail!(lookup_fail_fx_serial, FxHashMap, 0..SIZE, SIZE..SIZE * 2); | ||
bench_lookup_fail!(lookup_fail_std_serial, StdHashMap, 0..SIZE, SIZE..SIZE * 2); | ||
bench_lookup_fail!( | ||
lookup_fail_fx_highbits, | ||
FxHashMap, | ||
(0..SIZE).map(usize::swap_bytes), | ||
(SIZE..SIZE * 2).map(usize::swap_bytes) | ||
); | ||
bench_lookup_fail!( | ||
lookup_fail_std_highbits, | ||
StdHashMap, | ||
(0..SIZE).map(usize::swap_bytes), | ||
(SIZE..SIZE * 2).map(usize::swap_bytes) | ||
); | ||
bench_lookup_fail!( | ||
lookup_fail_fx_random, | ||
FxHashMap, | ||
RandomKeys::new(SIZE), | ||
RandomKeys::new2(SIZE) | ||
); | ||
bench_lookup_fail!( | ||
lookup_fail_std_random, | ||
StdHashMap, | ||
RandomKeys::new(SIZE), | ||
RandomKeys::new2(SIZE) | ||
); | ||
|
||
macro_rules! bench_iter { | ||
($name:ident, $maptype:ident, $keydist:expr) => { | ||
#[bench] | ||
fn $name(b: &mut Bencher) { | ||
let mut m = $maptype::default(); | ||
for i in $keydist { | ||
m.insert(i, i); | ||
} | ||
|
||
b.iter(|| { | ||
for i in &m { | ||
black_box(i); | ||
} | ||
}) | ||
} | ||
}) | ||
}; | ||
} | ||
|
||
bench_iter!(iter_fx_serial, FxHashMap, 0..SIZE); | ||
bench_iter!(iter_std_serial, StdHashMap, 0..SIZE); | ||
bench_iter!( | ||
iter_fx_highbits, | ||
FxHashMap, | ||
(0..SIZE).map(usize::swap_bytes) | ||
); | ||
bench_iter!( | ||
iter_std_highbits, | ||
StdHashMap, | ||
(0..SIZE).map(usize::swap_bytes) | ||
); | ||
bench_iter!(iter_fx_random, FxHashMap, RandomKeys::new(SIZE)); | ||
bench_iter!(iter_std_random, StdHashMap, RandomKeys::new(SIZE)); |