use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion}; use std::{ sync::{Arc, Barrier, RwLock}, thread, time::{Duration, Instant}, }; #[derive(Clone)] struct MultithreadedBench { start: Arc, end: Arc, slab: Arc, } impl MultithreadedBench { fn new(slab: Arc) -> Self { Self { start: Arc::new(Barrier::new(5)), end: Arc::new(Barrier::new(5)), slab, } } fn thread(&self, f: impl FnOnce(&Barrier, &T) + Send + 'static) -> &Self { let start = self.start.clone(); let end = self.end.clone(); let slab = self.slab.clone(); thread::spawn(move || { f(&*start, &*slab); end.wait(); }); self } fn run(&self) -> Duration { self.start.wait(); let t0 = Instant::now(); self.end.wait(); t0.elapsed() } } const N_INSERTIONS: &[usize] = &[100, 300, 500, 700, 1000, 3000, 5000]; fn insert_remove_local(c: &mut Criterion) { // the 10000-insertion benchmark takes the `slab` crate about an hour to // run; don't run this unless you're prepared for that... // const N_INSERTIONS: &'static [usize] = &[100, 500, 1000, 5000, 10000]; let mut group = c.benchmark_group("insert_remove_local"); let g = group.measurement_time(Duration::from_secs(15)); for i in N_INSERTIONS { g.bench_with_input(BenchmarkId::new("sharded_slab", i), i, |b, &i| { b.iter_custom(|iters| { let mut total = Duration::from_secs(0); for _ in 0..iters { let bench = MultithreadedBench::new(Arc::new(sharded_slab::Slab::new())); let elapsed = bench .thread(move |start, slab| { start.wait(); let v: Vec<_> = (0..i).map(|i| slab.insert(i).unwrap()).collect(); for i in v { slab.remove(i); } }) .thread(move |start, slab| { start.wait(); let v: Vec<_> = (0..i).map(|i| slab.insert(i).unwrap()).collect(); for i in v { slab.remove(i); } }) .thread(move |start, slab| { start.wait(); let v: Vec<_> = (0..i).map(|i| slab.insert(i).unwrap()).collect(); for i in v { slab.remove(i); } }) .thread(move |start, slab| { start.wait(); let v: Vec<_> = (0..i).map(|i| slab.insert(i).unwrap()).collect(); for i in v { slab.remove(i); } }) .run(); total += elapsed; } total }) }); g.bench_with_input(BenchmarkId::new("slab_biglock", i), i, |b, &i| { b.iter_custom(|iters| { let mut total = Duration::from_secs(0); let i = i; for _ in 0..iters { let bench = MultithreadedBench::new(Arc::new(RwLock::new(slab::Slab::new()))); let elapsed = bench .thread(move |start, slab| { start.wait(); let v: Vec<_> = (0..i).map(|i| slab.write().unwrap().insert(i)).collect(); for i in v { slab.write().unwrap().remove(i); } }) .thread(move |start, slab| { start.wait(); let v: Vec<_> = (0..i).map(|i| slab.write().unwrap().insert(i)).collect(); for i in v { slab.write().unwrap().remove(i); } }) .thread(move |start, slab| { start.wait(); let v: Vec<_> = (0..i).map(|i| slab.write().unwrap().insert(i)).collect(); for i in v { slab.write().unwrap().remove(i); } }) .thread(move |start, slab| { start.wait(); let v: Vec<_> = (0..i).map(|i| slab.write().unwrap().insert(i)).collect(); for i in v { slab.write().unwrap().remove(i); } }) .run(); total += elapsed; } total }) }); } group.finish(); } fn insert_remove_single_thread(c: &mut Criterion) { // the 10000-insertion benchmark takes the `slab` crate about an hour to // run; don't run this unless you're prepared for that... // const N_INSERTIONS: &'static [usize] = &[100, 500, 1000, 5000, 10000]; let mut group = c.benchmark_group("insert_remove_single_threaded"); for i in N_INSERTIONS { group.bench_with_input(BenchmarkId::new("sharded_slab", i), i, |b, &i| { let slab = sharded_slab::Slab::new(); b.iter(|| { let v: Vec<_> = (0..i).map(|i| slab.insert(i).unwrap()).collect(); for i in v { slab.remove(i); } }); }); group.bench_with_input(BenchmarkId::new("slab_no_lock", i), i, |b, &i| { let mut slab = slab::Slab::new(); b.iter(|| { let v: Vec<_> = (0..i).map(|i| slab.insert(i)).collect(); for i in v { slab.remove(i); } }); }); group.bench_with_input(BenchmarkId::new("slab_uncontended", i), i, |b, &i| { let slab = RwLock::new(slab::Slab::new()); b.iter(|| { let v: Vec<_> = (0..i).map(|i| slab.write().unwrap().insert(i)).collect(); for i in v { slab.write().unwrap().remove(i); } }); }); } group.finish(); } criterion_group!(benches, insert_remove_local, insert_remove_single_thread); criterion_main!(benches);