mirror of
https://github.com/google/benchmark.git
synced 2025-04-21 17:00:28 +08:00
Compare commits
6 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
48f5cc21ba | ||
|
ff52b227db | ||
|
f828d71c59 | ||
|
0da57b85cf | ||
|
2918a094b0 | ||
|
cb4239f398 |
@ -17,7 +17,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-22.04, ubuntu-20.04]
|
||||
os: [ubuntu-latest]
|
||||
build_type: ['Release', 'Debug']
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
2
.github/workflows/build-and-test.yml
vendored
2
.github/workflows/build-and-test.yml
vendored
@ -20,7 +20,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-22.04, ubuntu-20.04, ubuntu-22.04-arm, macos-latest]
|
||||
os: [ubuntu-24.04, ubuntu-22.04, ubuntu-24.04-arm, macos-latest]
|
||||
build_type: ['Release', 'Debug']
|
||||
compiler: ['g++', 'clang++']
|
||||
lib: ['shared', 'static']
|
||||
|
@ -677,6 +677,54 @@ BENCHMARK_REGISTER_F(MyFixture, DoubleTest)->Threads(2);
|
||||
// `DoubleTest` is now registered.
|
||||
```
|
||||
|
||||
If you want to use a method template for your fixtures,
|
||||
which you instantiate afterward, use the following macros:
|
||||
|
||||
* `BENCHMARK_TEMPLATE_METHOD_F(ClassName, Method)`
|
||||
* `BENCHMARK_TEMPLATE_INSTANTIATE_F(ClassName, Method, ...)`
|
||||
|
||||
With these macros you can define one method for several instantiations.
|
||||
Example (using `MyFixture` from above):
|
||||
|
||||
```c++
|
||||
// Defines `Test` using the class template `MyFixture`.
|
||||
BENCHMARK_TEMPLATE_METHOD_F(MyFixture, Test)(benchmark::State& st) {
|
||||
for (auto _ : st) {
|
||||
...
|
||||
}
|
||||
}
|
||||
|
||||
// Instantiates and registers the benchmark `MyFixture<int>::Test`.
|
||||
BENCHMARK_TEMPLATE_INSTANTIATE_F(MyFixture, Test, int)->Threads(2);
|
||||
// Instantiates and registers the benchmark `MyFixture<double>::Test`.
|
||||
BENCHMARK_TEMPLATE_INSTANTIATE_F(MyFixture, Test, double)->Threads(4);
|
||||
```
|
||||
|
||||
Inside the method definition of `BENCHMARK_TEMPLATE_METHOD_F` the type `Base` refers
|
||||
to the type of the instantiated fixture.
|
||||
Accesses to members of the fixture must be prefixed by `this->`.
|
||||
|
||||
`BENCHMARK_TEMPLATE_METHOD_F`and `BENCHMARK_TEMPLATE_INSTANTIATE_F` can only be used,
|
||||
if the fixture does not use non-type template parameters.
|
||||
If you want to pass values as template parameters, use e.g. `std::integral_constant`.
|
||||
For example:
|
||||
|
||||
```c++
|
||||
template<typename Sz>
|
||||
class SizedFixture : public benchmark::Fixture {
|
||||
static constexpr auto Size = Sz::value;
|
||||
int myValue;
|
||||
};
|
||||
|
||||
BENCHMARK_TEMPLATE_METHOD_F(SizedFixture, Test)(benchmark::State& st) {
|
||||
for (auto _ : st) {
|
||||
this->myValue = Base::Size;
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_TEMPLATE_INSTANTIATE_F(SizedFixture, Test, std::integral_constant<5>)->Threads(2);
|
||||
```
|
||||
|
||||
<a name="custom-counters" />
|
||||
|
||||
## Custom Counters
|
||||
@ -863,6 +911,46 @@ BENCHMARK(BM_test)->Range(8, 8<<10)->UseRealTime();
|
||||
|
||||
Without `UseRealTime`, CPU time is used by default.
|
||||
|
||||
### Manual Multithreaded Benchmarks
|
||||
|
||||
Google/benchmark uses `std::thread` as multithreading environment per default.
|
||||
If you want to use another multithreading environment (e.g. OpenMP), you can provide
|
||||
a factory function to your benchmark using the `ThreadRunner` function.
|
||||
The factory function takes the number of threads as argument and creates a custom class
|
||||
derived from `benchmark::ThreadRunnerBase`.
|
||||
This custom class must override the function
|
||||
`void RunThreads(const std::function<void(int)>& fn)`.
|
||||
`RunThreads` is called by the main thread and spawns the requested number of threads.
|
||||
Each spawned thread must call `fn(thread_index)`, where `thread_index` is its own
|
||||
thread index. Before `RunThreads` returns, all spawned threads must be joined.
|
||||
```c++
|
||||
class OpenMPThreadRunner : public benchmark::ThreadRunnerBase
|
||||
{
|
||||
OpenMPThreadRunner(int num_threads)
|
||||
: num_threads_(num_threads)
|
||||
{}
|
||||
|
||||
void RunThreads(const std::function<void(int)>& fn) final
|
||||
{
|
||||
#pragma omp parallel num_threads(num_threads_)
|
||||
fn(omp_get_thread_num());
|
||||
}
|
||||
|
||||
private:
|
||||
int num_threads_;
|
||||
};
|
||||
|
||||
BENCHMARK(BM_MultiThreaded)
|
||||
->ThreadRunner([](int num_threads) {
|
||||
return std::make_unique<OpenMPThreadRunner>(num_threads);
|
||||
})
|
||||
->Threads(1)->Threads(2)->Threads(4);
|
||||
```
|
||||
The above example creates a parallel OpenMP region before it enters `BM_MultiThreaded`.
|
||||
The actual benchmark code can remain the same and is therefore not tied to a specific
|
||||
thread runner. The measurement does not include the time for creating and joining the
|
||||
threads.
|
||||
|
||||
<a name="cpu-timers" />
|
||||
|
||||
## CPU Timers
|
||||
|
@ -1093,8 +1093,18 @@ inline BENCHMARK_ALWAYS_INLINE State::StateIterator State::end() {
|
||||
return StateIterator();
|
||||
}
|
||||
|
||||
// Base class for user-defined multi-threading
|
||||
struct ThreadRunnerBase {
|
||||
virtual ~ThreadRunnerBase() {}
|
||||
virtual void RunThreads(const std::function<void(int)>& fn) = 0;
|
||||
};
|
||||
|
||||
namespace internal {
|
||||
|
||||
// Define alias of ThreadRunner factory function type
|
||||
using threadrunner_factory =
|
||||
std::function<std::unique_ptr<ThreadRunnerBase>(int)>;
|
||||
|
||||
typedef void(Function)(State&);
|
||||
|
||||
// ------------------------------------------------------
|
||||
@ -1299,6 +1309,9 @@ class BENCHMARK_EXPORT Benchmark {
|
||||
// Equivalent to ThreadRange(NumCPUs(), NumCPUs())
|
||||
Benchmark* ThreadPerCpu();
|
||||
|
||||
// Sets a user-defined threadrunner (see ThreadRunnerBase)
|
||||
Benchmark* ThreadRunner(threadrunner_factory&& factory);
|
||||
|
||||
virtual void Run(State& state) = 0;
|
||||
|
||||
TimeUnit GetTimeUnit() const;
|
||||
@ -1340,6 +1353,8 @@ class BENCHMARK_EXPORT Benchmark {
|
||||
callback_function setup_;
|
||||
callback_function teardown_;
|
||||
|
||||
threadrunner_factory threadrunner_;
|
||||
|
||||
BENCHMARK_DISALLOW_COPY_AND_ASSIGN(Benchmark);
|
||||
};
|
||||
|
||||
@ -1391,7 +1406,8 @@ class LambdaBenchmark : public Benchmark {
|
||||
inline internal::Benchmark* RegisterBenchmark(const std::string& name,
|
||||
internal::Function* fn) {
|
||||
return internal::RegisterBenchmarkInternal(
|
||||
benchmark::internal::make_unique<internal::FunctionBenchmark>(name, fn));
|
||||
::benchmark::internal::make_unique<internal::FunctionBenchmark>(name,
|
||||
fn));
|
||||
}
|
||||
|
||||
template <class Lambda>
|
||||
@ -1399,8 +1415,8 @@ internal::Benchmark* RegisterBenchmark(const std::string& name, Lambda&& fn) {
|
||||
using BenchType =
|
||||
internal::LambdaBenchmark<typename std::decay<Lambda>::type>;
|
||||
return internal::RegisterBenchmarkInternal(
|
||||
benchmark::internal::make_unique<BenchType>(name,
|
||||
std::forward<Lambda>(fn)));
|
||||
::benchmark::internal::make_unique<BenchType>(name,
|
||||
std::forward<Lambda>(fn)));
|
||||
}
|
||||
|
||||
template <class Lambda, class... Args>
|
||||
@ -1464,7 +1480,7 @@ class Fixture : public internal::Benchmark {
|
||||
#define BENCHMARK(...) \
|
||||
BENCHMARK_PRIVATE_DECLARE(_benchmark_) = \
|
||||
(::benchmark::internal::RegisterBenchmarkInternal( \
|
||||
benchmark::internal::make_unique< \
|
||||
::benchmark::internal::make_unique< \
|
||||
::benchmark::internal::FunctionBenchmark>(#__VA_ARGS__, \
|
||||
__VA_ARGS__)))
|
||||
|
||||
@ -1490,7 +1506,7 @@ class Fixture : public internal::Benchmark {
|
||||
#define BENCHMARK_CAPTURE(func, test_case_name, ...) \
|
||||
BENCHMARK_PRIVATE_DECLARE(_benchmark_) = \
|
||||
(::benchmark::internal::RegisterBenchmarkInternal( \
|
||||
benchmark::internal::make_unique< \
|
||||
::benchmark::internal::make_unique< \
|
||||
::benchmark::internal::FunctionBenchmark>( \
|
||||
#func "/" #test_case_name, \
|
||||
[](::benchmark::State& st) { func(st, __VA_ARGS__); })))
|
||||
@ -1506,20 +1522,20 @@ class Fixture : public internal::Benchmark {
|
||||
#define BENCHMARK_TEMPLATE1(n, a) \
|
||||
BENCHMARK_PRIVATE_DECLARE(n) = \
|
||||
(::benchmark::internal::RegisterBenchmarkInternal( \
|
||||
benchmark::internal::make_unique< \
|
||||
::benchmark::internal::make_unique< \
|
||||
::benchmark::internal::FunctionBenchmark>(#n "<" #a ">", n<a>)))
|
||||
|
||||
#define BENCHMARK_TEMPLATE2(n, a, b) \
|
||||
BENCHMARK_PRIVATE_DECLARE(n) = \
|
||||
(::benchmark::internal::RegisterBenchmarkInternal( \
|
||||
benchmark::internal::make_unique< \
|
||||
::benchmark::internal::make_unique< \
|
||||
::benchmark::internal::FunctionBenchmark>(#n "<" #a "," #b ">", \
|
||||
n<a, b>)))
|
||||
|
||||
#define BENCHMARK_TEMPLATE(n, ...) \
|
||||
BENCHMARK_PRIVATE_DECLARE(n) = \
|
||||
(::benchmark::internal::RegisterBenchmarkInternal( \
|
||||
benchmark::internal::make_unique< \
|
||||
::benchmark::internal::make_unique< \
|
||||
::benchmark::internal::FunctionBenchmark>( \
|
||||
#n "<" #__VA_ARGS__ ">", n<__VA_ARGS__>)))
|
||||
|
||||
@ -1541,7 +1557,7 @@ class Fixture : public internal::Benchmark {
|
||||
#define BENCHMARK_TEMPLATE2_CAPTURE(func, a, b, test_case_name, ...) \
|
||||
BENCHMARK_PRIVATE_DECLARE(func) = \
|
||||
(::benchmark::internal::RegisterBenchmarkInternal( \
|
||||
benchmark::internal::make_unique< \
|
||||
::benchmark::internal::make_unique< \
|
||||
::benchmark::internal::FunctionBenchmark>( \
|
||||
#func "<" #a "," #b ">" \
|
||||
"/" #test_case_name, \
|
||||
@ -1613,7 +1629,38 @@ class Fixture : public internal::Benchmark {
|
||||
#define BENCHMARK_PRIVATE_REGISTER_F(TestName) \
|
||||
BENCHMARK_PRIVATE_DECLARE(TestName) = \
|
||||
(::benchmark::internal::RegisterBenchmarkInternal( \
|
||||
benchmark::internal::make_unique<TestName>()))
|
||||
::benchmark::internal::make_unique<TestName>()))
|
||||
|
||||
#define BENCHMARK_TEMPLATE_PRIVATE_CONCAT_NAME_F(BaseClass, Method) \
|
||||
BaseClass##_##Method##_BenchmarkTemplate
|
||||
|
||||
#define BENCHMARK_TEMPLATE_METHOD_F(BaseClass, Method) \
|
||||
template <class... Args> \
|
||||
class BENCHMARK_TEMPLATE_PRIVATE_CONCAT_NAME_F(BaseClass, Method) \
|
||||
: public BaseClass<Args...> { \
|
||||
protected: \
|
||||
using Base = BaseClass<Args...>; \
|
||||
void BenchmarkCase(::benchmark::State&) override; \
|
||||
}; \
|
||||
template <class... Args> \
|
||||
void BENCHMARK_TEMPLATE_PRIVATE_CONCAT_NAME_F( \
|
||||
BaseClass, Method)<Args...>::BenchmarkCase
|
||||
|
||||
#define BENCHMARK_TEMPLATE_PRIVATE_INSTANTIATE_F(BaseClass, Method, \
|
||||
UniqueName, ...) \
|
||||
class UniqueName : public BENCHMARK_TEMPLATE_PRIVATE_CONCAT_NAME_F( \
|
||||
BaseClass, Method)<__VA_ARGS__> { \
|
||||
public: \
|
||||
UniqueName() { this->SetName(#BaseClass "<" #__VA_ARGS__ ">/" #Method); } \
|
||||
}; \
|
||||
BENCHMARK_PRIVATE_DECLARE(BaseClass##_##Method##_Benchmark) = \
|
||||
(::benchmark::internal::RegisterBenchmarkInternal( \
|
||||
::benchmark::internal::make_unique<UniqueName>()))
|
||||
|
||||
#define BENCHMARK_TEMPLATE_INSTANTIATE_F(BaseClass, Method, ...) \
|
||||
BENCHMARK_TEMPLATE_PRIVATE_INSTANTIATE_F( \
|
||||
BaseClass, Method, BENCHMARK_PRIVATE_NAME(BaseClass##Method), \
|
||||
__VA_ARGS__)
|
||||
|
||||
// This macro will define and register a benchmark within a fixture class.
|
||||
#define BENCHMARK_F(BaseClass, Method) \
|
||||
|
@ -41,6 +41,9 @@ class BenchmarkInstance {
|
||||
int threads() const { return threads_; }
|
||||
void Setup() const;
|
||||
void Teardown() const;
|
||||
const auto& GetUserThreadRunnerFactory() const {
|
||||
return benchmark_.threadrunner_;
|
||||
}
|
||||
|
||||
State Run(IterationCount iters, int thread_id, internal::ThreadTimer* timer,
|
||||
internal::ThreadManager* manager,
|
||||
|
@ -484,6 +484,11 @@ Benchmark* Benchmark::ThreadPerCpu() {
|
||||
return this;
|
||||
}
|
||||
|
||||
Benchmark* Benchmark::ThreadRunner(threadrunner_factory&& factory) {
|
||||
threadrunner_ = std::move(factory);
|
||||
return this;
|
||||
}
|
||||
|
||||
void Benchmark::SetName(const std::string& name) { name_ = name; }
|
||||
|
||||
const char* Benchmark::GetName() const { return name_.c_str(); }
|
||||
|
@ -34,6 +34,7 @@
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
@ -182,6 +183,38 @@ IterationCount ComputeIters(const benchmark::internal::BenchmarkInstance& b,
|
||||
return iters_or_time.iters;
|
||||
}
|
||||
|
||||
class ThreadRunnerDefault : public ThreadRunnerBase {
|
||||
public:
|
||||
explicit ThreadRunnerDefault(int num_threads)
|
||||
: pool(static_cast<size_t>(num_threads - 1)) {}
|
||||
|
||||
void RunThreads(const std::function<void(int)>& fn) final {
|
||||
// Run all but one thread in separate threads
|
||||
for (std::size_t ti = 0; ti < pool.size(); ++ti) {
|
||||
pool[ti] = std::thread(fn, static_cast<int>(ti + 1));
|
||||
}
|
||||
// And run one thread here directly.
|
||||
// (If we were asked to run just one thread, we don't create new threads.)
|
||||
// Yes, we need to do this here *after* we start the separate threads.
|
||||
fn(0);
|
||||
|
||||
// The main thread has finished. Now let's wait for the other threads.
|
||||
for (std::thread& thread : pool) {
|
||||
thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::thread> pool;
|
||||
};
|
||||
|
||||
std::unique_ptr<ThreadRunnerBase> GetThreadRunner(
|
||||
const threadrunner_factory& userThreadRunnerFactory, int num_threads) {
|
||||
return userThreadRunnerFactory
|
||||
? userThreadRunnerFactory(num_threads)
|
||||
: std::make_unique<ThreadRunnerDefault>(num_threads);
|
||||
}
|
||||
|
||||
} // end namespace
|
||||
|
||||
BenchTimeType ParseBenchMinTime(const std::string& value) {
|
||||
@ -258,7 +291,8 @@ BenchmarkRunner::BenchmarkRunner(
|
||||
has_explicit_iteration_count(b.iterations() != 0 ||
|
||||
parsed_benchtime_flag.tag ==
|
||||
BenchTimeType::ITERS),
|
||||
pool(static_cast<size_t>(b.threads() - 1)),
|
||||
thread_runner(
|
||||
GetThreadRunner(b.GetUserThreadRunnerFactory(), b.threads())),
|
||||
iters(FLAGS_benchmark_dry_run
|
||||
? 1
|
||||
: (has_explicit_iteration_count
|
||||
@ -289,23 +323,10 @@ BenchmarkRunner::IterationResults BenchmarkRunner::DoNIterations() {
|
||||
std::unique_ptr<internal::ThreadManager> manager;
|
||||
manager.reset(new internal::ThreadManager(b.threads()));
|
||||
|
||||
// Run all but one thread in separate threads
|
||||
for (std::size_t ti = 0; ti < pool.size(); ++ti) {
|
||||
pool[ti] = std::thread(&RunInThread, &b, iters, static_cast<int>(ti + 1),
|
||||
manager.get(), perf_counters_measurement_ptr,
|
||||
/*profiler_manager=*/nullptr);
|
||||
}
|
||||
// And run one thread here directly.
|
||||
// (If we were asked to run just one thread, we don't create new threads.)
|
||||
// Yes, we need to do this here *after* we start the separate threads.
|
||||
RunInThread(&b, iters, 0, manager.get(), perf_counters_measurement_ptr,
|
||||
/*profiler_manager=*/nullptr);
|
||||
|
||||
// The main thread has finished. Now let's wait for the other threads.
|
||||
manager->WaitForAllThreads();
|
||||
for (std::thread& thread : pool) {
|
||||
thread.join();
|
||||
}
|
||||
thread_runner->RunThreads([&](int thread_idx) {
|
||||
RunInThread(&b, iters, thread_idx, manager.get(),
|
||||
perf_counters_measurement_ptr, /*profiler_manager=*/nullptr);
|
||||
});
|
||||
|
||||
IterationResults i;
|
||||
// Acquire the measurements/counters from the manager, UNDER THE LOCK!
|
||||
@ -434,7 +455,6 @@ MemoryManager::Result BenchmarkRunner::RunMemoryManager(
|
||||
RunInThread(&b, memory_iterations, 0, manager.get(),
|
||||
perf_counters_measurement_ptr,
|
||||
/*profiler_manager=*/nullptr);
|
||||
manager->WaitForAllThreads();
|
||||
manager.reset();
|
||||
b.Teardown();
|
||||
MemoryManager::Result memory_result;
|
||||
@ -450,7 +470,6 @@ void BenchmarkRunner::RunProfilerManager(IterationCount profile_iterations) {
|
||||
RunInThread(&b, profile_iterations, 0, manager.get(),
|
||||
/*perf_counters_measurement_ptr=*/nullptr,
|
||||
/*profiler_manager=*/profiler_manager);
|
||||
manager->WaitForAllThreads();
|
||||
manager.reset();
|
||||
b.Teardown();
|
||||
}
|
||||
|
@ -15,6 +15,7 @@
|
||||
#ifndef BENCHMARK_RUNNER_H_
|
||||
#define BENCHMARK_RUNNER_H_
|
||||
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
@ -89,7 +90,7 @@ class BenchmarkRunner {
|
||||
|
||||
int num_repetitions_done = 0;
|
||||
|
||||
std::vector<std::thread> pool;
|
||||
std::unique_ptr<ThreadRunnerBase> thread_runner;
|
||||
|
||||
IterationCount iters; // preserved between repetitions!
|
||||
// So only the first repetition has to find/calculate it,
|
||||
|
@ -11,30 +11,15 @@ namespace internal {
|
||||
|
||||
class ThreadManager {
|
||||
public:
|
||||
explicit ThreadManager(int num_threads)
|
||||
: alive_threads_(num_threads), start_stop_barrier_(num_threads) {}
|
||||
explicit ThreadManager(int num_threads) : start_stop_barrier_(num_threads) {}
|
||||
|
||||
Mutex& GetBenchmarkMutex() const RETURN_CAPABILITY(benchmark_mutex_) {
|
||||
return benchmark_mutex_;
|
||||
}
|
||||
|
||||
bool StartStopBarrier() EXCLUDES(end_cond_mutex_) {
|
||||
return start_stop_barrier_.wait();
|
||||
}
|
||||
bool StartStopBarrier() { return start_stop_barrier_.wait(); }
|
||||
|
||||
void NotifyThreadComplete() EXCLUDES(end_cond_mutex_) {
|
||||
start_stop_barrier_.removeThread();
|
||||
if (--alive_threads_ == 0) {
|
||||
MutexLock lock(end_cond_mutex_);
|
||||
end_condition_.notify_all();
|
||||
}
|
||||
}
|
||||
|
||||
void WaitForAllThreads() EXCLUDES(end_cond_mutex_) {
|
||||
MutexLock lock(end_cond_mutex_);
|
||||
end_condition_.wait(lock.native_handle(),
|
||||
[this]() { return alive_threads_ == 0; });
|
||||
}
|
||||
void NotifyThreadComplete() { start_stop_barrier_.removeThread(); }
|
||||
|
||||
struct Result {
|
||||
IterationCount iterations = 0;
|
||||
@ -51,10 +36,7 @@ class ThreadManager {
|
||||
|
||||
private:
|
||||
mutable Mutex benchmark_mutex_;
|
||||
std::atomic<int> alive_threads_;
|
||||
Barrier start_stop_barrier_;
|
||||
Mutex end_cond_mutex_;
|
||||
Condition end_condition_;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
@ -180,6 +180,9 @@ benchmark_add_test(NAME reporter_output_test COMMAND reporter_output_test --benc
|
||||
compile_output_test(templated_fixture_test)
|
||||
benchmark_add_test(NAME templated_fixture_test COMMAND templated_fixture_test --benchmark_min_time=0.01s)
|
||||
|
||||
compile_output_test(templated_fixture_method_test)
|
||||
benchmark_add_test(NAME templated_fixture_method_test COMMAND templated_fixture_method_test --benchmark_min_time=0.01s)
|
||||
|
||||
compile_output_test(user_counters_test)
|
||||
benchmark_add_test(NAME user_counters_test COMMAND user_counters_test --benchmark_min_time=0.01s)
|
||||
|
||||
@ -189,6 +192,9 @@ benchmark_add_test(NAME perf_counters_test COMMAND perf_counters_test --benchmar
|
||||
compile_output_test(internal_threading_test)
|
||||
benchmark_add_test(NAME internal_threading_test COMMAND internal_threading_test --benchmark_min_time=0.01s)
|
||||
|
||||
compile_output_test(manual_threading_test)
|
||||
benchmark_add_test(NAME manual_threading_test COMMAND manual_threading_test --benchmark_min_time=0.01s)
|
||||
|
||||
compile_output_test(report_aggregates_only_test)
|
||||
benchmark_add_test(NAME report_aggregates_only_test COMMAND report_aggregates_only_test --benchmark_min_time=0.01s)
|
||||
|
||||
|
174
test/manual_threading_test.cc
Normal file
174
test/manual_threading_test.cc
Normal file
@ -0,0 +1,174 @@
|
||||
|
||||
#include <memory>
|
||||
#undef NDEBUG
|
||||
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
#include "../src/timers.h"
|
||||
#include "benchmark/benchmark.h"
|
||||
|
||||
namespace {
|
||||
|
||||
const std::chrono::duration<double, std::milli> time_frame(50);
|
||||
const double time_frame_in_sec(
|
||||
std::chrono::duration_cast<std::chrono::duration<double, std::ratio<1, 1>>>(
|
||||
time_frame)
|
||||
.count());
|
||||
|
||||
void MyBusySpinwait() {
|
||||
const auto start = benchmark::ChronoClockNow();
|
||||
|
||||
while (true) {
|
||||
const auto now = benchmark::ChronoClockNow();
|
||||
const auto elapsed = now - start;
|
||||
|
||||
if (std::chrono::duration<double, std::chrono::seconds::period>(elapsed) >=
|
||||
time_frame) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int numRunThreadsCalled_ = 0;
|
||||
|
||||
class ManualThreadRunner : public benchmark::ThreadRunnerBase {
|
||||
public:
|
||||
explicit ManualThreadRunner(int num_threads)
|
||||
: pool(static_cast<size_t>(num_threads - 1)) {}
|
||||
|
||||
void RunThreads(const std::function<void(int)>& fn) final {
|
||||
for (std::size_t ti = 0; ti < pool.size(); ++ti) {
|
||||
pool[ti] = std::thread(fn, static_cast<int>(ti + 1));
|
||||
}
|
||||
|
||||
fn(0);
|
||||
|
||||
for (std::thread& thread : pool) {
|
||||
thread.join();
|
||||
}
|
||||
|
||||
++numRunThreadsCalled_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::thread> pool;
|
||||
};
|
||||
|
||||
// ========================================================================= //
|
||||
// --------------------------- TEST CASES BEGIN ---------------------------- //
|
||||
// ========================================================================= //
|
||||
|
||||
// ========================================================================= //
|
||||
// BM_ManualThreading
|
||||
// Creation of threads is done before the start of the measurement,
|
||||
// joining after the finish of the measurement.
|
||||
void BM_ManualThreading(benchmark::State& state) {
|
||||
for (auto _ : state) {
|
||||
MyBusySpinwait();
|
||||
state.SetIterationTime(time_frame_in_sec);
|
||||
}
|
||||
state.counters["invtime"] =
|
||||
benchmark::Counter{1, benchmark::Counter::kIsRate};
|
||||
}
|
||||
|
||||
} // end namespace
|
||||
|
||||
BENCHMARK(BM_ManualThreading)
|
||||
->Iterations(1)
|
||||
->ThreadRunner([](int num_threads) {
|
||||
return std::make_unique<ManualThreadRunner>(num_threads);
|
||||
})
|
||||
->Threads(1);
|
||||
BENCHMARK(BM_ManualThreading)
|
||||
->Iterations(1)
|
||||
->ThreadRunner([](int num_threads) {
|
||||
return std::make_unique<ManualThreadRunner>(num_threads);
|
||||
})
|
||||
->Threads(1)
|
||||
->UseRealTime();
|
||||
BENCHMARK(BM_ManualThreading)
|
||||
->Iterations(1)
|
||||
->ThreadRunner([](int num_threads) {
|
||||
return std::make_unique<ManualThreadRunner>(num_threads);
|
||||
})
|
||||
->Threads(1)
|
||||
->UseManualTime();
|
||||
BENCHMARK(BM_ManualThreading)
|
||||
->Iterations(1)
|
||||
->ThreadRunner([](int num_threads) {
|
||||
return std::make_unique<ManualThreadRunner>(num_threads);
|
||||
})
|
||||
->Threads(1)
|
||||
->MeasureProcessCPUTime();
|
||||
BENCHMARK(BM_ManualThreading)
|
||||
->Iterations(1)
|
||||
->ThreadRunner([](int num_threads) {
|
||||
return std::make_unique<ManualThreadRunner>(num_threads);
|
||||
})
|
||||
->Threads(1)
|
||||
->MeasureProcessCPUTime()
|
||||
->UseRealTime();
|
||||
BENCHMARK(BM_ManualThreading)
|
||||
->Iterations(1)
|
||||
->ThreadRunner([](int num_threads) {
|
||||
return std::make_unique<ManualThreadRunner>(num_threads);
|
||||
})
|
||||
->Threads(1)
|
||||
->MeasureProcessCPUTime()
|
||||
->UseManualTime();
|
||||
|
||||
BENCHMARK(BM_ManualThreading)
|
||||
->Iterations(1)
|
||||
->ThreadRunner([](int num_threads) {
|
||||
return std::make_unique<ManualThreadRunner>(num_threads);
|
||||
})
|
||||
->Threads(2);
|
||||
BENCHMARK(BM_ManualThreading)
|
||||
->Iterations(1)
|
||||
->ThreadRunner([](int num_threads) {
|
||||
return std::make_unique<ManualThreadRunner>(num_threads);
|
||||
})
|
||||
->Threads(2)
|
||||
->UseRealTime();
|
||||
BENCHMARK(BM_ManualThreading)
|
||||
->Iterations(1)
|
||||
->ThreadRunner([](int num_threads) {
|
||||
return std::make_unique<ManualThreadRunner>(num_threads);
|
||||
})
|
||||
->Threads(2)
|
||||
->UseManualTime();
|
||||
BENCHMARK(BM_ManualThreading)
|
||||
->Iterations(1)
|
||||
->ThreadRunner([](int num_threads) {
|
||||
return std::make_unique<ManualThreadRunner>(num_threads);
|
||||
})
|
||||
->Threads(2)
|
||||
->MeasureProcessCPUTime();
|
||||
BENCHMARK(BM_ManualThreading)
|
||||
->Iterations(1)
|
||||
->ThreadRunner([](int num_threads) {
|
||||
return std::make_unique<ManualThreadRunner>(num_threads);
|
||||
})
|
||||
->Threads(2)
|
||||
->MeasureProcessCPUTime()
|
||||
->UseRealTime();
|
||||
BENCHMARK(BM_ManualThreading)
|
||||
->Iterations(1)
|
||||
->ThreadRunner([](int num_threads) {
|
||||
return std::make_unique<ManualThreadRunner>(num_threads);
|
||||
})
|
||||
->Threads(2)
|
||||
->MeasureProcessCPUTime()
|
||||
->UseManualTime();
|
||||
|
||||
// ========================================================================= //
|
||||
// ---------------------------- TEST CASES END ----------------------------- //
|
||||
// ========================================================================= //
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
benchmark::Initialize(&argc, argv);
|
||||
benchmark::RunSpecifiedBenchmarks();
|
||||
benchmark::Shutdown();
|
||||
assert(numRunThreadsCalled_ > 0);
|
||||
}
|
26
test/templated_fixture_method_test.cc
Normal file
26
test/templated_fixture_method_test.cc
Normal file
@ -0,0 +1,26 @@
|
||||
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
|
||||
#include "benchmark/benchmark.h"
|
||||
|
||||
template <typename T>
|
||||
class MyFixture : public ::benchmark::Fixture {
|
||||
public:
|
||||
MyFixture() : data(0) {}
|
||||
|
||||
T data;
|
||||
|
||||
using type = T;
|
||||
};
|
||||
|
||||
BENCHMARK_TEMPLATE_METHOD_F(MyFixture, Foo)(benchmark::State& st) {
|
||||
for (auto _ : st) {
|
||||
this->data += typename Base::type(1);
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_TEMPLATE_INSTANTIATE_F(MyFixture, Foo, int);
|
||||
BENCHMARK_TEMPLATE_INSTANTIATE_F(MyFixture, Foo, double);
|
||||
|
||||
BENCHMARK_MAIN();
|
Loading…
Reference in New Issue
Block a user