178b4b5ccSLouis Dionne //===----------------------------------------------------------------------===// 26a54dfbfSLouis Dionne // 378b4b5ccSLouis Dionne // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 478b4b5ccSLouis Dionne // See https://llvm.org/LICENSE.txt for license information. 578b4b5ccSLouis Dionne // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 678b4b5ccSLouis Dionne // 778b4b5ccSLouis Dionne //===----------------------------------------------------------------------===// 878b4b5ccSLouis Dionne 9b2d24947SLouis Dionne // UNSUPPORTED: c++03, c++11, c++14, c++17 10b2d24947SLouis Dionne 11*9ebc6f5dSNikolas Klauser #include <cstdint> 12b2d24947SLouis Dionne #include <optional> 1378b4b5ccSLouis Dionne #include <stop_token> 1478b4b5ccSLouis Dionne #include <thread> 1578b4b5ccSLouis Dionne 1678b4b5ccSLouis Dionne #include "benchmark/benchmark.h" 1778b4b5ccSLouis Dionne #include "make_test_thread.h" 1878b4b5ccSLouis Dionne 1978b4b5ccSLouis Dionne using namespace std::chrono_literals; 2078b4b5ccSLouis Dionne 2178b4b5ccSLouis Dionne // We have a single thread created by std::jthread consuming the stop_token: 2278b4b5ccSLouis Dionne // polling for stop_requested. 2378b4b5ccSLouis Dionne void BM_stop_token_single_thread_polling_stop_requested(benchmark::State& state) { 2478b4b5ccSLouis Dionne auto thread_func = [&](std::stop_token st, std::atomic<std::uint64_t>* loop_count) { 2578b4b5ccSLouis Dionne while (!st.stop_requested()) { 2678b4b5ccSLouis Dionne // doing some work 2778b4b5ccSLouis Dionne loop_count->fetch_add(1, std::memory_order_relaxed); 2878b4b5ccSLouis Dionne } 2978b4b5ccSLouis Dionne }; 3078b4b5ccSLouis Dionne 3178b4b5ccSLouis Dionne std::atomic<std::uint64_t> loop_count(0); 3278b4b5ccSLouis Dionne std::uint64_t total_loop_test_param = state.range(0); 3378b4b5ccSLouis Dionne 3478b4b5ccSLouis Dionne auto thread = support::make_test_jthread(thread_func, &loop_count); 3578b4b5ccSLouis Dionne 3678b4b5ccSLouis Dionne for (auto _ : state) { 3778b4b5ccSLouis Dionne auto start_total = loop_count.load(std::memory_order_relaxed); 3878b4b5ccSLouis Dionne 3978b4b5ccSLouis Dionne while (loop_count.load(std::memory_order_relaxed) - start_total < total_loop_test_param) { 4078b4b5ccSLouis Dionne std::this_thread::yield(); 4178b4b5ccSLouis Dionne } 4278b4b5ccSLouis Dionne } 4378b4b5ccSLouis Dionne } 4478b4b5ccSLouis Dionne 4578b4b5ccSLouis Dionne BENCHMARK(BM_stop_token_single_thread_polling_stop_requested)->RangeMultiplier(2)->Range(1 << 10, 1 << 24); 4678b4b5ccSLouis Dionne 4778b4b5ccSLouis Dionne // We have multiple threads polling for stop_requested of the same stop_token. 4878b4b5ccSLouis Dionne void BM_stop_token_multi_thread_polling_stop_requested(benchmark::State& state) { 4978b4b5ccSLouis Dionne std::atomic<bool> start{false}; 5078b4b5ccSLouis Dionne 5178b4b5ccSLouis Dionne auto thread_func = [&start](std::atomic<std::uint64_t>* loop_count, std::stop_token st) { 5278b4b5ccSLouis Dionne start.wait(false); 5378b4b5ccSLouis Dionne while (!st.stop_requested()) { 5478b4b5ccSLouis Dionne // doing some work 5578b4b5ccSLouis Dionne loop_count->fetch_add(1, std::memory_order_relaxed); 5678b4b5ccSLouis Dionne } 5778b4b5ccSLouis Dionne }; 5878b4b5ccSLouis Dionne 5978b4b5ccSLouis Dionne constexpr size_t thread_count = 20; 6078b4b5ccSLouis Dionne 6178b4b5ccSLouis Dionne std::uint64_t total_loop_test_param = state.range(0); 6278b4b5ccSLouis Dionne 6378b4b5ccSLouis Dionne std::vector<std::atomic<std::uint64_t>> loop_counts(thread_count); 6478b4b5ccSLouis Dionne std::stop_source ss; 6578b4b5ccSLouis Dionne std::vector<std::jthread> threads; 6678b4b5ccSLouis Dionne threads.reserve(thread_count); 6778b4b5ccSLouis Dionne 6878b4b5ccSLouis Dionne for (size_t i = 0; i < thread_count; ++i) { 6978b4b5ccSLouis Dionne threads.emplace_back(support::make_test_jthread(thread_func, &loop_counts[i], ss.get_token())); 7078b4b5ccSLouis Dionne } 7178b4b5ccSLouis Dionne 7278b4b5ccSLouis Dionne auto get_total_loop = [&loop_counts] { 7378b4b5ccSLouis Dionne std::uint64_t total = 0; 7478b4b5ccSLouis Dionne for (const auto& loop_count : loop_counts) { 7578b4b5ccSLouis Dionne total += loop_count.load(std::memory_order_relaxed); 7678b4b5ccSLouis Dionne } 7778b4b5ccSLouis Dionne return total; 7878b4b5ccSLouis Dionne }; 7978b4b5ccSLouis Dionne 8078b4b5ccSLouis Dionne start = true; 8178b4b5ccSLouis Dionne start.notify_all(); 8278b4b5ccSLouis Dionne 8378b4b5ccSLouis Dionne for (auto _ : state) { 8478b4b5ccSLouis Dionne auto start_total = get_total_loop(); 8578b4b5ccSLouis Dionne 8678b4b5ccSLouis Dionne while (get_total_loop() - start_total < total_loop_test_param) { 8778b4b5ccSLouis Dionne std::this_thread::yield(); 8878b4b5ccSLouis Dionne } 8978b4b5ccSLouis Dionne } 9078b4b5ccSLouis Dionne 9178b4b5ccSLouis Dionne ss.request_stop(); 9278b4b5ccSLouis Dionne } 9378b4b5ccSLouis Dionne 9478b4b5ccSLouis Dionne BENCHMARK(BM_stop_token_multi_thread_polling_stop_requested)->RangeMultiplier(2)->Range(1 << 10, 1 << 24); 9578b4b5ccSLouis Dionne 9678b4b5ccSLouis Dionne // We have a single thread created by std::jthread consuming the stop_token: 9778b4b5ccSLouis Dionne // registering/deregistering callbacks, one at a time. 9878b4b5ccSLouis Dionne void BM_stop_token_single_thread_reg_unreg_callback(benchmark::State& state) { 9978b4b5ccSLouis Dionne auto thread_func = [&](std::stop_token st, std::atomic<std::uint64_t>* reg_count) { 10078b4b5ccSLouis Dionne while (!st.stop_requested()) { 10178b4b5ccSLouis Dionne std::stop_callback cb{st, [&]() noexcept {}}; 10278b4b5ccSLouis Dionne benchmark::DoNotOptimize(cb); 10378b4b5ccSLouis Dionne reg_count->fetch_add(1, std::memory_order_relaxed); 10478b4b5ccSLouis Dionne } 10578b4b5ccSLouis Dionne }; 10678b4b5ccSLouis Dionne 10778b4b5ccSLouis Dionne std::atomic<std::uint64_t> reg_count(0); 10878b4b5ccSLouis Dionne std::uint64_t total_reg_test_param = state.range(0); 10978b4b5ccSLouis Dionne 11078b4b5ccSLouis Dionne auto thread = support::make_test_jthread(thread_func, ®_count); 11178b4b5ccSLouis Dionne 11278b4b5ccSLouis Dionne for (auto _ : state) { 11378b4b5ccSLouis Dionne auto start_total = reg_count.load(std::memory_order_relaxed); 11478b4b5ccSLouis Dionne 11578b4b5ccSLouis Dionne while (reg_count.load(std::memory_order_relaxed) - start_total < total_reg_test_param) { 11678b4b5ccSLouis Dionne std::this_thread::yield(); 11778b4b5ccSLouis Dionne } 11878b4b5ccSLouis Dionne } 11978b4b5ccSLouis Dionne } 12078b4b5ccSLouis Dionne BENCHMARK(BM_stop_token_single_thread_reg_unreg_callback)->RangeMultiplier(2)->Range(1 << 10, 1 << 24); 12178b4b5ccSLouis Dionne 12278b4b5ccSLouis Dionne // At startup, it creates a single stop_source which it will then pass an associated stop_token to every 12378b4b5ccSLouis Dionne // request. 12478b4b5ccSLouis Dionne // 12578b4b5ccSLouis Dionne // Assume a thread-pool handles these requests and for each request it polls for stop_requested(), then attaches a 12678b4b5ccSLouis Dionne // stop-callback, does some work, then detaches the stop-callback some time later. The lifetime of requests/callbacks 12778b4b5ccSLouis Dionne // would overlap with other requests/callback from the same thread. 12878b4b5ccSLouis Dionne // 12978b4b5ccSLouis Dionne // Say something like each thread keeping a circular buffer of N stop-callbacks and destroying the stop-callbacks in 13078b4b5ccSLouis Dionne // FIFO order 13178b4b5ccSLouis Dionne void BM_stop_token_async_reg_unreg_callback(benchmark::State& state) { 13278b4b5ccSLouis Dionne struct dummy_stop_callback { 13378b4b5ccSLouis Dionne void operator()() const noexcept {} 13478b4b5ccSLouis Dionne }; 13578b4b5ccSLouis Dionne 13678b4b5ccSLouis Dionne constexpr size_t thread_count = 20; 13778b4b5ccSLouis Dionne constexpr size_t concurrent_request_count = 1000; 13878b4b5ccSLouis Dionne std::atomic<bool> start{false}; 13978b4b5ccSLouis Dionne 14078b4b5ccSLouis Dionne std::uint64_t total_reg_test_param = state.range(0); 14178b4b5ccSLouis Dionne std::vector<std::atomic<std::uint64_t>> reg_counts(thread_count); 14278b4b5ccSLouis Dionne 14378b4b5ccSLouis Dionne std::stop_source ss; 14478b4b5ccSLouis Dionne std::vector<std::jthread> threads; 14578b4b5ccSLouis Dionne threads.reserve(thread_count); 14678b4b5ccSLouis Dionne 14778b4b5ccSLouis Dionne auto thread_func = [&start](std::atomic<std::uint64_t>* count, std::stop_token st) { 14878b4b5ccSLouis Dionne std::vector<std::optional<std::stop_callback<dummy_stop_callback>>> cbs(concurrent_request_count); 14978b4b5ccSLouis Dionne 15078b4b5ccSLouis Dionne start.wait(false); 15178b4b5ccSLouis Dionne 15278b4b5ccSLouis Dionne std::uint32_t index = 0; 15378b4b5ccSLouis Dionne while (!st.stop_requested()) { 15478b4b5ccSLouis Dionne cbs[index].emplace(st, dummy_stop_callback{}); 15578b4b5ccSLouis Dionne index = (index + 1) % concurrent_request_count; 15678b4b5ccSLouis Dionne count->fetch_add(1, std::memory_order_relaxed); 15778b4b5ccSLouis Dionne } 15878b4b5ccSLouis Dionne }; 15978b4b5ccSLouis Dionne 16078b4b5ccSLouis Dionne for (size_t i = 0; i < thread_count; ++i) { 16178b4b5ccSLouis Dionne threads.emplace_back(support::make_test_jthread(thread_func, ®_counts[i], ss.get_token())); 16278b4b5ccSLouis Dionne } 16378b4b5ccSLouis Dionne 16478b4b5ccSLouis Dionne auto get_total_reg = [&] { 16578b4b5ccSLouis Dionne std::uint64_t total = 0; 16678b4b5ccSLouis Dionne for (const auto& reg_count : reg_counts) { 16778b4b5ccSLouis Dionne total += reg_count.load(std::memory_order_relaxed); 16878b4b5ccSLouis Dionne } 16978b4b5ccSLouis Dionne return total; 17078b4b5ccSLouis Dionne }; 17178b4b5ccSLouis Dionne 17278b4b5ccSLouis Dionne start = true; 17378b4b5ccSLouis Dionne start.notify_all(); 17478b4b5ccSLouis Dionne 17578b4b5ccSLouis Dionne for (auto _ : state) { 17678b4b5ccSLouis Dionne auto start_total = get_total_reg(); 17778b4b5ccSLouis Dionne 17878b4b5ccSLouis Dionne while (get_total_reg() - start_total < total_reg_test_param) { 17978b4b5ccSLouis Dionne std::this_thread::yield(); 18078b4b5ccSLouis Dionne } 18178b4b5ccSLouis Dionne } 18278b4b5ccSLouis Dionne 18378b4b5ccSLouis Dionne ss.request_stop(); 18478b4b5ccSLouis Dionne } 18578b4b5ccSLouis Dionne BENCHMARK(BM_stop_token_async_reg_unreg_callback)->RangeMultiplier(2)->Range(1 << 10, 1 << 24); 18678b4b5ccSLouis Dionne 18778b4b5ccSLouis Dionne BENCHMARK_MAIN(); 188