Skip to content

Commit 6005b70

Browse files
huixie90Hui Xie
authored andcommitted
[libc++] atomic wait more benchmark
1 parent e48916f commit 6005b70

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
@@ -12,21 +12,86 @@
1212
#include <cstdint>
1313
#include <numeric>
1414
#include <stop_token>
15+
#include <pthread.h>
16+
#include <sched.h>
1517
#include <thread>
1618

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

2022
using namespace std::chrono_literals;
2123

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

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

@@ -39,19 +104,34 @@ void BM_atomic_wait_one_thread_one_atomic_wait(benchmark::State& state) {
39104
}
40105
}
41106
}
42-
BENCHMARK(BM_atomic_wait_one_thread_one_atomic_wait)->RangeMultiplier(2)->Range(1 << 10, 1 << 24);
43107

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

53133
std::uint64_t total_loop_test_param = state.range(0);
54-
constexpr auto num_waiting_threads = 15;
134+
constexpr auto num_waiting_threads = NumWaitingThreads::value;
55135
std::vector<std::jthread> wait_threads;
56136
wait_threads.reserve(num_waiting_threads);
57137

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

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

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

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

159413
BENCHMARK_MAIN();

0 commit comments

Comments
 (0)