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, 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, 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, entities: &[Entity]) { map.clear(); for entity in entities { map.insert(entity.id, entity.clone()); } } fn bench_btreemap_search(map: &BTreeMap, id: i64) -> Option<&Entity> { map.get(&id) } fn bench_hashmap_insert(map: &mut HashMap, entities: &[Entity]) { map.clear(); for entity in entities { map.insert(entity.id, entity.clone()); } } fn bench_hashmap_search(map: &HashMap, id: i64) -> Option<&Entity> { map.get(&id) } fn bench_hashbrown_insert(map: &mut HashbrownMap, entities: &[Entity]) { map.clear(); for entity in entities { map.insert(entity.id, entity.clone()); } } fn bench_hashbrown_search(map: &HashbrownMap, id: i64) -> Option<&Entity> { map.get(&id) } fn bench_fxhashmap_insert(map: &mut FxHashMap, entities: &[Entity]) { map.clear(); for entity in entities { map.insert(entity.id, entity.clone()); } } fn bench_fxhashmap_search(map: &FxHashMap, id: i64) -> Option<&Entity> { map.get(&id) } // ========== ГЕНЕРАЦИЯ ДАННЫХ ========== fn generate_entities(count: usize) -> Vec { (0..count).map(|i| Entity::new(i as i64)).collect() } fn generate_search_ids(count: usize, max_id: i64) -> Vec { // 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::>(); 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);