127 lines
4.0 KiB
Rust
127 lines
4.0 KiB
Rust
use criterion::{Criterion, criterion_group, criterion_main};
|
|
use std::collections::HashMap as StdHashMap;
|
|
use std::hint::black_box;
|
|
|
|
use hashbrown::HashMap as BrownHashMap;
|
|
|
|
const SIZES: [usize; 3] = [5, 10, 50];
|
|
const QUERY_COUNT: usize = 4096;
|
|
|
|
/// Детерминированный ключ ~17 символов, например "key_9e3779b9_0042".
|
|
fn make_key(i: u64) -> String {
|
|
let h = i.wrapping_mul(0x9E37_79B9_7F4A_7C15);
|
|
format!("key_{:08x}_{:04}", h & 0xFFFF_FFFF, i)
|
|
}
|
|
|
|
// Для длинных ключей (пути/URL) замени на:
|
|
// fn make_key(i: u64) -> String {
|
|
// format!(
|
|
// "/api/v2/users/{i}/settings/visibility_{:08x}",
|
|
// i.wrapping_mul(0x9E37_79B9)
|
|
// )
|
|
// }
|
|
|
|
fn make_vec(n: usize) -> Vec<(String, u64)> {
|
|
(0..n as u64).map(|i| (make_key(i), i)).collect()
|
|
}
|
|
|
|
fn make_std_map(data: &[(String, u64)]) -> StdHashMap<String, u64> {
|
|
data.iter().map(|(k, v)| (k.clone(), *v)).collect()
|
|
}
|
|
|
|
fn make_brown_map(data: &[(String, u64)]) -> BrownHashMap<String, u64> {
|
|
data.iter().map(|(k, v)| (k.clone(), *v)).collect()
|
|
}
|
|
|
|
fn make_queries(n: usize, with_misses: bool) -> Vec<String> {
|
|
let keys: Vec<String> = (0..n as u64).map(make_key).collect();
|
|
let missing = "__missing_key__".to_string();
|
|
|
|
(0..QUERY_COUNT)
|
|
.map(|i| {
|
|
if with_misses && i % 16 == 15 {
|
|
missing.clone()
|
|
} else {
|
|
let idx = i.wrapping_mul(2_654_435_761) % n;
|
|
keys[idx].clone()
|
|
}
|
|
})
|
|
.collect()
|
|
}
|
|
|
|
fn lookup_std(map: &StdHashMap<String, u64>, queries: &[String]) -> u64 {
|
|
let mut sum = 0u64;
|
|
for q in queries {
|
|
match map.get(black_box(q.as_str())) {
|
|
Some(v) => sum = sum.wrapping_add(*v),
|
|
None => sum = sum.wrapping_add(1),
|
|
}
|
|
}
|
|
black_box(sum)
|
|
}
|
|
|
|
fn lookup_brown(map: &BrownHashMap<String, u64>, queries: &[String]) -> u64 {
|
|
let mut sum = 0u64;
|
|
for q in queries {
|
|
match map.get(black_box(q.as_str())) {
|
|
Some(v) => sum = sum.wrapping_add(*v),
|
|
None => sum = sum.wrapping_add(1),
|
|
}
|
|
}
|
|
black_box(sum)
|
|
}
|
|
|
|
fn lookup_vec(data: &[(String, u64)], queries: &[String]) -> u64 {
|
|
let mut sum = 0u64;
|
|
for q in queries {
|
|
let q = black_box(q);
|
|
match data.iter().find(|(k, _)| k == q) {
|
|
Some((_, v)) => sum = sum.wrapping_add(*v),
|
|
None => sum = sum.wrapping_add(1),
|
|
}
|
|
}
|
|
black_box(sum)
|
|
}
|
|
|
|
fn bench(c: &mut Criterion) {
|
|
let mut group = c.benchmark_group("map_vs_vec_str");
|
|
|
|
for n in SIZES {
|
|
let vec_data = make_vec(n);
|
|
let std_map = make_std_map(&vec_data);
|
|
let brown_map = make_brown_map(&vec_data);
|
|
|
|
let queries_hit = make_queries(n, false);
|
|
let queries_mixed = make_queries(n, true);
|
|
|
|
// std::collections::HashMap (SipHash)
|
|
group.bench_function(format!("std_hashmap_hit/{n}"), |b| {
|
|
b.iter(|| lookup_std(black_box(&std_map), black_box(&queries_hit)))
|
|
});
|
|
group.bench_function(format!("std_hashmap_mixed/{n}"), |b| {
|
|
b.iter(|| lookup_std(black_box(&std_map), black_box(&queries_mixed)))
|
|
});
|
|
|
|
// hashbrown (SwissTable + foldhash)
|
|
group.bench_function(format!("hashbrown_hit/{n}"), |b| {
|
|
b.iter(|| lookup_brown(black_box(&brown_map), black_box(&queries_hit)))
|
|
});
|
|
group.bench_function(format!("hashbrown_mixed/{n}"), |b| {
|
|
b.iter(|| lookup_brown(black_box(&brown_map), black_box(&queries_mixed)))
|
|
});
|
|
|
|
// Vec<(String, u64)>, линейный поиск
|
|
group.bench_function(format!("vec_linear_hit/{n}"), |b| {
|
|
b.iter(|| lookup_vec(black_box(&vec_data), black_box(&queries_hit)))
|
|
});
|
|
group.bench_function(format!("vec_linear_mixed/{n}"), |b| {
|
|
b.iter(|| lookup_vec(black_box(&vec_data), black_box(&queries_mixed)))
|
|
});
|
|
}
|
|
|
|
group.finish();
|
|
}
|
|
|
|
criterion_group!(benches, bench);
|
|
criterion_main!(benches);
|