Add benchmarks

This commit is contained in:
Gwilym Inzani 2023-05-10 16:01:16 +01:00
parent 7873f9ed5c
commit c5341cb5fa
2 changed files with 319 additions and 0 deletions

View file

@ -11,6 +11,7 @@ rustc-hash = { version = "1", default-features = false }
[dev-dependencies]
rand = { version = "0.8", default-features = false, features = ["small_rng"] }
lazy_static = "1.4"
[profile.dev]
opt-level = 3

View file

@ -0,0 +1,318 @@
// 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<K, V> = std::collections::hash_map::HashMap<K, V>;
// 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<usize> {
// 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);
}