Skip to content

Commit 30851f5

Browse files
huixie90Hui Xie
authored andcommitted
[libc++] atomic wait more benchmark
1 parent c95daac commit 30851f5

File tree

1 file changed

+273
-19
lines changed

1 file changed

+273
-19
lines changed

libcxx/test/benchmarks/atomic_wait.bench.cpp

Lines changed: 273 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,86 @@
1111
#include <atomic>
1212
#include <numeric>
1313
#include <stop_token>
14+
#include <pthread.h>
15+
#include <sched.h>
1416
#include <thread>
1517

1618
#include "benchmark/benchmark.h"
1719
#include "make_test_thread.h"
1820

1921
using namespace std::chrono_literals;
2022

21-
void BM_atomic_wait_one_thread_one_atomic_wait(benchmark::State& state) {
22-
std::atomic<std::uint64_t> a;
23-
auto thread_func = [&](std::stop_token st) {
23+
struct HighPrioTask {
24+
sched_param param;
25+
pthread_attr_t attr_t;
26+
pthread_t thread;
27+
std::atomic_bool stopped{false};
28+
29+
HighPrioTask(const HighPrioTask&) = delete;
30+
31+
HighPrioTask() {
32+
pthread_attr_init(&attr_t);
33+
pthread_attr_setschedpolicy(&attr_t, SCHED_FIFO);
34+
param.sched_priority = sched_get_priority_max(SCHED_FIFO);
35+
pthread_attr_setschedparam(&attr_t, &param);
36+
pthread_attr_setinheritsched(&attr_t, PTHREAD_EXPLICIT_SCHED);
37+
38+
auto thread_fun = [](void* arg) -> void* {
39+
auto* stop = reinterpret_cast<std::atomic_bool*>(arg);
40+
while (!stop->load(std::memory_order_relaxed)) {
41+
// spin
42+
}
43+
return nullptr;
44+
};
45+
46+
if (pthread_create(&thread, &attr_t, thread_fun, &stopped) != 0) {
47+
throw std::runtime_error("failed to create thread");
48+
}
49+
}
50+
51+
~HighPrioTask() {
52+
stopped = true;
53+
pthread_attr_destroy(&attr_t);
54+
pthread_join(thread, nullptr);
55+
}
56+
};
57+
58+
59+
template <std::size_t N>
60+
struct NumHighPrioTasks {
61+
static constexpr auto value = N;
62+
};
63+
64+
65+
struct KeepNotifying {
66+
template <class Atomic>
67+
static void notify(Atomic& a, std::stop_token st) {
2468
while (!st.stop_requested()) {
2569
a.fetch_add(1, std::memory_order_relaxed);
2670
a.notify_all();
2771
}
28-
};
72+
}
73+
};
74+
75+
template <std::size_t N>
76+
struct NotifyEveryNus {
77+
template <class Atomic>
78+
static void notify(Atomic& a, std::stop_token st) {
79+
while (!st.stop_requested()) {
80+
auto start = std::chrono::system_clock::now();
81+
a.fetch_add(1, std::memory_order_relaxed);
82+
a.notify_all();
83+
while (std::chrono::system_clock::now() - start < std::chrono::microseconds{N}) {
84+
}
85+
}
86+
}
87+
};
88+
89+
template <class NotifyPolicy, class NumPrioTasks>
90+
void BM_1_atomic_1_waiter_1_notifier(benchmark::State& state) {
91+
[[maybe_unused]] std::array<HighPrioTask, NumPrioTasks::value> tasks{};
92+
std::atomic<std::uint64_t> a;
93+
auto thread_func = [&](std::stop_token st) { NotifyPolicy::notify(a, st); };
2994

3095
std::uint64_t total_loop_test_param = state.range(0);
3196

@@ -38,19 +103,34 @@ void BM_atomic_wait_one_thread_one_atomic_wait(benchmark::State& state) {
38103
}
39104
}
40105
}
41-
BENCHMARK(BM_atomic_wait_one_thread_one_atomic_wait)->RangeMultiplier(2)->Range(1 << 10, 1 << 24);
42106

43-
void BM_atomic_wait_multi_thread_one_atomic_wait(benchmark::State& state) {
107+
BENCHMARK(BM_1_atomic_1_waiter_1_notifier<KeepNotifying, NumHighPrioTasks<0>>)->RangeMultiplier(2)->Range(1 << 10, 1 << 24);
108+
BENCHMARK(BM_1_atomic_1_waiter_1_notifier<NotifyEveryNus<50>, NumHighPrioTasks<0>>)->RangeMultiplier(2)->Range(1 << 10, 1 << 16);
109+
BENCHMARK(BM_1_atomic_1_waiter_1_notifier<NotifyEveryNus<100>, NumHighPrioTasks<0>>)->RangeMultiplier(2)->Range(1 << 10, 1 << 16);
110+
111+
BENCHMARK(BM_1_atomic_1_waiter_1_notifier<KeepNotifying, NumHighPrioTasks<4>>)->RangeMultiplier(2)->Range(1 << 10, 1 << 24);
112+
BENCHMARK(BM_1_atomic_1_waiter_1_notifier<NotifyEveryNus<50>, NumHighPrioTasks<4>>)->RangeMultiplier(2)->Range(1 << 10, 1 << 16);
113+
BENCHMARK(BM_1_atomic_1_waiter_1_notifier<NotifyEveryNus<100>, NumHighPrioTasks<4>>)->RangeMultiplier(2)->Range(1 << 10, 1 << 16);
114+
115+
BENCHMARK(BM_1_atomic_1_waiter_1_notifier<KeepNotifying, NumHighPrioTasks<7>>)->RangeMultiplier(2)->Range(1 << 4, 1 << 8);
116+
BENCHMARK(BM_1_atomic_1_waiter_1_notifier<NotifyEveryNus<50>, NumHighPrioTasks<7>>)->RangeMultiplier(2)->Range(1 << 4, 1 << 8);
117+
BENCHMARK(BM_1_atomic_1_waiter_1_notifier<NotifyEveryNus<100>, NumHighPrioTasks<7>>)->RangeMultiplier(2)->Range(1 << 4, 1 << 8);
118+
119+
120+
template <std::size_t N>
121+
struct NumWaitingThreads {
122+
static constexpr auto value = N;
123+
};
124+
125+
template <class NotifyPolicy, class NumWaitingThreads, class NumPrioTasks>
126+
void BM_1_atomic_multi_waiter_1_notifier(benchmark::State& state) {
127+
[[maybe_unused]] std::array<HighPrioTask, NumPrioTasks::value> tasks{};
128+
44129
std::atomic<std::uint64_t> a;
45-
auto notify_func = [&](std::stop_token st) {
46-
while (!st.stop_requested()) {
47-
a.fetch_add(1, std::memory_order_relaxed);
48-
a.notify_all();
49-
}
50-
};
130+
auto notify_func = [&](std::stop_token st) { NotifyPolicy::notify(a, st); };
51131

52132
std::uint64_t total_loop_test_param = state.range(0);
53-
constexpr auto num_waiting_threads = 15;
133+
constexpr auto num_waiting_threads = NumWaitingThreads::value;
54134
std::vector<std::jthread> wait_threads;
55135
wait_threads.reserve(num_waiting_threads);
56136

@@ -92,17 +172,113 @@ void BM_atomic_wait_multi_thread_one_atomic_wait(benchmark::State& state) {
92172
t.join();
93173
}
94174
}
95-
BENCHMARK(BM_atomic_wait_multi_thread_one_atomic_wait)->RangeMultiplier(2)->Range(1 << 10, 1 << 20);
175+
BENCHMARK(BM_1_atomic_multi_waiter_1_notifier<KeepNotifying, NumWaitingThreads<3>, NumHighPrioTasks<0>>)
176+
->RangeMultiplier(2)
177+
->Range(1 << 10, 1 << 20);
178+
BENCHMARK(BM_1_atomic_multi_waiter_1_notifier<KeepNotifying, NumWaitingThreads<7>, NumHighPrioTasks<0>>)
179+
->RangeMultiplier(2)
180+
->Range(1 << 10, 1 << 20);
181+
BENCHMARK(BM_1_atomic_multi_waiter_1_notifier<KeepNotifying, NumWaitingThreads<15>, NumHighPrioTasks<0>>)
182+
->RangeMultiplier(2)
183+
->Range(1 << 10, 1 << 20);
184+
185+
BENCHMARK(BM_1_atomic_multi_waiter_1_notifier<NotifyEveryNus<50>, NumWaitingThreads<3>, NumHighPrioTasks<0>>)
186+
->RangeMultiplier(2)
187+
->Range(1 << 10, 1 << 16);
188+
BENCHMARK(BM_1_atomic_multi_waiter_1_notifier<NotifyEveryNus<50>, NumWaitingThreads<7>, NumHighPrioTasks<0>>)
189+
->RangeMultiplier(2)
190+
->Range(1 << 10, 1 << 16);
191+
BENCHMARK(BM_1_atomic_multi_waiter_1_notifier<NotifyEveryNus<50>, NumWaitingThreads<15>, NumHighPrioTasks<0>>)
192+
->RangeMultiplier(2)
193+
->Range(1 << 10, 1 << 16);
194+
195+
BENCHMARK(BM_1_atomic_multi_waiter_1_notifier<NotifyEveryNus<100>, NumWaitingThreads<3>, NumHighPrioTasks<0>>)
196+
->RangeMultiplier(2)
197+
->Range(1 << 8, 1 << 14);
198+
BENCHMARK(BM_1_atomic_multi_waiter_1_notifier<NotifyEveryNus<100>, NumWaitingThreads<7>, NumHighPrioTasks<0>>)
199+
->RangeMultiplier(2)
200+
->Range(1 << 8, 1 << 14);
201+
BENCHMARK(BM_1_atomic_multi_waiter_1_notifier<NotifyEveryNus<100>, NumWaitingThreads<15>, NumHighPrioTasks<0>>)
202+
->RangeMultiplier(2)
203+
->Range(1 << 8, 1 << 14);
204+
205+
206+
BENCHMARK(BM_1_atomic_multi_waiter_1_notifier<KeepNotifying, NumWaitingThreads<3>, NumHighPrioTasks<4>>)
207+
->RangeMultiplier(2)
208+
->Range(1 << 10, 1 << 18);
209+
BENCHMARK(BM_1_atomic_multi_waiter_1_notifier<KeepNotifying, NumWaitingThreads<7>, NumHighPrioTasks<4>>)
210+
->RangeMultiplier(2)
211+
->Range(1 << 10, 1 << 18);
212+
BENCHMARK(BM_1_atomic_multi_waiter_1_notifier<KeepNotifying, NumWaitingThreads<15>, NumHighPrioTasks<4>>)
213+
->RangeMultiplier(2)
214+
->Range(1 << 10, 1 << 18);
215+
216+
BENCHMARK(BM_1_atomic_multi_waiter_1_notifier<NotifyEveryNus<50>, NumWaitingThreads<3>, NumHighPrioTasks<4>>)
217+
->RangeMultiplier(2)
218+
->Range(1 << 10, 1 << 14);
219+
BENCHMARK(BM_1_atomic_multi_waiter_1_notifier<NotifyEveryNus<50>, NumWaitingThreads<7>, NumHighPrioTasks<4>>)
220+
->RangeMultiplier(2)
221+
->Range(1 << 10, 1 << 14);
222+
BENCHMARK(BM_1_atomic_multi_waiter_1_notifier<NotifyEveryNus<50>, NumWaitingThreads<15>, NumHighPrioTasks<4>>)
223+
->RangeMultiplier(2)
224+
->Range(1 << 10, 1 << 14);
96225

97-
void BM_atomic_wait_multi_thread_wait_different_atomics(benchmark::State& state) {
226+
BENCHMARK(BM_1_atomic_multi_waiter_1_notifier<NotifyEveryNus<100>, NumWaitingThreads<3>, NumHighPrioTasks<4>>)
227+
->RangeMultiplier(2)
228+
->Range(1 << 8, 1 << 14);
229+
BENCHMARK(BM_1_atomic_multi_waiter_1_notifier<NotifyEveryNus<100>, NumWaitingThreads<7>, NumHighPrioTasks<4>>)
230+
->RangeMultiplier(2)
231+
->Range(1 << 8, 1 << 14);
232+
BENCHMARK(BM_1_atomic_multi_waiter_1_notifier<NotifyEveryNus<100>, NumWaitingThreads<15>, NumHighPrioTasks<4>>)
233+
->RangeMultiplier(2)
234+
->Range(1 << 8, 1 << 14);
235+
236+
BENCHMARK(BM_1_atomic_multi_waiter_1_notifier<KeepNotifying, NumWaitingThreads<3>, NumHighPrioTasks<7>>)
237+
->RangeMultiplier(2)
238+
->Range(1 << 4, 1 << 8);
239+
BENCHMARK(BM_1_atomic_multi_waiter_1_notifier<KeepNotifying, NumWaitingThreads<7>, NumHighPrioTasks<7>>)
240+
->RangeMultiplier(2)
241+
->Range(1 << 4, 1 << 8);
242+
BENCHMARK(BM_1_atomic_multi_waiter_1_notifier<KeepNotifying, NumWaitingThreads<15>, NumHighPrioTasks<7>>)
243+
->RangeMultiplier(2)
244+
->Range(1 << 4, 1 << 8);
245+
246+
BENCHMARK(BM_1_atomic_multi_waiter_1_notifier<NotifyEveryNus<50>, NumWaitingThreads<3>, NumHighPrioTasks<7>>)
247+
->RangeMultiplier(2)
248+
->Range(1 << 4, 1 << 8);
249+
BENCHMARK(BM_1_atomic_multi_waiter_1_notifier<NotifyEveryNus<50>, NumWaitingThreads<7>, NumHighPrioTasks<7>>)
250+
->RangeMultiplier(2)
251+
->Range(1 << 4, 1 << 8);
252+
BENCHMARK(BM_1_atomic_multi_waiter_1_notifier<NotifyEveryNus<50>, NumWaitingThreads<15>, NumHighPrioTasks<7>>)
253+
->RangeMultiplier(2)
254+
->Range(1 << 4, 1 << 8);
255+
256+
BENCHMARK(BM_1_atomic_multi_waiter_1_notifier<NotifyEveryNus<100>, NumWaitingThreads<3>, NumHighPrioTasks<7>>)
257+
->RangeMultiplier(2)
258+
->Range(1 << 4, 1 << 8);
259+
BENCHMARK(BM_1_atomic_multi_waiter_1_notifier<NotifyEveryNus<100>, NumWaitingThreads<7>, NumHighPrioTasks<7>>)
260+
->RangeMultiplier(2)
261+
->Range(1 << 4, 1 << 8);
262+
BENCHMARK(BM_1_atomic_multi_waiter_1_notifier<NotifyEveryNus<100>, NumWaitingThreads<15>, NumHighPrioTasks<7>>)
263+
->RangeMultiplier(2)
264+
->Range(1 << 4, 1 << 8);
265+
266+
267+
template <std::size_t N>
268+
struct NumberOfAtomics {
269+
static constexpr auto value = N;
270+
};
271+
272+
template <class NotifyPolicy, class NumberOfAtomics, class NumPrioTasks>
273+
void BM_N_atomics_N_waiter_N_notifier(benchmark::State& state) {
274+
[[maybe_unused]] std::array<HighPrioTask, NumPrioTasks::value> tasks{};
98275
const std::uint64_t total_loop_test_param = state.range(0);
99-
constexpr std::uint64_t num_atomics = 7;
276+
constexpr std::uint64_t num_atomics = NumberOfAtomics::value;
100277
std::vector<std::atomic<std::uint64_t>> atomics(num_atomics);
101278

102279
auto notify_func = [&](std::stop_token st, size_t idx) {
103280
while (!st.stop_requested()) {
104-
atomics[idx].fetch_add(1, std::memory_order_relaxed);
105-
atomics[idx].notify_all();
281+
NotifyPolicy::notify(atomics[idx], st);
106282
}
107283
};
108284

@@ -153,6 +329,84 @@ void BM_atomic_wait_multi_thread_wait_different_atomics(benchmark::State& state)
153329
t.join();
154330
}
155331
}
156-
BENCHMARK(BM_atomic_wait_multi_thread_wait_different_atomics)->RangeMultiplier(2)->Range(1 << 10, 1 << 20);
332+
333+
BENCHMARK(BM_N_atomics_N_waiter_N_notifier<KeepNotifying, NumberOfAtomics<2>, NumHighPrioTasks<0>>)
334+
->RangeMultiplier(2)
335+
->Range(1 << 10, 1 << 20);
336+
BENCHMARK(BM_N_atomics_N_waiter_N_notifier<KeepNotifying, NumberOfAtomics<3>, NumHighPrioTasks<0>>)
337+
->RangeMultiplier(2)
338+
->Range(1 << 10, 1 << 20);
339+
BENCHMARK(BM_N_atomics_N_waiter_N_notifier<KeepNotifying, NumberOfAtomics<5>, NumHighPrioTasks<0>>)
340+
->RangeMultiplier(2)
341+
->Range(1 << 10, 1 << 20);
342+
BENCHMARK(BM_N_atomics_N_waiter_N_notifier<KeepNotifying, NumberOfAtomics<7>, NumHighPrioTasks<0>>)
343+
->RangeMultiplier(2)
344+
->Range(1 << 10, 1 << 20);
345+
346+
BENCHMARK(BM_N_atomics_N_waiter_N_notifier<NotifyEveryNus<50>, NumberOfAtomics<2>, NumHighPrioTasks<0>>)
347+
->RangeMultiplier(2)
348+
->Range(1 << 10, 1 << 16);
349+
BENCHMARK(BM_N_atomics_N_waiter_N_notifier<NotifyEveryNus<50>, NumberOfAtomics<3>, NumHighPrioTasks<0>>)
350+
->RangeMultiplier(2)
351+
->Range(1 << 10, 1 << 16);
352+
BENCHMARK(BM_N_atomics_N_waiter_N_notifier<NotifyEveryNus<50>, NumberOfAtomics<5>, NumHighPrioTasks<0>>)
353+
->RangeMultiplier(2)
354+
->Range(1 << 10, 1 << 16);
355+
BENCHMARK(BM_N_atomics_N_waiter_N_notifier<NotifyEveryNus<50>, NumberOfAtomics<7>, NumHighPrioTasks<0>>)
356+
->RangeMultiplier(2)
357+
->Range(1 << 10, 1 << 16);
358+
359+
BENCHMARK(BM_N_atomics_N_waiter_N_notifier<NotifyEveryNus<100>, NumberOfAtomics<2>, NumHighPrioTasks<0>>)
360+
->RangeMultiplier(2)
361+
->Range(1 << 8, 1 << 14);
362+
BENCHMARK(BM_N_atomics_N_waiter_N_notifier<NotifyEveryNus<100>, NumberOfAtomics<3>, NumHighPrioTasks<0>>)
363+
->RangeMultiplier(2)
364+
->Range(1 << 8, 1 << 14);
365+
BENCHMARK(BM_N_atomics_N_waiter_N_notifier<NotifyEveryNus<100>, NumberOfAtomics<5>, NumHighPrioTasks<0>>)
366+
->RangeMultiplier(2)
367+
->Range(1 << 8, 1 << 14);
368+
BENCHMARK(BM_N_atomics_N_waiter_N_notifier<NotifyEveryNus<100>, NumberOfAtomics<7>, NumHighPrioTasks<0>>)
369+
->RangeMultiplier(2)
370+
->Range(1 << 8, 1 << 14);
371+
372+
BENCHMARK(BM_N_atomics_N_waiter_N_notifier<KeepNotifying, NumberOfAtomics<2>, NumHighPrioTasks<4>>)
373+
->RangeMultiplier(2)
374+
->Range(1 << 10, 1 << 20);
375+
BENCHMARK(BM_N_atomics_N_waiter_N_notifier<KeepNotifying, NumberOfAtomics<3>, NumHighPrioTasks<4>>)
376+
->RangeMultiplier(2)
377+
->Range(1 << 10, 1 << 20);
378+
BENCHMARK(BM_N_atomics_N_waiter_N_notifier<KeepNotifying, NumberOfAtomics<5>, NumHighPrioTasks<4>>)
379+
->RangeMultiplier(2)
380+
->Range(1 << 10, 1 << 20);
381+
BENCHMARK(BM_N_atomics_N_waiter_N_notifier<KeepNotifying, NumberOfAtomics<7>, NumHighPrioTasks<4>>)
382+
->RangeMultiplier(2)
383+
->Range(1 << 10, 1 << 20);
384+
385+
BENCHMARK(BM_N_atomics_N_waiter_N_notifier<NotifyEveryNus<50>, NumberOfAtomics<2>, NumHighPrioTasks<4>>)
386+
->RangeMultiplier(2)
387+
->Range(1 << 10, 1 << 16);
388+
BENCHMARK(BM_N_atomics_N_waiter_N_notifier<NotifyEveryNus<50>, NumberOfAtomics<3>, NumHighPrioTasks<4>>)
389+
->RangeMultiplier(2)
390+
->Range(1 << 10, 1 << 16);
391+
BENCHMARK(BM_N_atomics_N_waiter_N_notifier<NotifyEveryNus<50>, NumberOfAtomics<5>, NumHighPrioTasks<4>>)
392+
->RangeMultiplier(2)
393+
->Range(1 << 10, 1 << 16);
394+
BENCHMARK(BM_N_atomics_N_waiter_N_notifier<NotifyEveryNus<50>, NumberOfAtomics<7>, NumHighPrioTasks<4>>)
395+
->RangeMultiplier(2)
396+
->Range(1 << 10, 1 << 16);
397+
398+
399+
BENCHMARK(BM_N_atomics_N_waiter_N_notifier<NotifyEveryNus<100>, NumberOfAtomics<2>, NumHighPrioTasks<4>>)
400+
->RangeMultiplier(2)
401+
->Range(1 << 8, 1 << 14);
402+
BENCHMARK(BM_N_atomics_N_waiter_N_notifier<NotifyEveryNus<100>, NumberOfAtomics<3>, NumHighPrioTasks<4>>)
403+
->RangeMultiplier(2)
404+
->Range(1 << 8, 1 << 14);
405+
BENCHMARK(BM_N_atomics_N_waiter_N_notifier<NotifyEveryNus<100>, NumberOfAtomics<5>, NumHighPrioTasks<4>>)
406+
->RangeMultiplier(2)
407+
->Range(1 << 6, 1 << 10);
408+
BENCHMARK(BM_N_atomics_N_waiter_N_notifier<NotifyEveryNus<100>, NumberOfAtomics<7>, NumHighPrioTasks<4>>)
409+
->RangeMultiplier(2)
410+
->Range(1 << 4, 1 << 8);
157411

158412
BENCHMARK_MAIN();

0 commit comments

Comments
 (0)