1use std::{
2 sync::{
3 Arc,
4 atomic::{AtomicI32, Ordering},
5 },
6 time::Instant,
7};
8
9use hypercounter::HyperCounter;
10
11const THREADS: i32 = 12;
12
13fn pretty_int(i: i32) -> String {
14 let mut s = String::new();
15 let i_str = i.to_string();
16 let chars_rev = i_str.chars().rev().enumerate();
17
18 for (idx, val) in chars_rev {
19 if idx != 0 && idx % 3 == 0 {
20 s.insert(0, ',');
21 }
22 s.insert(0, val);
23 }
24
25 s
26}
27
28fn main() {
29 load_ops_per_second_single_threaded_fetch_add_single_key();
30 load_ops_per_second_single_threaded_fetch_add_multi_key();
31 load_ops_per_second_single_threaded_insert();
32 load_ops_per_second_single_threaded_remove();
33 load_ops_per_second_single_threaded_churn();
34 load_ops_per_second_multi_threaded_fetch_add_single_key();
35 load_ops_per_second_multi_threaded_fetch_add_multi_key();
36 load_ops_per_second_multi_threaded_insert();
37 load_ops_per_second_multi_threaded_remove();
38 load_ops_per_second_multi_threaded_churn();
39}
40
41fn load_ops_per_second_single_threaded_fetch_add_single_key() {
42 let counter: HyperCounter<i32, AtomicI32> = HyperCounter::new();
43
44 let now = Instant::now();
45 let mut i = 0;
46
47 loop {
48 counter.fetch_add(1, 1, Ordering::Relaxed);
49 i += 1;
50
51 if now.elapsed().as_secs() >= 1 {
52 break;
53 }
54 }
55
56 println!("Single-threaded single-key load ops/sec: {}", pretty_int(i));
57}
58
59fn load_ops_per_second_single_threaded_fetch_add_multi_key() {
60 let counter: HyperCounter<i32, AtomicI32> = HyperCounter::new();
61
62 let now = Instant::now();
63 let mut i = 0;
64
65 loop {
66 let key = i % 1000;
67 counter.fetch_add(key, 1, Ordering::Relaxed);
68 i += 1;
69
70 if now.elapsed().as_secs() >= 1 {
71 break;
72 }
73 }
74
75 println!("Single-threaded multi-key load ops/sec: {}", pretty_int(i));
76}
77
78fn load_ops_per_second_single_threaded_insert() {
79 let counter: HyperCounter<i32, AtomicI32> = HyperCounter::new();
80
81 let now = Instant::now();
82 let mut i = 0;
83
84 loop {
85 counter.fetch_add(i, 1, Ordering::Relaxed);
86 i += 1;
87
88 if now.elapsed().as_secs() >= 1 {
89 break;
90 }
91 }
92
93 println!("Single-threaded insert ops/sec: {}", pretty_int(i));
94}
95
96fn load_ops_per_second_single_threaded_remove() {
97 let counter: HyperCounter<i32, AtomicI32> = HyperCounter::new();
98
99 for i in 0..2_000_000 {
100 counter.fetch_add(i, 1, Ordering::Relaxed);
101 }
102
103 let now = Instant::now();
104 let mut i = 0;
105
106 loop {
107 counter.fetch_sub(i, 1, Ordering::Relaxed);
108 i += 1;
109
110 if now.elapsed().as_secs() >= 1 {
111 break;
112 }
113 }
114
115 println!("Single-threaded remove ops/sec: {}", pretty_int(i));
116}
117
118fn load_ops_per_second_single_threaded_churn() {
119 let counter: HyperCounter<i32, AtomicI32> = HyperCounter::new();
120
121 let now = Instant::now();
122 let mut i = 0;
123
124 loop {
125 if i % 2 == 0 {
126 counter.fetch_add(i, 1, Ordering::Relaxed);
127 } else {
128 counter.fetch_sub(i, 1, Ordering::Relaxed);
129 }
130
131 i += 1;
132
133 if now.elapsed().as_secs() >= 1 {
134 break;
135 }
136 }
137
138 println!("Single-threaded churn op/s: {}", pretty_int(i));
139}
140
141fn load_ops_per_second_multi_threaded_fetch_add_single_key() {
142 let counter: Arc<HyperCounter<i32, AtomicI32>> = Arc::new(HyperCounter::new());
143
144 let mut handles = vec![];
145
146 for _ in 0..THREADS {
147 let counter = counter.clone();
148
149 let handle = std::thread::spawn(move || {
150 let now = Instant::now();
151
152 loop {
153 counter.fetch_add(1, 1, Ordering::Relaxed);
154
155 if now.elapsed().as_secs() >= 1 {
156 break;
157 }
158 }
159 });
160
161 handles.push(handle);
162 }
163
164 for handle in handles {
165 handle.join().unwrap();
166 }
167
168 let i = counter.load(&1, Ordering::Relaxed);
169
170 println!("Multi-threaded single-key load ops/sec: {}", pretty_int(i));
171}
172
173fn load_ops_per_second_multi_threaded_fetch_add_multi_key() {
174 let counter: Arc<HyperCounter<i32, AtomicI32>> = Arc::new(HyperCounter::new());
175
176 let mut handles = vec![];
177
178 for thread_id in 0..THREADS {
179 let counter = counter.clone();
180
181 let handle = std::thread::spawn(move || {
182 let now = Instant::now();
183
184 for i in 0.. {
185 let key = (i + thread_id) % 1000;
186 counter.fetch_add(key, 1, Ordering::Relaxed);
187
188 if now.elapsed().as_secs() >= 1 {
189 break;
190 }
191 }
192 });
193
194 handles.push(handle);
195 }
196
197 for handle in handles {
198 handle.join().unwrap();
199 }
200
201 let mut i = 0;
202
203 for key in 0..1000 {
204 i += counter.load(&key, Ordering::Relaxed);
205 }
206
207 println!("Multi-threaded multi-key load ops/sec: {}", pretty_int(i));
208}
209
210fn load_ops_per_second_multi_threaded_insert() {
211 let counter: Arc<HyperCounter<i32, AtomicI32>> = Arc::new(HyperCounter::new());
212
213 let mut handles = vec![];
214
215 for thread_id in 0..THREADS {
216 let counter = counter.clone();
217
218 let handle = std::thread::spawn(move || {
219 let now = Instant::now();
220
221 for i in 0.. {
222 let key = i + thread_id * 1_000_000;
223 counter.fetch_add(key, 1, Ordering::Relaxed);
224
225 if now.elapsed().as_secs() >= 1 {
226 break;
227 }
228 }
229 });
230
231 handles.push(handle);
232 }
233
234 for handle in handles {
235 handle.join().unwrap();
236 }
237
238 let i = counter.len();
239
240 println!("Multi-threaded insert ops/sec: {}", pretty_int(i as i32));
241}
242
243fn load_ops_per_second_multi_threaded_remove() {
244 let counter: Arc<HyperCounter<i32, AtomicI32>> = Arc::new(HyperCounter::new());
245
246 for i in 0..8_000_000 {
247 counter.fetch_add(i, 1, Ordering::Relaxed);
248 }
249
250 let mut handles = vec![];
251
252 for thread_id in 0..THREADS {
253 let counter = counter.clone();
254
255 let handle = std::thread::spawn(move || {
256 let now = Instant::now();
257
258 for i in 0.. {
259 let key = i + thread_id * 1_000_000;
260 counter.fetch_sub(key, 1, Ordering::Relaxed);
261
262 if now.elapsed().as_secs() >= 1 {
263 break;
264 }
265 }
266 });
267
268 handles.push(handle);
269 }
270
271 for handle in handles {
272 handle.join().unwrap();
273 }
274
275 let i = counter.len();
276
277 println!(
278 "Multi-threaded remove ops/sec: {}",
279 pretty_int(8_000_000 - i as i32)
280 );
281}
282
283fn load_ops_per_second_multi_threaded_churn() {
284 let counter: Arc<HyperCounter<i32, AtomicI32>> = Arc::new(HyperCounter::new());
285
286 let mut handles = vec![];
287
288 for _ in 0..THREADS {
289 let counter = counter.clone();
290
291 let handle = std::thread::spawn(move || {
292 let now = Instant::now();
293 let mut i = 0;
294
295 loop {
296 if i % 2 == 0 {
297 counter.fetch_add(1, 1, Ordering::Relaxed);
298 } else {
299 counter.fetch_sub(1, 1, Ordering::Relaxed);
300 }
301
302 i += 1;
303
304 if now.elapsed().as_secs() >= 1 {
305 break;
306 }
307 }
308
309 i
310 });
311
312 handles.push(handle);
313 }
314
315 let mut i = 0;
316
317 for handle in handles {
318 i += handle.join().unwrap();
319 }
320
321 println!("Multi-threaded churn op/s: {}", pretty_int(i));
322}