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