Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add the std hasher and different key distributions to benchmarks. #83

Merged
merged 2 commits into from Jun 2, 2019
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
316 changes: 200 additions & 116 deletions benches/bench.rs
@@ -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));