335 lines
7.8 KiB
Rust
335 lines
7.8 KiB
Rust
use std::sync::atomic::{AtomicBool, Ordering};
|
|
use std::sync::{Arc, Mutex};
|
|
|
|
use arc_swap::{ArcSwap, ArcSwapOption, Cache};
|
|
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
|
use crossbeam_utils::thread;
|
|
use once_cell::sync::Lazy;
|
|
|
|
// Mostly a leftover from earlier times, but it still allows one to tweak the number of ops per one
|
|
// iteration of the benchmark easily, so it's left in here.
|
|
const ITERS: usize = 1;
|
|
|
|
macro_rules! method {
|
|
($c: expr, $name:ident) => {{
|
|
let mut g = $c.benchmark_group(&format!("{}_{}", NAME, stringify!($name)));
|
|
noise(&mut g, "r1", 1, 0, 0, $name);
|
|
noise(&mut g, "r3", 3, 0, 0, $name);
|
|
noise(&mut g, "l1", 0, 1, 0, $name);
|
|
noise(&mut g, "l3", 0, 3, 0, $name);
|
|
noise(&mut g, "rw", 1, 0, 1, $name);
|
|
noise(&mut g, "lw", 0, 1, 1, $name);
|
|
noise(&mut g, "w2", 0, 0, 2, $name);
|
|
g.bench_function("uncontended", |b| b.iter($name));
|
|
g.finish();
|
|
}};
|
|
}
|
|
|
|
macro_rules! noise {
|
|
() => {
|
|
use criterion::measurement::Measurement;
|
|
use criterion::BenchmarkGroup;
|
|
|
|
use super::{thread, Arc, AtomicBool, Ordering, ITERS};
|
|
|
|
fn noise<M: Measurement, F: Fn()>(
|
|
g: &mut BenchmarkGroup<M>,
|
|
name: &str,
|
|
readers: usize,
|
|
leasers: usize,
|
|
writers: usize,
|
|
f: F,
|
|
) {
|
|
let flag = Arc::new(AtomicBool::new(true));
|
|
thread::scope(|s| {
|
|
for _ in 0..readers {
|
|
s.spawn(|_| {
|
|
while flag.load(Ordering::Relaxed) {
|
|
read();
|
|
}
|
|
});
|
|
}
|
|
for _ in 0..leasers {
|
|
s.spawn(|_| {
|
|
while flag.load(Ordering::Relaxed) {
|
|
lease();
|
|
}
|
|
});
|
|
}
|
|
for _ in 0..writers {
|
|
s.spawn(|_| {
|
|
while flag.load(Ordering::Relaxed) {
|
|
write();
|
|
}
|
|
});
|
|
}
|
|
g.bench_function(name, |b| b.iter(&f));
|
|
flag.store(false, Ordering::Relaxed);
|
|
})
|
|
.unwrap();
|
|
}
|
|
};
|
|
}
|
|
|
|
macro_rules! strategy {
|
|
($name: ident, $type: ty) => {
|
|
mod $name {
|
|
use super::*;
|
|
|
|
static A: Lazy<$type> = Lazy::new(|| <$type>::from_pointee(0));
|
|
const NAME: &str = stringify!($name);
|
|
|
|
fn lease() {
|
|
for _ in 0..ITERS {
|
|
black_box(**A.load());
|
|
}
|
|
}
|
|
|
|
// Leases kind of degrade in performance if there are multiple on the same thread.
|
|
fn four_leases() {
|
|
for _ in 0..ITERS {
|
|
let l1 = A.load();
|
|
let l2 = A.load();
|
|
let l3 = A.load();
|
|
let l4 = A.load();
|
|
black_box((**l1, **l2, **l3, **l4));
|
|
}
|
|
}
|
|
|
|
fn read() {
|
|
for _ in 0..ITERS {
|
|
black_box(A.load_full());
|
|
}
|
|
}
|
|
|
|
fn write() {
|
|
for _ in 0..ITERS {
|
|
black_box(A.store(Arc::new(0)));
|
|
}
|
|
}
|
|
|
|
noise!();
|
|
|
|
pub fn run_all(c: &mut Criterion) {
|
|
method!(c, read);
|
|
method!(c, write);
|
|
method!(c, lease);
|
|
method!(c, four_leases);
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
strategy!(arc_swap_b, ArcSwap::<usize>);
|
|
|
|
mod arc_swap_option {
|
|
use super::{black_box, ArcSwapOption, Criterion, Lazy};
|
|
|
|
static A: Lazy<ArcSwapOption<usize>> = Lazy::new(|| ArcSwapOption::from(None));
|
|
const NAME: &str = "arc_swap_option";
|
|
|
|
fn lease() {
|
|
for _ in 0..ITERS {
|
|
black_box(A.load().as_ref().map(|l| **l).unwrap_or(0));
|
|
}
|
|
}
|
|
|
|
fn read() {
|
|
for _ in 0..ITERS {
|
|
black_box(A.load_full().map(|a| -> usize { *a }).unwrap_or(0));
|
|
}
|
|
}
|
|
|
|
fn write() {
|
|
for _ in 0..ITERS {
|
|
black_box(A.store(Some(Arc::new(0))));
|
|
}
|
|
}
|
|
|
|
noise!();
|
|
|
|
pub fn run_all(c: &mut Criterion) {
|
|
method!(c, read);
|
|
method!(c, write);
|
|
method!(c, lease);
|
|
}
|
|
}
|
|
|
|
mod arc_swap_cached {
|
|
use super::{black_box, ArcSwap, Cache, Criterion, Lazy};
|
|
|
|
static A: Lazy<ArcSwap<usize>> = Lazy::new(|| ArcSwap::from_pointee(0));
|
|
const NAME: &str = "arc_swap_cached";
|
|
|
|
fn read() {
|
|
let mut cache = Cache::from(&A as &ArcSwap<usize>);
|
|
for _ in 0..ITERS {
|
|
black_box(Arc::clone(cache.load()));
|
|
}
|
|
}
|
|
|
|
fn lease() {
|
|
for _ in 0..ITERS {
|
|
black_box(**A.load());
|
|
}
|
|
}
|
|
|
|
fn write() {
|
|
for _ in 0..ITERS {
|
|
black_box(A.store(Arc::new(0)));
|
|
}
|
|
}
|
|
|
|
noise!();
|
|
|
|
pub fn run_all(c: &mut Criterion) {
|
|
method!(c, read);
|
|
method!(c, write);
|
|
}
|
|
}
|
|
|
|
mod mutex {
|
|
use super::{black_box, Criterion, Lazy, Mutex};
|
|
|
|
static M: Lazy<Mutex<Arc<usize>>> = Lazy::new(|| Mutex::new(Arc::new(0)));
|
|
const NAME: &str = "mutex";
|
|
|
|
fn lease() {
|
|
for _ in 0..ITERS {
|
|
black_box(**M.lock().unwrap());
|
|
}
|
|
}
|
|
|
|
fn read() {
|
|
for _ in 0..ITERS {
|
|
black_box(Arc::clone(&*M.lock().unwrap()));
|
|
}
|
|
}
|
|
|
|
fn write() {
|
|
for _ in 0..ITERS {
|
|
black_box(*M.lock().unwrap() = Arc::new(42));
|
|
}
|
|
}
|
|
|
|
noise!();
|
|
|
|
pub fn run_all(c: &mut Criterion) {
|
|
method!(c, read);
|
|
method!(c, write);
|
|
}
|
|
}
|
|
|
|
mod parking_mutex {
|
|
use parking_lot::Mutex as ParkingMutex;
|
|
|
|
use super::{black_box, Criterion, Lazy};
|
|
|
|
static M: Lazy<ParkingMutex<Arc<usize>>> = Lazy::new(|| ParkingMutex::new(Arc::new(0)));
|
|
const NAME: &str = "parking_mutex";
|
|
|
|
fn lease() {
|
|
for _ in 0..ITERS {
|
|
black_box(**M.lock());
|
|
}
|
|
}
|
|
|
|
fn read() {
|
|
for _ in 0..ITERS {
|
|
black_box(Arc::clone(&*M.lock()));
|
|
}
|
|
}
|
|
|
|
fn write() {
|
|
for _ in 0..ITERS {
|
|
black_box(*M.lock() = Arc::new(42));
|
|
}
|
|
}
|
|
|
|
noise!();
|
|
|
|
pub fn run_all(c: &mut Criterion) {
|
|
method!(c, read);
|
|
method!(c, write);
|
|
}
|
|
}
|
|
|
|
mod rwlock {
|
|
use std::sync::RwLock;
|
|
|
|
use super::{black_box, Criterion, Lazy};
|
|
|
|
static L: Lazy<RwLock<Arc<usize>>> = Lazy::new(|| RwLock::new(Arc::new(0)));
|
|
const NAME: &str = "rwlock";
|
|
|
|
fn lease() {
|
|
for _ in 0..ITERS {
|
|
black_box(**L.read().unwrap());
|
|
}
|
|
}
|
|
|
|
fn read() {
|
|
for _ in 0..ITERS {
|
|
black_box(Arc::clone(&*L.read().unwrap()));
|
|
}
|
|
}
|
|
|
|
fn write() {
|
|
for _ in 0..ITERS {
|
|
black_box(*L.write().unwrap() = Arc::new(42));
|
|
}
|
|
}
|
|
|
|
noise!();
|
|
|
|
pub fn run_all(c: &mut Criterion) {
|
|
method!(c, read);
|
|
method!(c, write);
|
|
}
|
|
}
|
|
|
|
mod parking_rwlock {
|
|
use parking_lot::RwLock;
|
|
|
|
use super::{black_box, Criterion, Lazy};
|
|
|
|
static L: Lazy<RwLock<Arc<usize>>> = Lazy::new(|| RwLock::new(Arc::new(0)));
|
|
const NAME: &str = "parking_rwlock";
|
|
|
|
fn lease() {
|
|
for _ in 0..ITERS {
|
|
black_box(**L.read());
|
|
}
|
|
}
|
|
|
|
fn read() {
|
|
for _ in 0..ITERS {
|
|
black_box(Arc::clone(&*L.read()));
|
|
}
|
|
}
|
|
|
|
fn write() {
|
|
for _ in 0..ITERS {
|
|
black_box(*L.write() = Arc::new(42));
|
|
}
|
|
}
|
|
|
|
noise!();
|
|
|
|
pub fn run_all(c: &mut Criterion) {
|
|
method!(c, read);
|
|
method!(c, write);
|
|
}
|
|
}
|
|
|
|
criterion_group!(
|
|
benches,
|
|
arc_swap_b::run_all,
|
|
arc_swap_option::run_all,
|
|
arc_swap_cached::run_all,
|
|
mutex::run_all,
|
|
parking_mutex::run_all,
|
|
rwlock::run_all,
|
|
parking_rwlock::run_all,
|
|
);
|
|
criterion_main!(benches);
|