#![allow(unused)]
#![feature(iter_collect_into, iter_intersperse, file_create_new)]
use num_format::{Locale, ToFormattedString};
use rand::{
distributions::{Alphanumeric, Standard},
random, thread_rng, Rng,
};
use std::{
collections::{BTreeMap, HashMap},
fs::File,
hint::black_box,
io::{BufWriter, Seek, SeekFrom, Write},
ops::Deref,
time::{Duration, Instant},
};
use vector_mapp::{binary::BinaryMap, vec::VecMap};
pub struct Bencher {
warmup: Duration,
duration: Duration,
result: VecMap<&'static str, Vec<Duration>>,
}
impl Bencher {
#[inline(never)]
pub fn iter<T, F: FnMut() -> T>(&mut self, name: &str, param: usize, mut f: F) {
let now = Instant::now();
let mut runs = 0u128;
loop {
black_box(f());
if now.elapsed() >= self.warmup {
break;
}
runs += 1
}
runs = ((runs as f64) * self.duration.as_secs_f64() / self.warmup.as_secs_f64()) as u128;
let now = Instant::now();
for _ in 0..runs {
black_box(f());
}
let delta = now.elapsed();
let average = Duration::from_secs_f64(delta.as_secs_f64() / (runs as f64));
println!("Benchmarked '{name}' [{param}]: {average:?}");
self.result[name].push(average);
}
#[inline]
pub fn write(&self, w: &mut BufWriter<File>) -> std::io::Result<()> {
for (key, value) in self.result.iter() {
let value = value
.iter()
.map(|x| format!("{}", x.as_nanos()))
.intersperse(String::from(","))
.collect::<String>();
w.write_fmt(format_args!("{key},{value}\n"))?;
}
return Ok(());
}
}
fn insert_with_size(size: usize, b: &mut Bencher) {
let entries = thread_rng()
.sample_iter(Standard)
.take(size)
.collect::<Vec<(u32, u32)>>();
b.iter("hashmap", size, || {
let mut hashmap = HashMap::new();
for (k, v) in entries.iter().copied() {
let _ = hashmap.insert(k, v);
}
return hashmap;
});
b.iter("btreemap", size, || {
let mut hashmap = BTreeMap::new();
for (k, v) in entries.iter().copied() {
let _ = hashmap.insert(k, v);
}
return hashmap;
});
b.iter("vecmap", size, || {
let mut hashmap = VecMap::new();
for (k, v) in entries.iter().copied() {
let _ = hashmap.insert(k, v);
}
return hashmap;
});
b.iter("binarymap", size, || {
let mut hashmap = BinaryMap::new();
for (k, v) in entries.iter().copied() {
let _ = hashmap.insert(k, v);
}
return hashmap;
});
}
fn insert_prealloc_with_size(size: usize, b: &mut Bencher) {
let entries = thread_rng()
.sample_iter(Standard)
.take(size)
.collect::<Vec<(u32, u32)>>();
b.iter("hashmap", size, || {
let mut hashmap = HashMap::with_capacity(size);
for (k, v) in entries.iter().copied() {
let _ = hashmap.insert(k, v);
}
return hashmap;
});
b.iter("vecmap", size, || {
let mut hashmap = VecMap::with_capacity(size);
for (k, v) in entries.iter().copied() {
let _ = hashmap.insert(k, v);
}
return hashmap;
});
b.iter("binarymap", size, || {
let mut hashmap = BinaryMap::with_capacity(size);
for (k, v) in entries.iter().copied() {
let _ = hashmap.insert(k, v);
}
return hashmap;
});
}
fn search_with_size(size: usize, chance: f64, b: &mut Bencher) {
let entries = thread_rng()
.sample_iter(Standard)
.take(size)
.collect::<Vec<(u32, u32)>>();
let searches = (0..size)
.map(|_| match thread_rng().gen_bool(chance) {
false => random::<u32>(),
true => unsafe { entries.get_unchecked(thread_rng().gen_range(0..size)).0 },
})
.collect::<Vec<_>>();
let hashmap = entries.iter().copied().collect::<HashMap<_, _>>();
b.iter("hashmap", size, || {
for key in searches.iter() {
black_box(hashmap.get(key));
}
});
let btreemap = entries.iter().copied().collect::<BTreeMap<_, _>>();
b.iter("btreemap", size, || {
for key in searches.iter() {
black_box(btreemap.get(key));
}
});
let vecmap = entries.iter().copied().collect::<VecMap<_, _>>();
b.iter("vecmap", size, || {
for key in searches.iter() {
black_box(vecmap.get(key));
}
});
let binarymap = entries.iter().copied().collect::<BinaryMap<_, _>>();
b.iter("binarymap", size, || {
for key in searches.iter() {
black_box(binarymap.get(key));
}
});
}
pub fn calculate<I: IntoIterator<Item = usize>, F: FnMut(usize, &mut Bencher)>(
name: &str,
idx: I,
mut f: F,
) -> std::io::Result<()> {
let file_name = thread_rng()
.sample_iter::<u8, _>(Alphanumeric)
.take(10)
.chain(format!("_{name}.csv").bytes())
.collect::<Vec<u8>>();
let mut b = Bencher {
warmup: Duration::from_secs(3),
duration: Duration::from_secs(5),
result: [
("hashmap", Vec::new()),
("btreemap", Vec::new()),
("vecmap", Vec::new()),
("binarymap", Vec::new()),
]
.into_iter()
.collect(),
};
let mut header = vec![String::new()];
let mut file = BufWriter::new(File::create_new(
String::from_utf8_lossy(&file_name).deref(),
)?);
for i in idx {
header.push(format!("{i}"));
f(i, &mut b);
file.seek(SeekFrom::Start(0))?;
let mut header = header.join(",").into_bytes();
header.push(b'\n');
file.write_all(&header)?;
b.write(&mut file)?;
}
file.flush()?;
return Ok(());
}
pub fn main() {
let iter = 1..=256;
std::thread::scope(|s| {
s.spawn(|| calculate("insert", iter.clone(), insert_with_size).unwrap());
s.spawn(|| calculate("insert_prealloc", iter.clone(), insert_prealloc_with_size).unwrap());
s.spawn(|| {
calculate("search_50", iter.clone(), |size, b| {
search_with_size(size, 0.5, b)
})
.unwrap()
});
calculate("search_100", iter.clone(), |size, b| {
search_with_size(size, 1., b)
})
.unwrap()
});
}