1use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
2use std::{
3 sync::{Arc, Barrier, RwLock},
4 thread,
5 time::{Duration, Instant},
6};
7
8#[derive(Clone)]
9struct MultithreadedBench<T> {
10 start: Arc<Barrier>,
11 end: Arc<Barrier>,
12 slab: Arc<T>,
13}
14
15impl<T: Send + Sync + 'static> MultithreadedBench<T> {
16 fn new(slab: Arc<T>) -> Self {
17 Self {
18 start: Arc::new(Barrier::new(5)),
19 end: Arc::new(Barrier::new(5)),
20 slab,
21 }
22 }
23
24 fn thread(&self, f: impl FnOnce(&Barrier, &T) + Send + 'static) -> &Self {
25 let start = self.start.clone();
26 let end = self.end.clone();
27 let slab = self.slab.clone();
28 thread::spawn(move || {
29 f(&start, &*slab);
30 end.wait();
31 });
32 self
33 }
34
35 fn run(&self) -> Duration {
36 self.start.wait();
37 let t0 = Instant::now();
38 self.end.wait();
39 t0.elapsed()
40 }
41}
42
43const N_INSERTIONS: &[usize] = &[100, 300, 500, 700, 1000, 3000, 5000];
44
45fn insert_remove_local(c: &mut Criterion) {
46 // the 10000-insertion benchmark takes the `slab` crate about an hour to
47 // run; don't run this unless you're prepared for that...
48 // const N_INSERTIONS: &'static [usize] = &[100, 500, 1000, 5000, 10000];
49 let mut group = c.benchmark_group("insert_remove_local");
50 let g = group.measurement_time(Duration::from_secs(15));
51
52 for i in N_INSERTIONS {
53 g.bench_with_input(BenchmarkId::new("sharded_slab", i), i, |b, &i| {
54 b.iter_custom(|iters| {
55 let mut total = Duration::from_secs(0);
56 for _ in 0..iters {
57 let bench = MultithreadedBench::new(Arc::new(sharded_slab::Slab::new()));
58 let elapsed = bench
59 .thread(move |start, slab| {
60 start.wait();
61 let v: Vec<_> = (0..i).map(|i| slab.insert(i).unwrap()).collect();
62 for i in v {
63 slab.remove(i);
64 }
65 })
66 .thread(move |start, slab| {
67 start.wait();
68 let v: Vec<_> = (0..i).map(|i| slab.insert(i).unwrap()).collect();
69 for i in v {
70 slab.remove(i);
71 }
72 })
73 .thread(move |start, slab| {
74 start.wait();
75 let v: Vec<_> = (0..i).map(|i| slab.insert(i).unwrap()).collect();
76 for i in v {
77 slab.remove(i);
78 }
79 })
80 .thread(move |start, slab| {
81 start.wait();
82 let v: Vec<_> = (0..i).map(|i| slab.insert(i).unwrap()).collect();
83 for i in v {
84 slab.remove(i);
85 }
86 })
87 .run();
88 total += elapsed;
89 }
90 total
91 })
92 });
93 g.bench_with_input(BenchmarkId::new("slab_biglock", i), i, |b, &i| {
94 b.iter_custom(|iters| {
95 let mut total = Duration::from_secs(0);
96 let i = i;
97 for _ in 0..iters {
98 let bench = MultithreadedBench::new(Arc::new(RwLock::new(slab::Slab::new())));
99 let elapsed = bench
100 .thread(move |start, slab| {
101 start.wait();
102 let v: Vec<_> =
103 (0..i).map(|i| slab.write().unwrap().insert(i)).collect();
104 for i in v {
105 slab.write().unwrap().remove(i);
106 }
107 })
108 .thread(move |start, slab| {
109 start.wait();
110 let v: Vec<_> =
111 (0..i).map(|i| slab.write().unwrap().insert(i)).collect();
112 for i in v {
113 slab.write().unwrap().remove(i);
114 }
115 })
116 .thread(move |start, slab| {
117 start.wait();
118 let v: Vec<_> =
119 (0..i).map(|i| slab.write().unwrap().insert(i)).collect();
120 for i in v {
121 slab.write().unwrap().remove(i);
122 }
123 })
124 .thread(move |start, slab| {
125 start.wait();
126 let v: Vec<_> =
127 (0..i).map(|i| slab.write().unwrap().insert(i)).collect();
128 for i in v {
129 slab.write().unwrap().remove(i);
130 }
131 })
132 .run();
133 total += elapsed;
134 }
135 total
136 })
137 });
138 }
139 group.finish();
140}
141
142fn insert_remove_single_thread(c: &mut Criterion) {
143 // the 10000-insertion benchmark takes the `slab` crate about an hour to
144 // run; don't run this unless you're prepared for that...
145 // const N_INSERTIONS: &'static [usize] = &[100, 500, 1000, 5000, 10000];
146 let mut group = c.benchmark_group("insert_remove_single_threaded");
147
148 for i in N_INSERTIONS {
149 group.bench_with_input(BenchmarkId::new("sharded_slab", i), i, |b, &i| {
150 let slab = sharded_slab::Slab::new();
151 b.iter(|| {
152 let v: Vec<_> = (0..i).map(|i| slab.insert(i).unwrap()).collect();
153 for i in v {
154 slab.remove(i);
155 }
156 });
157 });
158 group.bench_with_input(BenchmarkId::new("slab_no_lock", i), i, |b, &i| {
159 let mut slab = slab::Slab::new();
160 b.iter(|| {
161 let v: Vec<_> = (0..i).map(|i| slab.insert(i)).collect();
162 for i in v {
163 slab.remove(i);
164 }
165 });
166 });
167 group.bench_with_input(BenchmarkId::new("slab_uncontended", i), i, |b, &i| {
168 let slab = RwLock::new(slab::Slab::new());
169 b.iter(|| {
170 let v: Vec<_> = (0..i).map(|i| slab.write().unwrap().insert(i)).collect();
171 for i in v {
172 slab.write().unwrap().remove(i);
173 }
174 });
175 });
176 }
177 group.finish();
178}
179
180criterion_group!(benches, insert_remove_local, insert_remove_single_thread);
181criterion_main!(benches);
182