Skip to content

Commit 983fec1

Browse files
committed
[libc++] atomic wait more benchmark
1 parent 7c1c07c commit 983fec1

File tree

1 file changed

+273
-19
lines changed

1 file changed

+273
-19
lines changed

libcxx/benchmarks/atomic_wait.bench.cpp

Lines changed: 273 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,86 @@
77

88
#include <atomic>
99
#include <numeric>
10+
#include <pthread.h>
11+
#include <sched.h>
1012
#include <thread>
1113

1214
#include "benchmark/benchmark.h"
1315
#include "make_test_thread.h"
1416

1517
using namespace std::chrono_literals;
1618

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

2691
std::uint64_t total_loop_test_param = state.range(0);
2792

@@ -34,19 +99,34 @@ void BM_atomic_wait_one_thread_one_atomic_wait(benchmark::State& state) {
3499
}
35100
}
36101
}
37-
BENCHMARK(BM_atomic_wait_one_thread_one_atomic_wait)->RangeMultiplier(2)->Range(1 << 10, 1 << 24);
38102

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

48128
std::uint64_t total_loop_test_param = state.range(0);
49-
constexpr auto num_waiting_threads = 15;
129+
constexpr auto num_waiting_threads = NumWaitingThreads::value;
50130
std::vector<std::jthread> wait_threads;
51131
wait_threads.reserve(num_waiting_threads);
52132

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

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

98275
auto notify_func = [&](std::stop_token st, size_t idx) {
99276
while (!st.stop_requested()) {
100-
atomics[idx].fetch_add(1, std::memory_order_relaxed);
101-
atomics[idx].notify_all();
277+
NotifyPolicy::notify(atomics[idx], st);
102278
}
103279
};
104280

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

154408
BENCHMARK_MAIN();

0 commit comments

Comments
 (0)