blob: 987439a65d9f2d12f3f1e633ccacc96fd909fdcc [file] [log] [blame]
//! 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);