use criterion::{BenchmarkId, Criterion, black_box, criterion_group, criterion_main};
use simplehash::fnv::Fnv1aHasher64;
use simplehash::murmur::MurmurHasher64;
use std::collections::HashMap;
use std::hash::{BuildHasher, BuildHasherDefault};
use std::time::Instant;
#[derive(Default, Clone)]
struct MurmurHash3BuildHasher;
impl BuildHasher for MurmurHash3BuildHasher {
type Hasher = MurmurHasher64;
fn build_hasher(&self) -> Self::Hasher {
MurmurHasher64::new(0) }
}
fn bench_hashmap_with_different_hashers(c: &mut Criterion) {
let mut group = c.benchmark_group("HashMap Performance");
let sizes = [100, 1_000, 10_000];
for size in sizes {
let keys: Vec<String> = (0..size).map(|i| format!("key_{}", i)).collect();
group.bench_function(BenchmarkId::new("StdHashMap-Insert", size), |b| {
b.iter_custom(|iters| {
let mut total_duration = std::time::Duration::new(0, 0);
for _ in 0..iters {
let mut map: HashMap<String, u32> = HashMap::with_capacity(size as usize);
let start = Instant::now();
for (i, key) in keys.iter().enumerate() {
map.insert(key.clone(), i as u32);
}
total_duration += start.elapsed();
black_box(&map);
}
total_duration
});
});
group.bench_function(BenchmarkId::new("FNV1a64-HashMap-Insert", size), |b| {
b.iter_custom(|iters| {
let mut total_duration = std::time::Duration::new(0, 0);
for _ in 0..iters {
let mut map: HashMap<String, u32, BuildHasherDefault<Fnv1aHasher64>> =
HashMap::with_hasher(BuildHasherDefault::<Fnv1aHasher64>::default());
let start = Instant::now();
for (i, key) in keys.iter().enumerate() {
map.insert(key.clone(), i as u32);
}
total_duration += start.elapsed();
black_box(&map);
}
total_duration
});
});
group.bench_function(
BenchmarkId::new("MurmurHash3-64-HashMap-Insert", size),
|b| {
b.iter_custom(|iters| {
let mut total_duration = std::time::Duration::new(0, 0);
for _ in 0..iters {
let mut map: HashMap<String, u32, MurmurHash3BuildHasher> =
HashMap::with_hasher(MurmurHash3BuildHasher);
let start = Instant::now();
for (i, key) in keys.iter().enumerate() {
map.insert(key.clone(), i as u32);
}
total_duration += start.elapsed();
black_box(&map);
}
total_duration
});
},
);
let lookup_keys: Vec<&String> = keys.iter().step_by(10).collect();
group.bench_function(BenchmarkId::new("StdHashMap-Lookup", size), |b| {
let mut map: HashMap<String, u32> = HashMap::with_capacity(size as usize);
for (i, key) in keys.iter().enumerate() {
map.insert(key.clone(), i as u32);
}
b.iter(|| {
for key in black_box(&lookup_keys) {
black_box(map.get(*key));
}
});
});
group.bench_function(BenchmarkId::new("FNV1a64-HashMap-Lookup", size), |b| {
let mut map: HashMap<String, u32, BuildHasherDefault<Fnv1aHasher64>> =
HashMap::with_hasher(BuildHasherDefault::<Fnv1aHasher64>::default());
for (i, key) in keys.iter().enumerate() {
map.insert(key.clone(), i as u32);
}
b.iter(|| {
for key in black_box(&lookup_keys) {
black_box(map.get(*key));
}
});
});
group.bench_function(
BenchmarkId::new("MurmurHash3-64-HashMap-Lookup", size),
|b| {
let mut map: HashMap<String, u32, MurmurHash3BuildHasher> =
HashMap::with_hasher(MurmurHash3BuildHasher);
for (i, key) in keys.iter().enumerate() {
map.insert(key.clone(), i as u32);
}
b.iter(|| {
for key in black_box(&lookup_keys) {
black_box(map.get(*key));
}
});
},
);
}
group.finish();
}
fn bench_collision_resistance(c: &mut Criterion) {
let mut group = c.benchmark_group("HashMap Collision Resistance");
let size = 10_000;
let similar_keys: Vec<String> = (0..size)
.map(|i| format!("prefix_{:010}", i)) .collect();
group.bench_function("StdHashMap-SimilarKeys", |b| {
b.iter_custom(|iters| {
let mut total_duration = std::time::Duration::new(0, 0);
for _ in 0..iters {
let mut map: HashMap<String, u32> = HashMap::with_capacity(size as usize);
let start = Instant::now();
for (i, key) in similar_keys.iter().enumerate() {
map.insert(key.clone(), i as u32);
}
total_duration += start.elapsed();
black_box(&map);
}
total_duration
});
});
group.bench_function("FNV1a64-HashMap-SimilarKeys", |b| {
b.iter_custom(|iters| {
let mut total_duration = std::time::Duration::new(0, 0);
for _ in 0..iters {
let mut map: HashMap<String, u32, BuildHasherDefault<Fnv1aHasher64>> =
HashMap::with_hasher(BuildHasherDefault::<Fnv1aHasher64>::default());
let start = Instant::now();
for (i, key) in similar_keys.iter().enumerate() {
map.insert(key.clone(), i as u32);
}
total_duration += start.elapsed();
black_box(&map);
}
total_duration
});
});
group.bench_function("MurmurHash3-64-HashMap-SimilarKeys", |b| {
b.iter_custom(|iters| {
let mut total_duration = std::time::Duration::new(0, 0);
for _ in 0..iters {
let mut map: HashMap<String, u32, MurmurHash3BuildHasher> =
HashMap::with_hasher(MurmurHash3BuildHasher);
let start = Instant::now();
for (i, key) in similar_keys.iter().enumerate() {
map.insert(key.clone(), i as u32);
}
total_duration += start.elapsed();
black_box(&map);
}
total_duration
});
});
group.finish();
}
criterion_group!(
benches,
bench_hashmap_with_different_hashers,
bench_collision_resistance
);
criterion_main!(benches);