1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
use PhantomData;
use crateRunInitial;
/// A benchmark run will execute a specific number of multithreaded iterations on every
/// thread of a [`crate::ThreadPool`].
///
/// A `Run` must first be configured, after which it can be executed. The run logic separates
/// preparation (unmeasured) from execution (measured) phases.
///
/// # Execution phases
///
/// 1. **Thread Preparation**: Each thread executes the thread preparation callback once
/// 2. **Iteration Preparation**: Each thread prepares state for every iteration (unmeasured)
/// 3. **Measurement Begin**: Measurement wrapper begin callback is called per thread
/// 4. **Iteration Execution**: All iterations are executed (measured)
/// 5. **Measurement End**: Measurement wrapper end callback is called per thread
/// 6. **Cleanup**: All cleanup state is dropped (unmeasured)
///
/// # Examples
///
/// Basic usage with atomic counter:
/// ```
/// use std::sync::Arc;
/// use std::sync::atomic::{AtomicU64, Ordering};
///
/// use many_cpus::SystemHardware;
/// use par_bench::{Run, ThreadPool};
///
/// # fn main() {
/// let mut pool = ThreadPool::new(&SystemHardware::current().processors());
/// let counter = Arc::new(AtomicU64::new(0));
///
/// let run = Run::new()
/// .prepare_thread({
/// let counter = Arc::clone(&counter);
/// move |_| Arc::clone(&counter)
/// })
/// .prepare_iter(|args| Arc::clone(args.thread_state()))
/// .iter(|mut args| {
/// args.iter_state().fetch_add(1, Ordering::Relaxed);
/// });
///
/// let results = run.execute_on(&mut pool, 1000);
/// println!("Executed in: {:?}", results.mean_duration());
/// # }
/// ```
///
/// With measurement wrapper for custom metrics:
/// ```
/// use std::time::Instant;
///
/// use many_cpus::SystemHardware;
/// use par_bench::{Run, ThreadPool};
///
/// # fn main() {
/// let mut pool = ThreadPool::new(&SystemHardware::current().processors());
///
/// let run = Run::new()
/// .measure_wrapper(|_| Instant::now(), |start| start.elapsed())
/// .iter(|_| {
/// // Simulate some work
/// std::hint::black_box((0..100).sum::<i32>());
/// });
///
/// let results = run.execute_on(&mut pool, 1000);
///
/// // Access per-thread measurement data
/// for elapsed in results.measure_outputs() {
/// println!("Thread execution time: {:?}", elapsed);
/// }
/// # }
/// ```