#![feature(test)] extern crate test; use test::Bencher; use indexmap::IndexMap; use std::collections::HashMap; use rand::rngs::SmallRng; use rand::seq::SliceRandom; use rand::SeedableRng; use std::hash::{Hash, Hasher}; use std::borrow::Borrow; use std::ops::Deref; /// Use a consistently seeded Rng for benchmark stability fn small_rng() -> SmallRng { let seed = u64::from_le_bytes(*b"indexmap"); SmallRng::seed_from_u64(seed) } #[derive(PartialEq, Eq, Copy, Clone)] #[repr(transparent)] pub struct OneShot(pub T); impl Hash for OneShot { fn hash(&self, h: &mut H) { h.write(self.0.as_bytes()) } } impl<'a, S> From<&'a S> for &'a OneShot where S: AsRef, { fn from(s: &'a S) -> Self { let s: &str = s.as_ref(); unsafe { &*(s as *const str as *const OneShot) } } } impl Hash for OneShot { fn hash(&self, h: &mut H) { h.write(self.0.as_bytes()) } } impl Borrow> for OneShot { fn borrow(&self) -> &OneShot { <&OneShot>::from(&self.0) } } impl Deref for OneShot { type Target = T; fn deref(&self) -> &T { &self.0 } } fn shuffled_keys(iter: I) -> Vec where I: IntoIterator, { let mut v = Vec::from_iter(iter); let mut rng = small_rng(); v.shuffle(&mut rng); v } #[bench] fn insert_hashmap_string_10_000(b: &mut Bencher) { let c = 10_000; b.iter(|| { let mut map = HashMap::with_capacity(c); for x in 0..c { map.insert(x.to_string(), ()); } map }); } #[bench] fn insert_hashmap_string_oneshot_10_000(b: &mut Bencher) { let c = 10_000; b.iter(|| { let mut map = HashMap::with_capacity(c); for x in 0..c { map.insert(OneShot(x.to_string()), ()); } map }); } #[bench] fn insert_indexmap_string_10_000(b: &mut Bencher) { let c = 10_000; b.iter(|| { let mut map = IndexMap::with_capacity(c); for x in 0..c { map.insert(x.to_string(), ()); } map }); } #[bench] fn lookup_hashmap_10_000_exist_string(b: &mut Bencher) { let c = 10_000; let mut map = HashMap::with_capacity(c); let keys = shuffled_keys(0..c); for &key in &keys { map.insert(key.to_string(), 1); } let lookups = (5000..c).map(|x| x.to_string()).collect::>(); b.iter(|| { let mut found = 0; for key in &lookups { found += map.get(key).is_some() as i32; } found }); } #[bench] fn lookup_hashmap_10_000_exist_string_oneshot(b: &mut Bencher) { let c = 10_000; let mut map = HashMap::with_capacity(c); let keys = shuffled_keys(0..c); for &key in &keys { map.insert(OneShot(key.to_string()), 1); } let lookups = (5000..c) .map(|x| OneShot(x.to_string())) .collect::>(); b.iter(|| { let mut found = 0; for key in &lookups { found += map.get(key).is_some() as i32; } found }); } #[bench] fn lookup_indexmap_10_000_exist_string(b: &mut Bencher) { let c = 10_000; let mut map = IndexMap::with_capacity(c); let keys = shuffled_keys(0..c); for &key in &keys { map.insert(key.to_string(), 1); } let lookups = (5000..c).map(|x| x.to_string()).collect::>(); b.iter(|| { let mut found = 0; for key in &lookups { found += map.get(key).is_some() as i32; } found }); } #[bench] fn lookup_indexmap_10_000_exist_string_oneshot(b: &mut Bencher) { let c = 10_000; let mut map = IndexMap::with_capacity(c); let keys = shuffled_keys(0..c); for &key in &keys { map.insert(OneShot(key.to_string()), 1); } let lookups = (5000..c) .map(|x| OneShot(x.to_string())) .collect::>(); b.iter(|| { let mut found = 0; for key in &lookups { found += map.get(key).is_some() as i32; } found }); }