188 lines · cpp
1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9// UNSUPPORTED: c++03, c++11, c++14, c++1710 11#include <cstdint>12#include <optional>13#include <stop_token>14#include <thread>15 16#include "benchmark/benchmark.h"17#include "make_test_thread.h"18 19using namespace std::chrono_literals;20 21// We have a single thread created by std::jthread consuming the stop_token:22// polling for stop_requested.23void BM_stop_token_single_thread_polling_stop_requested(benchmark::State& state) {24 auto thread_func = [&](std::stop_token st, std::atomic<std::uint64_t>* loop_count) {25 while (!st.stop_requested()) {26 // doing some work27 loop_count->fetch_add(1, std::memory_order_relaxed);28 }29 };30 31 std::atomic<std::uint64_t> loop_count(0);32 std::uint64_t total_loop_test_param = state.range(0);33 34 auto thread = support::make_test_jthread(thread_func, &loop_count);35 36 for (auto _ : state) {37 auto start_total = loop_count.load(std::memory_order_relaxed);38 39 while (loop_count.load(std::memory_order_relaxed) - start_total < total_loop_test_param) {40 std::this_thread::yield();41 }42 }43}44 45BENCHMARK(BM_stop_token_single_thread_polling_stop_requested)->RangeMultiplier(2)->Range(1 << 10, 1 << 24);46 47// We have multiple threads polling for stop_requested of the same stop_token.48void BM_stop_token_multi_thread_polling_stop_requested(benchmark::State& state) {49 std::atomic<bool> start{false};50 51 auto thread_func = [&start](std::atomic<std::uint64_t>* loop_count, std::stop_token st) {52 start.wait(false);53 while (!st.stop_requested()) {54 // doing some work55 loop_count->fetch_add(1, std::memory_order_relaxed);56 }57 };58 59 constexpr size_t thread_count = 20;60 61 std::uint64_t total_loop_test_param = state.range(0);62 63 std::vector<std::atomic<std::uint64_t>> loop_counts(thread_count);64 std::stop_source ss;65 std::vector<std::jthread> threads;66 threads.reserve(thread_count);67 68 for (size_t i = 0; i < thread_count; ++i) {69 threads.emplace_back(support::make_test_jthread(thread_func, &loop_counts[i], ss.get_token()));70 }71 72 auto get_total_loop = [&loop_counts] {73 std::uint64_t total = 0;74 for (const auto& loop_count : loop_counts) {75 total += loop_count.load(std::memory_order_relaxed);76 }77 return total;78 };79 80 start = true;81 start.notify_all();82 83 for (auto _ : state) {84 auto start_total = get_total_loop();85 86 while (get_total_loop() - start_total < total_loop_test_param) {87 std::this_thread::yield();88 }89 }90 91 ss.request_stop();92}93 94BENCHMARK(BM_stop_token_multi_thread_polling_stop_requested)->RangeMultiplier(2)->Range(1 << 10, 1 << 24);95 96// We have a single thread created by std::jthread consuming the stop_token:97// registering/deregistering callbacks, one at a time.98void BM_stop_token_single_thread_reg_unreg_callback(benchmark::State& state) {99 auto thread_func = [&](std::stop_token st, std::atomic<std::uint64_t>* reg_count) {100 while (!st.stop_requested()) {101 std::stop_callback cb{st, [&]() noexcept {}};102 benchmark::DoNotOptimize(cb);103 reg_count->fetch_add(1, std::memory_order_relaxed);104 }105 };106 107 std::atomic<std::uint64_t> reg_count(0);108 std::uint64_t total_reg_test_param = state.range(0);109 110 auto thread = support::make_test_jthread(thread_func, ®_count);111 112 for (auto _ : state) {113 auto start_total = reg_count.load(std::memory_order_relaxed);114 115 while (reg_count.load(std::memory_order_relaxed) - start_total < total_reg_test_param) {116 std::this_thread::yield();117 }118 }119}120BENCHMARK(BM_stop_token_single_thread_reg_unreg_callback)->RangeMultiplier(2)->Range(1 << 10, 1 << 24);121 122// At startup, it creates a single stop_source which it will then pass an associated stop_token to every123// request.124//125// Assume a thread-pool handles these requests and for each request it polls for stop_requested(), then attaches a126// stop-callback, does some work, then detaches the stop-callback some time later. The lifetime of requests/callbacks127// would overlap with other requests/callback from the same thread.128//129// Say something like each thread keeping a circular buffer of N stop-callbacks and destroying the stop-callbacks in130// FIFO order131void BM_stop_token_async_reg_unreg_callback(benchmark::State& state) {132 struct dummy_stop_callback {133 void operator()() const noexcept {}134 };135 136 constexpr size_t thread_count = 20;137 constexpr size_t concurrent_request_count = 1000;138 std::atomic<bool> start{false};139 140 std::uint64_t total_reg_test_param = state.range(0);141 std::vector<std::atomic<std::uint64_t>> reg_counts(thread_count);142 143 std::stop_source ss;144 std::vector<std::jthread> threads;145 threads.reserve(thread_count);146 147 auto thread_func = [&start](std::atomic<std::uint64_t>* count, std::stop_token st) {148 std::vector<std::optional<std::stop_callback<dummy_stop_callback>>> cbs(concurrent_request_count);149 150 start.wait(false);151 152 std::uint32_t index = 0;153 while (!st.stop_requested()) {154 cbs[index].emplace(st, dummy_stop_callback{});155 index = (index + 1) % concurrent_request_count;156 count->fetch_add(1, std::memory_order_relaxed);157 }158 };159 160 for (size_t i = 0; i < thread_count; ++i) {161 threads.emplace_back(support::make_test_jthread(thread_func, ®_counts[i], ss.get_token()));162 }163 164 auto get_total_reg = [&] {165 std::uint64_t total = 0;166 for (const auto& reg_count : reg_counts) {167 total += reg_count.load(std::memory_order_relaxed);168 }169 return total;170 };171 172 start = true;173 start.notify_all();174 175 for (auto _ : state) {176 auto start_total = get_total_reg();177 178 while (get_total_reg() - start_total < total_reg_test_param) {179 std::this_thread::yield();180 }181 }182 183 ss.request_stop();184}185BENCHMARK(BM_stop_token_async_reg_unreg_callback)->RangeMultiplier(2)->Range(1 << 10, 1 << 24);186 187BENCHMARK_MAIN();188