1
use std::hint::black_box;
2
use std::sync::Arc;
3
use std::sync::Barrier;
4
use std::sync::atomic::AtomicBool;
5
use std::sync::atomic::Ordering;
6
use std::thread;
7

            
8
use criterion::Criterion;
9

            
10
/// The number of increments each thread performs per measured iteration.
11
pub const NUM_ITERATIONS: usize = 100000;
12
/// The number of threads to use for the benchmarks.
13
pub const THREADS: [usize; 6] = [1, 2, 4, 8, 16, 32];
14

            
15
/// Execute the benchmark for a given counter implementation.
16
///
17
/// Spawns `num_threads` worker threads that all hammer `increment` on a shared
18
/// counter `num_iterations` times per measured iteration, isolating the
19
/// cross-thread contention cost of the increment operation.
20
pub fn benchmark<T, I>(
21
    c: &mut Criterion,
22
    name: &str,
23
    shared: T,
24
    increment: I,
25
    num_threads: usize,
26
    num_iterations: usize,
27
) where
28
    T: Clone + Send + 'static,
29
    I: FnOnce(&T) + Send + Copy + 'static,
30
{
31
    // Share threads to avoid the overhead of spawning per measured iteration.
32
    let mut threads = vec![];
33

            
34
    #[derive(Clone)]
35
    struct ThreadInfo<T> {
36
        busy: Arc<AtomicBool>,
37
        begin_barrier: Arc<Barrier>,
38
        end_barrier: Arc<Barrier>,
39
        shared: T,
40
    }
41

            
42
    let info = ThreadInfo {
43
        busy: Arc::new(AtomicBool::new(true)),
44
        begin_barrier: Arc::new(Barrier::new(num_threads + 1)),
45
        end_barrier: Arc::new(Barrier::new(num_threads + 1)),
46
        shared,
47
    };
48

            
49
    for _ in 0..num_threads {
50
        let info = info.clone();
51
        threads.push(thread::spawn(move || {
52
            loop {
53
                info.begin_barrier.wait();
54

            
55
                if !info.busy.load(Ordering::SeqCst) {
56
                    // Quit the thread.
57
                    break;
58
                }
59

            
60
                // We execute it a fixed number of times.
61
                for _ in 0..num_iterations {
62
                    increment(&info.shared);
63
                    black_box(());
64
                }
65

            
66
                info.end_barrier.wait();
67
            }
68
        }));
69
    }
70

            
71
    c.bench_function(format!("{name} {num_threads} {num_iterations}").as_str(), |bencher| {
72
        bencher.iter(|| {
73
            info.begin_barrier.wait();
74

            
75
            info.end_barrier.wait();
76
        });
77
    });
78

            
79
    // Tell the threads to quit and wait for them to join.
80
    info.busy.store(false, Ordering::SeqCst);
81
    info.begin_barrier.wait();
82

            
83
    for thread in threads {
84
        thread.join().unwrap();
85
    }
86
}