From 88840ae088e466eb87c3191b57bd46b0af31d64c Mon Sep 17 00:00:00 2001 From: Louis Dionne Date: Fri, 12 Sep 2025 08:59:50 -0400 Subject: [PATCH] [libc++] Remove atomic_wait benchmarks The atomic_wait benchmarks are great, but in fact they are a bit too great. Indeed, they are so thorough that they overload the system they're running on, meaning they can't be run on our CI infrastructure. The NxN benchmarks even crash my system on macOS. This patch removes the benchmarks since we're trying to move towards more continuous benchmarking, which requires making a tradeoff between exhaustiveness and ability to run the benchmarks on a regular basis. Instead, it would be better to rework the benchmarks to make them more lightweight, but I think it is reasonable to do that at a later time in order to unblock continuous performance monitoring ASAP. --- .../atomic_wait_1_waiter_1_notifier.bench.cpp | 74 -------- .../atomic_wait_N_waiter_N_notifier.bench.cpp | 167 ------------------ libcxx/test/benchmarks/atomic_wait_helper.h | 92 ---------- ...mic_wait_multi_waiter_1_notifier.bench.cpp | 167 ------------------ .../atomic_wait_vs_mutex_lock.bench.cpp | 109 ------------ 5 files changed, 609 deletions(-) delete mode 100644 libcxx/test/benchmarks/atomic_wait_1_waiter_1_notifier.bench.cpp delete mode 100644 libcxx/test/benchmarks/atomic_wait_N_waiter_N_notifier.bench.cpp delete mode 100644 libcxx/test/benchmarks/atomic_wait_helper.h delete mode 100644 libcxx/test/benchmarks/atomic_wait_multi_waiter_1_notifier.bench.cpp delete mode 100644 libcxx/test/benchmarks/atomic_wait_vs_mutex_lock.bench.cpp diff --git a/libcxx/test/benchmarks/atomic_wait_1_waiter_1_notifier.bench.cpp b/libcxx/test/benchmarks/atomic_wait_1_waiter_1_notifier.bench.cpp deleted file mode 100644 index c3d7e6511925d..0000000000000 --- a/libcxx/test/benchmarks/atomic_wait_1_waiter_1_notifier.bench.cpp +++ /dev/null @@ -1,74 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -// UNSUPPORTED: c++03, c++11, c++14, c++17 - -#include "atomic_wait_helper.h" - -#include -#include -#include -#include -#include -#include -#include - -#include "benchmark/benchmark.h" -#include "make_test_thread.h" - -using namespace std::chrono_literals; - -template -void BM_1_atomic_1_waiter_1_notifier(benchmark::State& state) { - [[maybe_unused]] std::array tasks{}; - std::atomic a; - auto thread_func = [&](std::stop_token st) { NotifyPolicy::notify(a, st); }; - - std::uint64_t total_loop_test_param = state.range(0); - - auto thread = support::make_test_jthread(thread_func); - - for (auto _ : state) { - for (std::uint64_t i = 0; i < total_loop_test_param; ++i) { - auto old = a.load(std::memory_order_relaxed); - a.wait(old); - } - } -} - -BENCHMARK(BM_1_atomic_1_waiter_1_notifier>) - ->RangeMultiplier(2) - ->Range(1 << 16, 1 << 18); -BENCHMARK(BM_1_atomic_1_waiter_1_notifier, NumHighPrioTasks<0>>) - ->RangeMultiplier(2) - ->Range(1 << 10, 1 << 12); -BENCHMARK(BM_1_atomic_1_waiter_1_notifier, NumHighPrioTasks<0>>) - ->RangeMultiplier(2) - ->Range(1 << 10, 1 << 12); - -BENCHMARK(BM_1_atomic_1_waiter_1_notifier>) - ->RangeMultiplier(2) - ->Range(1 << 16, 1 << 18); -BENCHMARK(BM_1_atomic_1_waiter_1_notifier, NumHighPrioTasks<4>>) - ->RangeMultiplier(2) - ->Range(1 << 10, 1 << 12); -BENCHMARK(BM_1_atomic_1_waiter_1_notifier, NumHighPrioTasks<4>>) - ->RangeMultiplier(2) - ->Range(1 << 10, 1 << 12); - -BENCHMARK(BM_1_atomic_1_waiter_1_notifier>) - ->RangeMultiplier(2) - ->Range(1 << 4, 1 << 6); -BENCHMARK(BM_1_atomic_1_waiter_1_notifier, NumHighPrioTasks<7>>) - ->RangeMultiplier(2) - ->Range(1 << 3, 1 << 5); -BENCHMARK(BM_1_atomic_1_waiter_1_notifier, NumHighPrioTasks<7>>) - ->RangeMultiplier(2) - ->Range(1 << 3, 1 << 5); - -BENCHMARK_MAIN(); diff --git a/libcxx/test/benchmarks/atomic_wait_N_waiter_N_notifier.bench.cpp b/libcxx/test/benchmarks/atomic_wait_N_waiter_N_notifier.bench.cpp deleted file mode 100644 index d9b9aa212f602..0000000000000 --- a/libcxx/test/benchmarks/atomic_wait_N_waiter_N_notifier.bench.cpp +++ /dev/null @@ -1,167 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -// UNSUPPORTED: c++03, c++11, c++14, c++17 - -#include "atomic_wait_helper.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "benchmark/benchmark.h" -#include "make_test_thread.h" - -using namespace std::chrono_literals; - -template -void BM_N_atomics_N_waiter_N_notifier(benchmark::State& state) { - [[maybe_unused]] std::array tasks{}; - const std::uint64_t total_loop_test_param = state.range(0); - constexpr std::uint64_t num_atomics = NumberOfAtomics::value; - std::vector> atomics(num_atomics); - - auto notify_func = [&](std::stop_token st, size_t idx) { - while (!st.stop_requested()) { - NotifyPolicy::notify(atomics[idx], st); - } - }; - - std::atomic start_flag = 0; - std::atomic done_count = 0; - - auto wait_func = [&, total_loop_test_param](std::stop_token st, size_t idx) { - auto old_start = 0; - while (!st.stop_requested()) { - start_flag.wait(old_start); - old_start = start_flag.load(); - for (std::uint64_t i = 0; i < total_loop_test_param; ++i) { - auto old = atomics[idx].load(std::memory_order_relaxed); - atomics[idx].wait(old); - } - done_count.fetch_add(1); - } - }; - - std::vector notify_threads; - notify_threads.reserve(num_atomics); - - std::vector wait_threads; - wait_threads.reserve(num_atomics); - - for (size_t i = 0; i < num_atomics; ++i) { - notify_threads.emplace_back(support::make_test_jthread(notify_func, i)); - } - - for (size_t i = 0; i < num_atomics; ++i) { - wait_threads.emplace_back(support::make_test_jthread(wait_func, i)); - } - - for (auto _ : state) { - done_count = 0; - start_flag.fetch_add(1); - start_flag.notify_all(); - while (done_count < num_atomics) { - std::this_thread::yield(); - } - } - for (auto& t : wait_threads) { - t.request_stop(); - } - start_flag.fetch_add(1); - start_flag.notify_all(); - for (auto& t : wait_threads) { - t.join(); - } -} - -BENCHMARK(BM_N_atomics_N_waiter_N_notifier, NumHighPrioTasks<0>>) - ->RangeMultiplier(2) - ->Range(1 << 12, 1 << 14); -BENCHMARK(BM_N_atomics_N_waiter_N_notifier, NumHighPrioTasks<0>>) - ->RangeMultiplier(2) - ->Range(1 << 10, 1 << 12); -BENCHMARK(BM_N_atomics_N_waiter_N_notifier, NumHighPrioTasks<0>>) - ->RangeMultiplier(2) - ->Range(1 << 10, 1 << 12); -BENCHMARK(BM_N_atomics_N_waiter_N_notifier, NumHighPrioTasks<0>>) - ->RangeMultiplier(2) - ->Range(1 << 8, 1 << 10); - -BENCHMARK(BM_N_atomics_N_waiter_N_notifier, NumberOfAtomics<2>, NumHighPrioTasks<0>>) - ->RangeMultiplier(2) - ->Range(1 << 10, 1 << 12); -BENCHMARK(BM_N_atomics_N_waiter_N_notifier, NumberOfAtomics<3>, NumHighPrioTasks<0>>) - ->RangeMultiplier(2) - ->Range(1 << 8, 1 << 10); -BENCHMARK(BM_N_atomics_N_waiter_N_notifier, NumberOfAtomics<5>, NumHighPrioTasks<0>>) - ->RangeMultiplier(2) - ->Range(1 << 8, 1 << 10); -BENCHMARK(BM_N_atomics_N_waiter_N_notifier, NumberOfAtomics<7>, NumHighPrioTasks<0>>) - ->RangeMultiplier(2) - ->Range(1 << 6, 1 << 8); - -BENCHMARK(BM_N_atomics_N_waiter_N_notifier, NumberOfAtomics<2>, NumHighPrioTasks<0>>) - ->RangeMultiplier(2) - ->Range(1 << 8, 1 << 10); -BENCHMARK(BM_N_atomics_N_waiter_N_notifier, NumberOfAtomics<3>, NumHighPrioTasks<0>>) - ->RangeMultiplier(2) - ->Range(1 << 8, 1 << 10); -BENCHMARK(BM_N_atomics_N_waiter_N_notifier, NumberOfAtomics<5>, NumHighPrioTasks<0>>) - ->RangeMultiplier(2) - ->Range(1 << 7, 1 << 9); -BENCHMARK(BM_N_atomics_N_waiter_N_notifier, NumberOfAtomics<7>, NumHighPrioTasks<0>>) - ->RangeMultiplier(2) - ->Range(1 << 6, 1 << 8); - -BENCHMARK(BM_N_atomics_N_waiter_N_notifier, NumHighPrioTasks<4>>) - ->RangeMultiplier(2) - ->Range(1 << 7, 1 << 9); -BENCHMARK(BM_N_atomics_N_waiter_N_notifier, NumHighPrioTasks<4>>) - ->RangeMultiplier(2) - ->Range(1 << 7, 1 << 9); -BENCHMARK(BM_N_atomics_N_waiter_N_notifier, NumHighPrioTasks<4>>) - ->RangeMultiplier(2) - ->Range(1 << 6, 1 << 8); -BENCHMARK(BM_N_atomics_N_waiter_N_notifier, NumHighPrioTasks<4>>) - ->RangeMultiplier(2) - ->Range(1 << 4, 1 << 6); - -BENCHMARK(BM_N_atomics_N_waiter_N_notifier, NumberOfAtomics<2>, NumHighPrioTasks<4>>) - ->RangeMultiplier(2) - ->Range(1 << 7, 1 << 9); -BENCHMARK(BM_N_atomics_N_waiter_N_notifier, NumberOfAtomics<3>, NumHighPrioTasks<4>>) - ->RangeMultiplier(2) - ->Range(1 << 7, 1 << 9); -BENCHMARK(BM_N_atomics_N_waiter_N_notifier, NumberOfAtomics<5>, NumHighPrioTasks<4>>) - ->RangeMultiplier(2) - ->Range(1 << 5, 1 << 7); -BENCHMARK(BM_N_atomics_N_waiter_N_notifier, NumberOfAtomics<7>, NumHighPrioTasks<4>>) - ->RangeMultiplier(2) - ->Range(1 << 3, 1 << 5); - -BENCHMARK(BM_N_atomics_N_waiter_N_notifier, NumberOfAtomics<2>, NumHighPrioTasks<4>>) - ->RangeMultiplier(2) - ->Range(1 << 6, 1 << 8); -BENCHMARK(BM_N_atomics_N_waiter_N_notifier, NumberOfAtomics<3>, NumHighPrioTasks<4>>) - ->RangeMultiplier(2) - ->Range(1 << 6, 1 << 8); -BENCHMARK(BM_N_atomics_N_waiter_N_notifier, NumberOfAtomics<5>, NumHighPrioTasks<4>>) - ->RangeMultiplier(2) - ->Range(1 << 5, 1 << 7); -BENCHMARK(BM_N_atomics_N_waiter_N_notifier, NumberOfAtomics<7>, NumHighPrioTasks<4>>) - ->RangeMultiplier(2) - ->Range(1 << 3, 1 << 5); - -BENCHMARK_MAIN(); diff --git a/libcxx/test/benchmarks/atomic_wait_helper.h b/libcxx/test/benchmarks/atomic_wait_helper.h deleted file mode 100644 index cfdacf9e01688..0000000000000 --- a/libcxx/test/benchmarks/atomic_wait_helper.h +++ /dev/null @@ -1,92 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#ifndef TEST_BENCHMARK_ATOMIC_WAIT_HELPER_H -#define TEST_BENCHMARK_ATOMIC_WAIT_HELPER_H - -#include -#include -#include -#include -#include - -struct HighPrioTask { - sched_param param; - pthread_attr_t attr_t; - pthread_t thread; - std::atomic_bool stopped{false}; - - HighPrioTask(const HighPrioTask&) = delete; - - HighPrioTask() { - pthread_attr_init(&attr_t); - pthread_attr_setschedpolicy(&attr_t, SCHED_FIFO); - param.sched_priority = sched_get_priority_max(SCHED_FIFO); - pthread_attr_setschedparam(&attr_t, ¶m); - pthread_attr_setinheritsched(&attr_t, PTHREAD_EXPLICIT_SCHED); - - auto thread_fun = [](void* arg) -> void* { - auto* stop = reinterpret_cast(arg); - while (!stop->load(std::memory_order_relaxed)) { - // spin - } - return nullptr; - }; - - if (pthread_create(&thread, &attr_t, thread_fun, &stopped) != 0) { - throw std::runtime_error("failed to create thread"); - } - } - - ~HighPrioTask() { - stopped = true; - pthread_attr_destroy(&attr_t); - pthread_join(thread, nullptr); - } -}; - -template -struct NumHighPrioTasks { - static constexpr auto value = N; -}; - -template -struct NumWaitingThreads { - static constexpr auto value = N; -}; - -template -struct NumberOfAtomics { - static constexpr auto value = N; -}; - -struct KeepNotifying { - template - static void notify(Atomic& a, std::stop_token st) { - while (!st.stop_requested()) { - a.fetch_add(1, std::memory_order_relaxed); - a.notify_all(); - } - } -}; - -template -struct NotifyEveryNus { - template - static void notify(Atomic& a, std::stop_token st) { - while (!st.stop_requested()) { - auto start = std::chrono::system_clock::now(); - a.fetch_add(1, std::memory_order_relaxed); - a.notify_all(); - while (std::chrono::system_clock::now() - start < std::chrono::microseconds{N}) { - } - } - } -}; - -#endif // TEST_BENCHMARK_ATOMIC_WAIT_HELPER_H \ No newline at end of file diff --git a/libcxx/test/benchmarks/atomic_wait_multi_waiter_1_notifier.bench.cpp b/libcxx/test/benchmarks/atomic_wait_multi_waiter_1_notifier.bench.cpp deleted file mode 100644 index a14a6a2ad9c98..0000000000000 --- a/libcxx/test/benchmarks/atomic_wait_multi_waiter_1_notifier.bench.cpp +++ /dev/null @@ -1,167 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -// UNSUPPORTED: c++03, c++11, c++14, c++17 - -#include "atomic_wait_helper.h" - -#include -#include -#include -#include -#include -#include -#include - -#include "benchmark/benchmark.h" -#include "make_test_thread.h" - -using namespace std::chrono_literals; - -template -void BM_1_atomic_multi_waiter_1_notifier(benchmark::State& state) { - [[maybe_unused]] std::array tasks{}; - - std::atomic a; - auto notify_func = [&](std::stop_token st) { NotifyPolicy::notify(a, st); }; - - std::uint64_t total_loop_test_param = state.range(0); - constexpr auto num_waiting_threads = NumWaitingThreads::value; - std::vector wait_threads; - wait_threads.reserve(num_waiting_threads); - - auto notify_thread = support::make_test_jthread(notify_func); - - std::atomic start_flag = 0; - std::atomic done_count = 0; - auto wait_func = [&a, &start_flag, &done_count, total_loop_test_param](std::stop_token st) { - auto old_start = 0; - while (!st.stop_requested()) { - start_flag.wait(old_start); - old_start = start_flag.load(); - for (std::uint64_t i = 0; i < total_loop_test_param; ++i) { - auto old = a.load(std::memory_order_relaxed); - a.wait(old); - } - done_count.fetch_add(1); - } - }; - - for (size_t i = 0; i < num_waiting_threads; ++i) { - wait_threads.emplace_back(support::make_test_jthread(wait_func)); - } - - for (auto _ : state) { - done_count = 0; - start_flag.fetch_add(1); - start_flag.notify_all(); - while (done_count < num_waiting_threads) { - std::this_thread::yield(); - } - } - for (auto& t : wait_threads) { - t.request_stop(); - } - start_flag.fetch_add(1); - start_flag.notify_all(); - for (auto& t : wait_threads) { - t.join(); - } -} - -BENCHMARK(BM_1_atomic_multi_waiter_1_notifier, NumHighPrioTasks<0>>) - ->RangeMultiplier(2) - ->Range(1 << 14, 1 << 16); -BENCHMARK(BM_1_atomic_multi_waiter_1_notifier, NumHighPrioTasks<0>>) - ->RangeMultiplier(2) - ->Range(1 << 12, 1 << 14); -BENCHMARK(BM_1_atomic_multi_waiter_1_notifier, NumHighPrioTasks<0>>) - ->RangeMultiplier(2) - ->Range(1 << 10, 1 << 12); - -BENCHMARK(BM_1_atomic_multi_waiter_1_notifier, NumWaitingThreads<3>, NumHighPrioTasks<0>>) - ->RangeMultiplier(2) - ->Range(1 << 10, 1 << 12); -BENCHMARK(BM_1_atomic_multi_waiter_1_notifier, NumWaitingThreads<7>, NumHighPrioTasks<0>>) - ->RangeMultiplier(2) - ->Range(1 << 8, 1 << 10); -BENCHMARK(BM_1_atomic_multi_waiter_1_notifier, NumWaitingThreads<15>, NumHighPrioTasks<0>>) - ->RangeMultiplier(2) - ->Range(1 << 6, 1 << 8); - -BENCHMARK(BM_1_atomic_multi_waiter_1_notifier, NumWaitingThreads<3>, NumHighPrioTasks<0>>) - ->RangeMultiplier(2) - ->Range(1 << 8, 1 << 10); -BENCHMARK(BM_1_atomic_multi_waiter_1_notifier, NumWaitingThreads<7>, NumHighPrioTasks<0>>) - ->RangeMultiplier(2) - ->Range(1 << 6, 1 << 8); -BENCHMARK(BM_1_atomic_multi_waiter_1_notifier, NumWaitingThreads<15>, NumHighPrioTasks<0>>) - ->RangeMultiplier(2) - ->Range(1 << 4, 1 << 6); - -BENCHMARK(BM_1_atomic_multi_waiter_1_notifier, NumHighPrioTasks<4>>) - ->RangeMultiplier(2) - ->Range(1 << 8, 1 << 10); -BENCHMARK(BM_1_atomic_multi_waiter_1_notifier, NumHighPrioTasks<4>>) - ->RangeMultiplier(2) - ->Range(1 << 6, 1 << 8); -BENCHMARK(BM_1_atomic_multi_waiter_1_notifier, NumHighPrioTasks<4>>) - ->RangeMultiplier(2) - ->Range(1 << 4, 1 << 6); - -BENCHMARK(BM_1_atomic_multi_waiter_1_notifier, NumWaitingThreads<3>, NumHighPrioTasks<4>>) - ->RangeMultiplier(2) - ->Range(1 << 8, 1 << 10); -BENCHMARK(BM_1_atomic_multi_waiter_1_notifier, NumWaitingThreads<7>, NumHighPrioTasks<4>>) - ->RangeMultiplier(2) - ->Range(1 << 6, 1 << 8); -BENCHMARK(BM_1_atomic_multi_waiter_1_notifier, NumWaitingThreads<15>, NumHighPrioTasks<4>>) - ->RangeMultiplier(2) - ->Range(1 << 4, 1 << 6); - -BENCHMARK(BM_1_atomic_multi_waiter_1_notifier, NumWaitingThreads<3>, NumHighPrioTasks<4>>) - ->RangeMultiplier(2) - ->Range(1 << 8, 1 << 10); -BENCHMARK(BM_1_atomic_multi_waiter_1_notifier, NumWaitingThreads<7>, NumHighPrioTasks<4>>) - ->RangeMultiplier(2) - ->Range(1 << 6, 1 << 8); -BENCHMARK(BM_1_atomic_multi_waiter_1_notifier, NumWaitingThreads<15>, NumHighPrioTasks<4>>) - ->RangeMultiplier(2) - ->Range(1 << 4, 1 << 6); - -BENCHMARK(BM_1_atomic_multi_waiter_1_notifier, NumHighPrioTasks<7>>) - ->RangeMultiplier(2) - ->Range(1 << 4, 1 << 6); -BENCHMARK(BM_1_atomic_multi_waiter_1_notifier, NumHighPrioTasks<7>>) - ->RangeMultiplier(2) - ->Range(1 << 3, 1 << 5); -BENCHMARK(BM_1_atomic_multi_waiter_1_notifier, NumHighPrioTasks<7>>) - ->RangeMultiplier(2) - ->Range(1 << 2, 1 << 4); - -BENCHMARK(BM_1_atomic_multi_waiter_1_notifier, NumWaitingThreads<3>, NumHighPrioTasks<7>>) - ->RangeMultiplier(2) - ->Range(1 << 3, 1 << 5); -BENCHMARK(BM_1_atomic_multi_waiter_1_notifier, NumWaitingThreads<7>, NumHighPrioTasks<7>>) - ->RangeMultiplier(2) - ->Range(1 << 2, 1 << 4); -BENCHMARK(BM_1_atomic_multi_waiter_1_notifier, NumWaitingThreads<15>, NumHighPrioTasks<7>>) - ->RangeMultiplier(2) - ->Range(1 << 1, 1 << 3); - -BENCHMARK(BM_1_atomic_multi_waiter_1_notifier, NumWaitingThreads<3>, NumHighPrioTasks<7>>) - ->RangeMultiplier(2) - ->Range(1 << 3, 1 << 5); -BENCHMARK(BM_1_atomic_multi_waiter_1_notifier, NumWaitingThreads<7>, NumHighPrioTasks<7>>) - ->RangeMultiplier(2) - ->Range(1 << 2, 1 << 4); -BENCHMARK(BM_1_atomic_multi_waiter_1_notifier, NumWaitingThreads<15>, NumHighPrioTasks<7>>) - ->RangeMultiplier(2) - ->Range(1 << 1, 1 << 3); - -BENCHMARK_MAIN(); diff --git a/libcxx/test/benchmarks/atomic_wait_vs_mutex_lock.bench.cpp b/libcxx/test/benchmarks/atomic_wait_vs_mutex_lock.bench.cpp deleted file mode 100644 index a554c721df017..0000000000000 --- a/libcxx/test/benchmarks/atomic_wait_vs_mutex_lock.bench.cpp +++ /dev/null @@ -1,109 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -// UNSUPPORTED: c++03, c++11, c++14, c++17 - -#include -#include -#include -#include -#include - -#include "benchmark/benchmark.h" -#include "make_test_thread.h" - -using namespace std::chrono_literals; - -struct AtomicLock { - std::atomic& locked_; - - AtomicLock(const AtomicLock&) = delete; - AtomicLock& operator=(const AtomicLock&) = delete; - - AtomicLock(std::atomic& l) : locked_(l) { lock(); } - ~AtomicLock() { unlock(); } - - void lock() { - while (true) { - locked_.wait(true, std::memory_order_relaxed); - bool expected = false; - if (locked_.compare_exchange_weak(expected, true, std::memory_order_acquire, std::memory_order_relaxed)) - break; - } - } - - void unlock() { - locked_.store(false, std::memory_order_release); - locked_.notify_all(); - } -}; - -// using LockState = std::atomic; -// using Lock = AtomicLock; - -// using LockState = std::mutex; -// using Lock = std::unique_lock; - -template -void test_multi_thread_lock_unlock(benchmark::State& state) { - std::uint64_t total_loop_test_param = state.range(0); - constexpr auto num_threads = 15; - std::vector threads; - threads.reserve(num_threads); - - std::atomic start_flag = 0; - std::atomic done_count = 0; - - LockState lock_state{}; - - auto func = [&start_flag, &done_count, &lock_state, total_loop_test_param](std::stop_token st) { - auto old_start = 0; - while (!st.stop_requested()) { - start_flag.wait(old_start); - old_start = start_flag.load(); - - // main things under test: locking and unlocking in the loop - for (std::uint64_t i = 0; i < total_loop_test_param; ++i) { - Lock l{lock_state}; - } - - done_count.fetch_add(1); - } - }; - - for (size_t i = 0; i < num_threads; ++i) { - threads.emplace_back(support::make_test_jthread(func)); - } - - for (auto _ : state) { - done_count = 0; - start_flag.fetch_add(1); - start_flag.notify_all(); - while (done_count < num_threads) { - std::this_thread::yield(); - } - } - for (auto& t : threads) { - t.request_stop(); - } - start_flag.fetch_add(1); - start_flag.notify_all(); - for (auto& t : threads) { - t.join(); - } -} - -void BM_atomic_wait(benchmark::State& state) { test_multi_thread_lock_unlock, AtomicLock>(state); } -BENCHMARK(BM_atomic_wait)->RangeMultiplier(2)->Range(1 << 10, 1 << 20); - -void BM_mutex(benchmark::State& state) { - test_multi_thread_lock_unlock>(state); -} -BENCHMARK(BM_mutex)->RangeMultiplier(2)->Range(1 << 10, 1 << 20); - -BENCHMARK_MAIN();