// These benchmarks were taken from hashbrown. They are impossible to run // on the target GBA hardware, but hopefully running these on something like a // raspberry pi zero will give something comparable. // This benchmark suite contains some benchmarks along a set of dimensions: // 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 test::{black_box, Bencher}; use agb_hashmap::HashMap; use std::sync::atomic::{self, AtomicUsize}; const SIZE: usize = 1000; type StdHashMap = std::collections::hash_map::HashMap; // A random key iterator. #[derive(Clone, Copy)] struct RandomKeys { state: usize, } impl RandomKeys { fn new() -> Self { RandomKeys { state: 0 } } } impl Iterator for RandomKeys { type Item = usize; fn next(&mut self) -> Option { // Add 1 then multiply by some 32 bit prime. self.state = self.state.wrapping_add(1).wrapping_mul(3_787_392_781); Some(self.state) } } // Just an arbitrary side effect to make the maps not shortcircuit to the non-dropping path // when dropping maps/entries (most real world usages likely have drop in the key or value) lazy_static::lazy_static! { static ref SIDE_EFFECT: AtomicUsize = AtomicUsize::new(0); } #[derive(Clone)] struct DropType(usize); impl Drop for DropType { fn drop(&mut self) { SIDE_EFFECT.fetch_add(self.0, atomic::Ordering::SeqCst); } } macro_rules! bench_suite { ($bench_macro:ident, $bench_agb_hashmap_serial:ident, $bench_std_serial:ident, $bench_agb_hashmap_highbits:ident, $bench_std_highbits:ident, $bench_agb_hashmap_random:ident, $bench_std_random:ident) => { $bench_macro!($bench_agb_hashmap_serial, HashMap, 0..); $bench_macro!($bench_std_serial, StdHashMap, 0..); $bench_macro!( $bench_agb_hashmap_highbits, HashMap, (0..).map(usize::swap_bytes) ); $bench_macro!( $bench_std_highbits, StdHashMap, (0..).map(usize::swap_bytes) ); $bench_macro!($bench_agb_hashmap_random, HashMap, RandomKeys::new()); $bench_macro!($bench_std_random, StdHashMap, RandomKeys::new()); }; } macro_rules! bench_insert { ($name:ident, $maptype:ident, $keydist:expr) => { #[bench] fn $name(b: &mut Bencher) { let mut m = $maptype::with_capacity(SIZE); b.iter(|| { m.clear(); for i in ($keydist).take(SIZE) { m.insert(i, (DropType(i), [i; 20])); } black_box(&mut m); }); eprintln!("{}", SIDE_EFFECT.load(atomic::Ordering::SeqCst)); } }; } bench_suite!( bench_insert, insert_agb_hashmap_serial, insert_std_serial, insert_agb_hashmap_highbits, insert_std_highbits, insert_agb_hashmap_random, insert_std_random ); macro_rules! bench_grow_insert { ($name:ident, $maptype:ident, $keydist:expr) => { #[bench] fn $name(b: &mut Bencher) { b.iter(|| { let mut m = $maptype::default(); for i in ($keydist).take(SIZE) { m.insert(i, DropType(i)); } black_box(&mut m); }) } }; } bench_suite!( bench_grow_insert, grow_insert_agb_hashmap_serial, grow_insert_std_serial, grow_insert_agb_hashmap_highbits, grow_insert_std_highbits, grow_insert_agb_hashmap_random, grow_insert_std_random ); macro_rules! bench_insert_erase { ($name:ident, $maptype:ident, $keydist:expr) => { #[bench] fn $name(b: &mut Bencher) { let mut base = $maptype::default(); for i in ($keydist).take(SIZE) { base.insert(i, DropType(i)); } let skip = $keydist.skip(SIZE); b.iter(|| { let mut m = base.clone(); let mut add_iter = skip.clone(); let mut remove_iter = $keydist; // While keeping the size constant, // replace the first keydist with the second. for (add, remove) in (&mut add_iter).zip(&mut remove_iter).take(SIZE) { m.insert(add, DropType(add)); black_box(m.remove(&remove)); } black_box(m); }); eprintln!("{}", SIDE_EFFECT.load(atomic::Ordering::SeqCst)); } }; } bench_suite!( bench_insert_erase, insert_erase_agb_hashmap_serial, insert_erase_std_serial, insert_erase_agb_hashmap_highbits, insert_erase_std_highbits, insert_erase_agb_hashmap_random, insert_erase_std_random ); 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.take(SIZE) { m.insert(i, DropType(i)); } b.iter(|| { for i in $keydist.take(SIZE) { black_box(m.get(&i)); } }); eprintln!("{}", SIDE_EFFECT.load(atomic::Ordering::SeqCst)); } }; } bench_suite!( bench_lookup, lookup_agb_hashmap_serial, lookup_std_serial, lookup_agb_hashmap_highbits, lookup_std_highbits, lookup_agb_hashmap_random, lookup_std_random ); macro_rules! bench_lookup_fail { ($name:ident, $maptype:ident, $keydist:expr) => { #[bench] fn $name(b: &mut Bencher) { let mut m = $maptype::default(); let mut iter = $keydist; for i in (&mut iter).take(SIZE) { m.insert(i, DropType(i)); } b.iter(|| { for i in (&mut iter).take(SIZE) { black_box(m.get(&i)); } }) } }; } bench_suite!( bench_lookup_fail, lookup_fail_agb_hashmap_serial, lookup_fail_std_serial, lookup_fail_agb_hashmap_highbits, lookup_fail_std_highbits, lookup_fail_agb_hashmap_random, lookup_fail_std_random ); 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).take(SIZE) { m.insert(i, DropType(i)); } b.iter(|| { for i in &m { black_box(i); } }) } }; } bench_suite!( bench_iter, iter_agb_hashmap_serial, iter_std_serial, iter_agb_hashmap_highbits, iter_std_highbits, iter_agb_hashmap_random, iter_std_random ); macro_rules! clone_bench { ($maptype:ident) => { use super::DropType; use test::{black_box, Bencher}; #[bench] fn clone_small(b: &mut Bencher) { let mut m = $maptype::new(); for i in 0..10 { m.insert(i, DropType(i)); } b.iter(|| { black_box(m.clone()); }) } #[bench] fn clone_from_small(b: &mut Bencher) { let mut m = $maptype::new(); let mut m2 = $maptype::new(); for i in 0..10 { m.insert(i, DropType(i)); } b.iter(|| { m2.clone_from(&m); black_box(&mut m2); }) } #[bench] fn clone_large(b: &mut Bencher) { let mut m = $maptype::new(); for i in 0..1000 { m.insert(i, DropType(i)); } b.iter(|| { black_box(m.clone()); }) } #[bench] fn clone_from_large(b: &mut Bencher) { let mut m = $maptype::new(); let mut m2 = $maptype::new(); for i in 0..1000 { m.insert(i, DropType(i)); } b.iter(|| { m2.clone_from(&m); black_box(&mut m2); }) } }; } mod agb_hashmap_clone_benches { use agb_hashmap::HashMap; clone_bench!(HashMap); } mod std_hashmap_clone_benches { use std::collections::hash_map::HashMap; clone_bench!(HashMap); }