1
0
mirror of https://github.com/google/benchmark.git synced 2025-04-21 17:00:28 +08:00

Compare commits

...

6 Commits
v1.9.2 ... main

Author SHA1 Message Date
dominic
48f5cc21ba
Deprecate ubuntu-20.04 images in actions ()
https://github.com/actions/runner-images/issues/11101
2025-04-16 11:29:10 +01:00
krzikalla
ff52b227db
Fixed private macro name issue () 2025-04-11 15:02:03 +01:00
krzikalla
f828d71c59
Method templates for Fixtures introduced () 2025-04-11 12:25:46 +01:00
krzikalla
0da57b85cf
Threading api refactor ()
Refactor the multi-threading api to support
using custom user-provided thread factory
instead of always spawning POSIX Threads.
2025-03-29 10:49:25 +03:00
krzikalla
2918a094b0
Refactor threading run ()
* ThreadManager::WaitForAllThreads removed

* WaitForAllThreads was only called either in single threaded
  environments or just before all threads are joined anyway. As this
  doesn't add a useful synchronization point, it's removed.

* Formatting issue

* Thread Sanitizer satisfied

* More formatting issues
2025-03-27 18:10:05 +03:00
dominic
cb4239f398
Use the top-level ::benchmark namespace to resolve make_unique ()
Fixes 
2025-03-27 08:22:25 +03:00
12 changed files with 405 additions and 54 deletions

View File

@ -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

View File

@ -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']

View File

@ -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

View File

@ -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) \

View File

@ -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,

View File

@ -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(); }

View File

@ -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();
}

View File

@ -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,

View File

@ -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

View File

@ -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)

View 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);
}

View 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();