1 use std::sync::atomic::{AtomicBool, Ordering};
2 use std::sync::{Arc, Mutex};
3 
4 use arc_swap::{ArcSwap, ArcSwapOption, Cache};
5 use criterion::{black_box, criterion_group, criterion_main, Criterion};
6 use crossbeam_utils::thread;
7 use once_cell::sync::Lazy;
8 
9 // Mostly a leftover from earlier times, but it still allows one to tweak the number of ops per one
10 // iteration of the benchmark easily, so it's left in here.
11 const ITERS: usize = 1;
12 
13 macro_rules! method {
14     ($c: expr, $name:ident) => {{
15         let mut g = $c.benchmark_group(&format!("{}_{}", NAME, stringify!($name)));
16         noise(&mut g, "r1", 1, 0, 0, $name);
17         noise(&mut g, "r3", 3, 0, 0, $name);
18         noise(&mut g, "l1", 0, 1, 0, $name);
19         noise(&mut g, "l3", 0, 3, 0, $name);
20         noise(&mut g, "rw", 1, 0, 1, $name);
21         noise(&mut g, "lw", 0, 1, 1, $name);
22         noise(&mut g, "w2", 0, 0, 2, $name);
23         g.bench_function("uncontended", |b| b.iter($name));
24         g.finish();
25     }};
26 }
27 
28 macro_rules! noise {
29     () => {
30         use criterion::measurement::Measurement;
31         use criterion::BenchmarkGroup;
32 
33         use super::{thread, Arc, AtomicBool, Ordering, ITERS};
34 
35         fn noise<M: Measurement, F: Fn()>(
36             g: &mut BenchmarkGroup<M>,
37             name: &str,
38             readers: usize,
39             leasers: usize,
40             writers: usize,
41             f: F,
42         ) {
43             let flag = Arc::new(AtomicBool::new(true));
44             thread::scope(|s| {
45                 for _ in 0..readers {
46                     s.spawn(|_| {
47                         while flag.load(Ordering::Relaxed) {
48                             read();
49                         }
50                     });
51                 }
52                 for _ in 0..leasers {
53                     s.spawn(|_| {
54                         while flag.load(Ordering::Relaxed) {
55                             lease();
56                         }
57                     });
58                 }
59                 for _ in 0..writers {
60                     s.spawn(|_| {
61                         while flag.load(Ordering::Relaxed) {
62                             write();
63                         }
64                     });
65                 }
66                 g.bench_function(name, |b| b.iter(&f));
67                 flag.store(false, Ordering::Relaxed);
68             })
69             .unwrap();
70         }
71     };
72 }
73 
74 macro_rules! strategy {
75     ($name: ident, $type: ty) => {
76         mod $name {
77             use super::*;
78 
79             static A: Lazy<$type> = Lazy::new(|| <$type>::from_pointee(0));
80             const NAME: &str = stringify!($name);
81 
82             fn lease() {
83                 for _ in 0..ITERS {
84                     black_box(**A.load());
85                 }
86             }
87 
88             // Leases kind of degrade in performance if there are multiple on the same thread.
89             fn four_leases() {
90                 for _ in 0..ITERS {
91                     let l1 = A.load();
92                     let l2 = A.load();
93                     let l3 = A.load();
94                     let l4 = A.load();
95                     black_box((**l1, **l2, **l3, **l4));
96                 }
97             }
98 
99             fn read() {
100                 for _ in 0..ITERS {
101                     black_box(A.load_full());
102                 }
103             }
104 
105             fn write() {
106                 for _ in 0..ITERS {
107                     black_box(A.store(Arc::new(0)));
108                 }
109             }
110 
111             noise!();
112 
113             pub fn run_all(c: &mut Criterion) {
114                 method!(c, read);
115                 method!(c, write);
116                 method!(c, lease);
117                 method!(c, four_leases);
118             }
119         }
120     };
121 }
122 
123 strategy!(arc_swap_b, ArcSwap::<usize>);
124 
125 mod arc_swap_option {
126     use super::{black_box, ArcSwapOption, Criterion, Lazy};
127 
128     static A: Lazy<ArcSwapOption<usize>> = Lazy::new(|| ArcSwapOption::from(None));
129     const NAME: &str = "arc_swap_option";
130 
lease()131     fn lease() {
132         for _ in 0..ITERS {
133             black_box(A.load().as_ref().map(|l| **l).unwrap_or(0));
134         }
135     }
136 
read()137     fn read() {
138         for _ in 0..ITERS {
139             black_box(A.load_full().map(|a| -> usize { *a }).unwrap_or(0));
140         }
141     }
142 
write()143     fn write() {
144         for _ in 0..ITERS {
145             black_box(A.store(Some(Arc::new(0))));
146         }
147     }
148 
149     noise!();
150 
run_all(c: &mut Criterion)151     pub fn run_all(c: &mut Criterion) {
152         method!(c, read);
153         method!(c, write);
154         method!(c, lease);
155     }
156 }
157 
158 mod arc_swap_cached {
159     use super::{black_box, ArcSwap, Cache, Criterion, Lazy};
160 
161     static A: Lazy<ArcSwap<usize>> = Lazy::new(|| ArcSwap::from_pointee(0));
162     const NAME: &str = "arc_swap_cached";
163 
read()164     fn read() {
165         let mut cache = Cache::from(&A as &ArcSwap<usize>);
166         for _ in 0..ITERS {
167             black_box(Arc::clone(cache.load()));
168         }
169     }
170 
lease()171     fn lease() {
172         for _ in 0..ITERS {
173             black_box(**A.load());
174         }
175     }
176 
write()177     fn write() {
178         for _ in 0..ITERS {
179             black_box(A.store(Arc::new(0)));
180         }
181     }
182 
183     noise!();
184 
run_all(c: &mut Criterion)185     pub fn run_all(c: &mut Criterion) {
186         method!(c, read);
187         method!(c, write);
188     }
189 }
190 
191 mod mutex {
192     use super::{black_box, Criterion, Lazy, Mutex};
193 
194     static M: Lazy<Mutex<Arc<usize>>> = Lazy::new(|| Mutex::new(Arc::new(0)));
195     const NAME: &str = "mutex";
196 
lease()197     fn lease() {
198         for _ in 0..ITERS {
199             black_box(**M.lock().unwrap());
200         }
201     }
202 
read()203     fn read() {
204         for _ in 0..ITERS {
205             black_box(Arc::clone(&*M.lock().unwrap()));
206         }
207     }
208 
write()209     fn write() {
210         for _ in 0..ITERS {
211             black_box(*M.lock().unwrap() = Arc::new(42));
212         }
213     }
214 
215     noise!();
216 
run_all(c: &mut Criterion)217     pub fn run_all(c: &mut Criterion) {
218         method!(c, read);
219         method!(c, write);
220     }
221 }
222 
223 mod parking_mutex {
224     use parking_lot::Mutex as ParkingMutex;
225 
226     use super::{black_box, Criterion, Lazy};
227 
228     static M: Lazy<ParkingMutex<Arc<usize>>> = Lazy::new(|| ParkingMutex::new(Arc::new(0)));
229     const NAME: &str = "parking_mutex";
230 
lease()231     fn lease() {
232         for _ in 0..ITERS {
233             black_box(**M.lock());
234         }
235     }
236 
read()237     fn read() {
238         for _ in 0..ITERS {
239             black_box(Arc::clone(&*M.lock()));
240         }
241     }
242 
write()243     fn write() {
244         for _ in 0..ITERS {
245             black_box(*M.lock() = Arc::new(42));
246         }
247     }
248 
249     noise!();
250 
run_all(c: &mut Criterion)251     pub fn run_all(c: &mut Criterion) {
252         method!(c, read);
253         method!(c, write);
254     }
255 }
256 
257 mod rwlock {
258     use std::sync::RwLock;
259 
260     use super::{black_box, Criterion, Lazy};
261 
262     static L: Lazy<RwLock<Arc<usize>>> = Lazy::new(|| RwLock::new(Arc::new(0)));
263     const NAME: &str = "rwlock";
264 
lease()265     fn lease() {
266         for _ in 0..ITERS {
267             black_box(**L.read().unwrap());
268         }
269     }
270 
read()271     fn read() {
272         for _ in 0..ITERS {
273             black_box(Arc::clone(&*L.read().unwrap()));
274         }
275     }
276 
write()277     fn write() {
278         for _ in 0..ITERS {
279             black_box(*L.write().unwrap() = Arc::new(42));
280         }
281     }
282 
283     noise!();
284 
run_all(c: &mut Criterion)285     pub fn run_all(c: &mut Criterion) {
286         method!(c, read);
287         method!(c, write);
288     }
289 }
290 
291 mod parking_rwlock {
292     use parking_lot::RwLock;
293 
294     use super::{black_box, Criterion, Lazy};
295 
296     static L: Lazy<RwLock<Arc<usize>>> = Lazy::new(|| RwLock::new(Arc::new(0)));
297     const NAME: &str = "parking_rwlock";
298 
lease()299     fn lease() {
300         for _ in 0..ITERS {
301             black_box(**L.read());
302         }
303     }
304 
read()305     fn read() {
306         for _ in 0..ITERS {
307             black_box(Arc::clone(&*L.read()));
308         }
309     }
310 
write()311     fn write() {
312         for _ in 0..ITERS {
313             black_box(*L.write() = Arc::new(42));
314         }
315     }
316 
317     noise!();
318 
run_all(c: &mut Criterion)319     pub fn run_all(c: &mut Criterion) {
320         method!(c, read);
321         method!(c, write);
322     }
323 }
324 
325 criterion_group!(
326     benches,
327     arc_swap_b::run_all,
328     arc_swap_option::run_all,
329     arc_swap_cached::run_all,
330     mutex::run_all,
331     parking_mutex::run_all,
332     rwlock::run_all,
333     parking_rwlock::run_all,
334 );
335 criterion_main!(benches);
336