1 use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
2 use std::{
3     sync::{Arc, Barrier, RwLock},
4     thread,
5     time::{Duration, Instant},
6 };
7 
8 #[derive(Clone)]
9 struct MultithreadedBench<T> {
10     start: Arc<Barrier>,
11     end: Arc<Barrier>,
12     slab: Arc<T>,
13 }
14 
15 impl<T: Send + Sync + 'static> MultithreadedBench<T> {
new(slab: Arc<T>) -> Self16     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 
thread(&self, f: impl FnOnce(&Barrier, &T) + Send + 'static) -> &Self24     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 
run(&self) -> Duration35     fn run(&self) -> Duration {
36         self.start.wait();
37         let t0 = Instant::now();
38         self.end.wait();
39         t0.elapsed()
40     }
41 }
42 
43 const N_INSERTIONS: &[usize] = &[100, 300, 500, 700, 1000, 3000, 5000];
44 
insert_remove_local(c: &mut Criterion)45 fn 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 
insert_remove_single_thread(c: &mut Criterion)142 fn 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 
180 criterion_group!(benches, insert_remove_local, insert_remove_single_thread);
181 criterion_main!(benches);
182