Skip to content

Commit

Permalink
Auto merge of #83 - edre:bench, r=Amanieu
Browse files Browse the repository at this point in the history
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
bors committed Jun 2, 2019
2 parents 987b962 + 2025fb4 commit 143609d
Showing 1 changed file with 200 additions and 116 deletions.
316 changes: 200 additions & 116 deletions benches/bench.rs
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));

0 comments on commit 143609d

Please sign in to comment.