285 lines
8.6 KiB
Rust
285 lines
8.6 KiB
Rust
use criterion::{criterion_group, criterion_main, Criterion, BenchmarkId};
|
|
use std::collections::{BTreeMap, HashMap};
|
|
use std::hint::black_box;
|
|
use hashbrown::HashMap as HashbrownMap;
|
|
use rustc_hash::FxHashMap;
|
|
|
|
// Тестовая структура (аналог вашей сущности)
|
|
#[derive(Debug, Clone)]
|
|
struct Entity {
|
|
id: i64,
|
|
data: [u8; 64], // 64 байта данных для реалистичности
|
|
}
|
|
|
|
impl Entity {
|
|
fn new(id: i64) -> Self {
|
|
Self {
|
|
id,
|
|
data: [id as u8; 64],
|
|
}
|
|
}
|
|
}
|
|
|
|
// ========== ТЕСТОВЫЕ ФУНКЦИИ ==========
|
|
|
|
fn bench_vec_insert_sorted(vec: &mut Vec<Entity>, entities: &[Entity]) {
|
|
vec.clear();
|
|
for entity in entities {
|
|
vec.push(entity.clone());
|
|
}
|
|
// Vec уже отсортирован, так как ID монотонно возрастают
|
|
}
|
|
|
|
fn bench_vec_search_separated<'a>(ids: &[i64], vec: &'a [Entity], id: i64) -> Option<&'a Entity> {
|
|
match ids.binary_search(&id) {
|
|
Ok(index) => Some(&vec[index]),
|
|
Err(_) => None,
|
|
}
|
|
}
|
|
|
|
fn bench_vec_search(vec: &Vec<Entity>, id: i64) -> Option<&Entity> {
|
|
match vec.binary_search_by(|probe| probe.id.cmp(&id)) {
|
|
Ok(index) => Some(&vec[index]),
|
|
Err(_) => None,
|
|
}
|
|
}
|
|
|
|
fn bench_btreemap_insert(map: &mut BTreeMap<i64, Entity>, entities: &[Entity]) {
|
|
map.clear();
|
|
for entity in entities {
|
|
map.insert(entity.id, entity.clone());
|
|
}
|
|
}
|
|
|
|
fn bench_btreemap_search(map: &BTreeMap<i64, Entity>, id: i64) -> Option<&Entity> {
|
|
map.get(&id)
|
|
}
|
|
|
|
fn bench_hashmap_insert(map: &mut HashMap<i64, Entity>, entities: &[Entity]) {
|
|
map.clear();
|
|
for entity in entities {
|
|
map.insert(entity.id, entity.clone());
|
|
}
|
|
}
|
|
|
|
fn bench_hashmap_search(map: &HashMap<i64, Entity>, id: i64) -> Option<&Entity> {
|
|
map.get(&id)
|
|
}
|
|
|
|
fn bench_hashbrown_insert(map: &mut HashbrownMap<i64, Entity>, entities: &[Entity]) {
|
|
map.clear();
|
|
for entity in entities {
|
|
map.insert(entity.id, entity.clone());
|
|
}
|
|
}
|
|
|
|
fn bench_hashbrown_search(map: &HashbrownMap<i64, Entity>, id: i64) -> Option<&Entity> {
|
|
map.get(&id)
|
|
}
|
|
|
|
fn bench_fxhashmap_insert(map: &mut FxHashMap<i64, Entity>, entities: &[Entity]) {
|
|
map.clear();
|
|
for entity in entities {
|
|
map.insert(entity.id, entity.clone());
|
|
}
|
|
}
|
|
|
|
fn bench_fxhashmap_search(map: &FxHashMap<i64, Entity>, id: i64) -> Option<&Entity> {
|
|
map.get(&id)
|
|
}
|
|
|
|
// ========== ГЕНЕРАЦИЯ ДАННЫХ ==========
|
|
|
|
fn generate_entities(count: usize) -> Vec<Entity> {
|
|
(0..count).map(|i| Entity::new(i as i64)).collect()
|
|
}
|
|
|
|
fn generate_search_ids(count: usize, max_id: i64) -> Vec<i64> {
|
|
// 70% попаданий, 30% промахов (реалистичный сценарий)
|
|
let mut ids = Vec::with_capacity(count);
|
|
for i in 0..count {
|
|
if i % 10 < 7 {
|
|
// Попадание
|
|
ids.push((i as i64 * 13) % max_id);
|
|
} else {
|
|
// Промах (несуществующий ID)
|
|
ids.push(max_id + i as i64);
|
|
}
|
|
}
|
|
ids
|
|
}
|
|
|
|
// ========== БЕНЧМАРКИ ==========
|
|
|
|
fn bench_insert(c: &mut Criterion) {
|
|
let mut group = c.benchmark_group("insert");
|
|
|
|
for size in [10_000, 100_000, 500_000] {
|
|
let entities = generate_entities(size);
|
|
|
|
group.bench_with_input(BenchmarkId::new("Vec (sorted)", size), &entities, |b, entities| {
|
|
let mut vec = Vec::with_capacity(size);
|
|
b.iter(|| {
|
|
bench_vec_insert_sorted(&mut vec, black_box(entities));
|
|
});
|
|
});
|
|
|
|
group.bench_with_input(BenchmarkId::new("BTreeMap", size), &entities, |b, entities| {
|
|
let mut map = BTreeMap::new();
|
|
b.iter(|| {
|
|
bench_btreemap_insert(&mut map, black_box(entities));
|
|
});
|
|
});
|
|
|
|
group.bench_with_input(BenchmarkId::new("HashMap (std)", size), &entities, |b, entities| {
|
|
let mut map = HashMap::new();
|
|
b.iter(|| {
|
|
bench_hashmap_insert(&mut map, black_box(entities));
|
|
});
|
|
});
|
|
|
|
group.bench_with_input(BenchmarkId::new("HashMap (hashbrown)", size), &entities, |b, entities| {
|
|
let mut map = HashbrownMap::new();
|
|
b.iter(|| {
|
|
bench_hashbrown_insert(&mut map, black_box(entities));
|
|
});
|
|
});
|
|
|
|
group.bench_with_input(BenchmarkId::new("FxHashMap", size), &entities, |b, entities| {
|
|
let mut map = FxHashMap::default();
|
|
b.iter(|| {
|
|
bench_fxhashmap_insert(&mut map, black_box(entities));
|
|
});
|
|
});
|
|
}
|
|
|
|
group.finish();
|
|
}
|
|
|
|
fn bench_search(c: &mut Criterion) {
|
|
let mut group = c.benchmark_group("search");
|
|
|
|
for size in [10_000, 100_000, 500_000] {
|
|
let entities = generate_entities(size);
|
|
let entity_ids = entities.iter().map(|entity| entity.id).collect::<Vec<_>>();
|
|
let search_ids = generate_search_ids(1000, size as i64);
|
|
|
|
// Подготовка коллекций
|
|
let mut vec = Vec::with_capacity(size);
|
|
bench_vec_insert_sorted(&mut vec, &entities);
|
|
|
|
let mut btreemap = BTreeMap::new();
|
|
bench_btreemap_insert(&mut btreemap, &entities);
|
|
|
|
let mut hashmap = HashMap::new();
|
|
bench_hashmap_insert(&mut hashmap, &entities);
|
|
|
|
let mut hashbrown = HashbrownMap::new();
|
|
bench_hashbrown_insert(&mut hashbrown, &entities);
|
|
|
|
let mut fxhashmap = FxHashMap::default();
|
|
bench_fxhashmap_insert(&mut fxhashmap, &entities);
|
|
|
|
group.bench_with_input(BenchmarkId::new("Vec (binary search)", size), &search_ids, |b, ids| {
|
|
b.iter(|| {
|
|
for &id in ids {
|
|
black_box(bench_vec_search_separated(&entity_ids, &vec, black_box(id)));
|
|
}
|
|
});
|
|
});
|
|
|
|
group.bench_with_input(BenchmarkId::new("Vec (binary search) separated", size), &search_ids, |b, ids| {
|
|
b.iter(|| {
|
|
for &id in ids {
|
|
black_box(bench_vec_search(&vec, black_box(id)));
|
|
}
|
|
});
|
|
});
|
|
|
|
group.bench_with_input(BenchmarkId::new("BTreeMap", size), &search_ids, |b, ids| {
|
|
b.iter(|| {
|
|
for &id in ids {
|
|
black_box(bench_btreemap_search(&btreemap, black_box(id)));
|
|
}
|
|
});
|
|
});
|
|
|
|
group.bench_with_input(BenchmarkId::new("HashMap (std)", size), &search_ids, |b, ids| {
|
|
b.iter(|| {
|
|
for &id in ids {
|
|
black_box(bench_hashmap_search(&hashmap, black_box(id)));
|
|
}
|
|
});
|
|
});
|
|
|
|
group.bench_with_input(BenchmarkId::new("HashMap (hashbrown)", size), &search_ids, |b, ids| {
|
|
b.iter(|| {
|
|
for &id in ids {
|
|
black_box(bench_hashbrown_search(&hashbrown, black_box(id)));
|
|
}
|
|
});
|
|
});
|
|
|
|
group.bench_with_input(BenchmarkId::new("FxHashMap", size), &search_ids, |b, ids| {
|
|
b.iter(|| {
|
|
for &id in ids {
|
|
black_box(bench_fxhashmap_search(&fxhashmap, black_box(id)));
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
group.finish();
|
|
}
|
|
|
|
fn bench_memory_efficiency(c: &mut Criterion) {
|
|
let mut group = c.benchmark_group("memory_efficiency");
|
|
|
|
let size = 100_000;
|
|
let entities = generate_entities(size);
|
|
|
|
group.bench_function("Vec memory", |b| {
|
|
b.iter(|| {
|
|
let mut vec = Vec::with_capacity(size);
|
|
bench_vec_insert_sorted(&mut vec, black_box(&entities));
|
|
black_box(vec);
|
|
});
|
|
});
|
|
|
|
group.bench_function("BTreeMap memory", |b| {
|
|
b.iter(|| {
|
|
let mut map = BTreeMap::new();
|
|
bench_btreemap_insert(&mut map, black_box(&entities));
|
|
black_box(map);
|
|
});
|
|
});
|
|
|
|
group.bench_function("HashMap (std) memory", |b| {
|
|
b.iter(|| {
|
|
let mut map = HashMap::new();
|
|
bench_hashmap_insert(&mut map, black_box(&entities));
|
|
black_box(map);
|
|
});
|
|
});
|
|
|
|
group.bench_function("HashMap (hashbrown) memory", |b| {
|
|
b.iter(|| {
|
|
let mut map = HashbrownMap::new();
|
|
bench_hashbrown_insert(&mut map, black_box(&entities));
|
|
black_box(map);
|
|
});
|
|
});
|
|
|
|
group.bench_function("FxHashMap memory", |b| {
|
|
b.iter(|| {
|
|
let mut map = FxHashMap::default();
|
|
bench_fxhashmap_insert(&mut map, black_box(&entities));
|
|
black_box(map);
|
|
});
|
|
});
|
|
|
|
group.finish();
|
|
}
|
|
|
|
criterion_group!(benches, bench_insert, bench_search, bench_memory_efficiency);
|
|
criterion_main!(benches); |