| //! Benchmarks to track basic performance across changes. |
| //! |
| //! Slightly based on the <background.rs> benchmarks, but simplified and stripped down to run |
| //! reasonably fast. |
| |
| use std::sync::atomic::{AtomicBool, Ordering}; |
| use std::sync::Arc; |
| |
| use arc_swap::access::{Access, Map}; |
| use arc_swap::cache::Cache; |
| use arc_swap::ArcSwap; |
| use criterion::{black_box, criterion_group, criterion_main, Criterion}; |
| use crossbeam_utils::thread; |
| |
| /// Execute a group of measurements |
| /// |
| /// It expects any kind of „environment“ is already in place for it. |
| fn batch(c: &mut Criterion, name: &str, shared_number: &ArcSwap<usize>) { |
| let mut g = c.benchmark_group(name); |
| |
| g.bench_function("load", |b| { |
| b.iter(|| { |
| black_box(shared_number.load()); |
| }) |
| }); |
| g.bench_function("load_full", |b| { |
| b.iter(|| { |
| black_box(shared_number.load_full()); |
| }) |
| }); |
| g.bench_function("load_many", |b| { |
| // Here we simulate running out of the debt slots scenario |
| const MANY: usize = 32; |
| let mut guards = Vec::with_capacity(MANY); |
| b.iter(|| { |
| guards.push(black_box(shared_number.load())); |
| if guards.len() == MANY { |
| guards.clear(); |
| } |
| }) |
| }); |
| g.bench_function("store", |b| { |
| b.iter(|| { |
| black_box(shared_number.store(Arc::new(42))); |
| }) |
| }); |
| g.bench_function("cache", |b| { |
| let mut cache = Cache::new(shared_number); |
| b.iter(|| { |
| black_box(cache.load()); |
| }) |
| }); |
| |
| g.finish(); |
| } |
| |
| fn with_background<F: Fn(&ArcSwap<usize>) + Sync>( |
| c: &mut Criterion, |
| name: &str, |
| cnt: usize, |
| noise: F, |
| ) { |
| let stop = AtomicBool::new(false); |
| let shared_number = ArcSwap::from_pointee(42); |
| thread::scope(|s| { |
| // Start some background noise threads, to contend the arc swap. |
| for _ in 0..cnt { |
| s.spawn(|_| { |
| while !stop.load(Ordering::Relaxed) { |
| noise(&shared_number); |
| } |
| }); |
| } |
| |
| // Perform the benchmarks |
| batch(c, name, &shared_number); |
| |
| // Ask the threads to terminate, so they don't disturb any other banchmarks |
| stop.store(true, Ordering::Relaxed); |
| }) |
| .unwrap(); |
| } |
| |
| fn utilities(c: &mut Criterion) { |
| let mut g = c.benchmark_group("utilities"); |
| |
| struct Composed { |
| val: i32, |
| } |
| |
| g.bench_function("access-map", |b| { |
| let a = Arc::new(ArcSwap::from_pointee(Composed { val: 42 })); |
| let m = Map::new(Arc::clone(&a), |c: &Composed| &c.val); |
| b.iter(|| { |
| let g = black_box(m.load()); |
| assert_eq!(42, *g); |
| }); |
| }); |
| } |
| |
| fn benchmark(c: &mut Criterion) { |
| batch(c, "uncontended", &ArcSwap::from_pointee(42)); |
| with_background(c, "concurrent_loads", 2, |s| { |
| black_box(s.load()); |
| }); |
| with_background(c, "concurrent_store", 1, |s| { |
| black_box(s.store(Arc::new(42))); |
| }); |
| utilities(c); |
| } |
| |
| criterion_group!(benches, benchmark); |
| criterion_main!(benches); |