cpp-stat-bench 0.24.0
Benchmark library with statistics for C++.
Loading...
Searching...
No Matches
threadable_invoker.h
Go to the documentation of this file.
1/*
2 * Copyright 2021 MusicScience37 (Kenta Kabashima)
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
20#pragma once
21
22// IWYU pragma: no_include <version>
23
24#include <cstdlib>
25#include <exception>
26#include <future>
27#include <iostream>
28#include <memory>
29#include <thread>
30#include <type_traits>
31#include <utility>
32#include <vector>
33
40
41namespace stat_bench {
42namespace bench_impl {
43
44// Define invoke_result_t for C++14 and C++17
45#if defined(__cpp_lib_is_invocable) || defined(STAT_BENCH_DOCUMENTATION)
46// Since C++17
53template <typename Func, typename... Args>
54using invoke_result_t = std::invoke_result_t<Func, Args...>;
55#else
56// C++14
57template <typename Func, typename... Args>
58using invoke_result_t = std::result_of_t<Func(Args...)>; // NOLINT
59#endif
60
61// Define invoke_result_t for C++14 and C++17
62#ifdef STAT_BENCH_DOCUMENTATION
71template <typename Func, typename... Args>
72void invoke_and_ignore_return_value(const Func& func, Args&&... args) {
73 do_not_optimize(func(std::forward<Args>(args)...));
74}
75#else
86template <typename Func, typename... Args>
87auto invoke_and_ignore_return_value(const Func& func, Args&&... args)
88 -> std::enable_if_t<
89 // NOLINTNEXTLINE(modernize-type-traits): is_same_v is unavailable in C++14.
90 std::is_same<invoke_result_t<Func, Args...>, void>::value> {
91 func(std::forward<Args>(args)...);
92}
93
105template <typename Func, typename... Args>
106auto invoke_and_ignore_return_value(const Func& func, Args&&... args)
107 -> std::enable_if_t<
108 // NOLINTNEXTLINE(modernize-type-traits): is_same_v is unavailable in C++14.
109 !std::is_same<invoke_result_t<Func, Args...>, void>::value> {
110 do_not_optimize(func(std::forward<Args>(args)...));
111}
112#endif
113
118public:
127 ThreadableInvoker(std::size_t num_threads, std::size_t iterations,
128 std::size_t samples, std::size_t warm_up_samples)
129 : num_threads_(num_threads),
130 iterations_(iterations),
131 samples_(samples),
132 warm_up_samples_(warm_up_samples) {
133 if (num_threads_ == 0) {
134 throw StatBenchException("Number of threads must be at least one.");
135 }
136 if (iterations_ == 0) {
137 throw StatBenchException(
138 "Number of iterations must be at least one.");
139 }
140 if (samples_ == 0) {
141 throw StatBenchException("Number of samples must be at least one.");
142 }
143 if (samples_ <= warm_up_samples) {
144 throw StatBenchException(
145 "Number of samples for measurement must be at least one.");
146 }
147 if (num_threads_ >= 2U) {
148 barrier_ = util::create_sync_barrier(num_threads_);
149 }
150 }
151
159 template <typename Func>
160 [[nodiscard]] auto measure(const Func& func) const
161 -> std::vector<std::vector<clock::Duration>> {
162 if (num_threads_ == 1) {
163 return std::vector<std::vector<clock::Duration>>{
164 measure_here(func, 0)};
165 }
166
167 std::vector<std::promise<std::vector<clock::Duration>>> promises;
168 promises.resize(num_threads_);
169 std::vector<std::future<std::vector<clock::Duration>>> futures;
170 futures.reserve(num_threads_);
171 for (auto& promise : promises) {
172 futures.push_back(promise.get_future());
173 }
174
175 std::vector<std::thread> threads;
176 threads.reserve(num_threads_);
177 std::exception_ptr error;
178 try {
179 for (std::size_t i = 0; i < num_threads_; ++i) {
180 threads.emplace_back([this, &func, i, &promises]() {
181 barrier_->wait();
182 measure_here(func, i, promises[i]);
183 });
184 }
185 } catch (const std::exception& e) {
186 // If thread cannot be created, nothing can be done here.
187 std::cerr << e.what()
188 << std::endl; // NOLINT(performance-avoid-endl)
189 std::abort();
190 }
191 for (auto& thread : threads) {
192 thread.join();
193 }
194
195 std::vector<std::vector<clock::Duration>> durations;
196 durations.reserve(num_threads_);
197 for (auto& future : futures) {
198 durations.push_back(future.get());
199 }
200 return durations;
201 }
202
203private:
212 template <typename Func>
213 [[nodiscard]] auto measure_here(const Func& func,
214 std::size_t thread_index) const -> std::vector<clock::Duration> {
215 clock::StopWatch watch;
216
217 // warming up
218 std::size_t sample_index = 0;
219 for (; sample_index < warm_up_samples_; ++sample_index) {
221 for (std::size_t iteration_index = 0; iteration_index < iterations_;
222 ++iteration_index) {
224 func, thread_index, sample_index, iteration_index);
225 }
227 }
228
229 // actual measurement
230 watch.start(samples_);
231 for (; sample_index < samples_; ++sample_index) {
233 for (std::size_t iteration_index = 0; iteration_index < iterations_;
234 ++iteration_index) {
236 func, thread_index, sample_index, iteration_index);
237 }
239 watch.lap();
240 }
241 return watch.calc_durations();
242 }
243
252 template <typename Func>
253 void measure_here(const Func& func, std::size_t thread_index,
254 std::promise<std::vector<clock::Duration>>& promise) const {
255 try {
256 promise.set_value(measure_here(func, thread_index));
257 } catch (...) {
258 promise.set_exception(std::current_exception());
259 }
260 }
261
263 std::size_t num_threads_;
264
266 std::size_t iterations_;
267
269 std::size_t samples_;
270
272 std::size_t warm_up_samples_;
273
275 std::shared_ptr<util::ISyncBarrier> barrier_;
276};
277
278} // namespace bench_impl
279} // namespace stat_bench
Class of exceptions in this library.
ThreadableInvoker(std::size_t num_threads, std::size_t iterations, std::size_t samples, std::size_t warm_up_samples)
Constructor.
auto measure(const Func &func) const -> std::vector< std::vector< clock::Duration > >
Measure time.
Class of a stop watch.
Definition stop_watch.h:35
auto calc_durations() const
Calculate measured durations.
Definition stop_watch.h:75
void lap()
Measure a time point for a lap.
Definition stop_watch.h:68
void start(std::size_t num_laps)
Start measurement.
Definition stop_watch.h:59
Definition of do_not_optimize function.
Definition of Duration class.
Definition of memory_barrier function.
Namespace of internal implementation.
std::invoke_result_t< Func, Args... > invoke_result_t
Get the result type of invoking a function with arguments.
void invoke_and_ignore_return_value(const Func &func, Args &&... args)
Invoke a function and ignore the return value.
auto create_sync_barrier(std::size_t num_waiting_threads) -> std::shared_ptr< ISyncBarrier >
Create a barrier to synchronize threads.
Namespace of stat_bench source codes.
void do_not_optimize(T &&val) noexcept
Prevent compilers to optimize away a value.
void memory_barrier() noexcept
Prevent optimizers to move memory access beyond this function call.
Definition of StatBenchException class.
Definition of StopWatch class.
Definition of SyncBarrier class.