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> { |
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 | |
43 | const N_INSERTIONS: &[usize] = &[100, 300, 500, 700, 1000, 3000, 5000]; |
44 | |
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 | |
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 | |