Add `BENCHMARK_TEMPLATE[12]_CAPTURE`, fusion of `BENCHMARK_CAPTURE` and `BENCHMARK_TEMPLATE` (#1747)

Test coverage isn't great, but not worse than the existing one.

You'd think `BENCHMARK_CAPTURE` would suffice,
but you can't pass `func<targs>` to it (due to the `<` and `>`),
and when passing `(func<targs>)` we get issues with brackets.
So i'm not sure if we can fully avoid this helper.

That being said, if there is only a single template argument,
`BENCHMARK_CAPTURE()` works fine if we avoid using function name.
This commit is contained in:
Roman Lebedev 2024-01-30 15:44:36 +03:00 committed by GitHub
parent 30a37e1b0b
commit e990563876
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 78 additions and 1 deletions

View File

@ -28,6 +28,8 @@
[Templated Benchmarks](#templated-benchmarks) [Templated Benchmarks](#templated-benchmarks)
[Templated Benchmarks that take arguments](#templated-benchmarks-with-arguments)
[Fixtures](#fixtures) [Fixtures](#fixtures)
[Custom Counters](#custom-counters) [Custom Counters](#custom-counters)
@ -574,6 +576,30 @@ Three macros are provided for adding benchmark templates.
#define BENCHMARK_TEMPLATE2(func, arg1, arg2) #define BENCHMARK_TEMPLATE2(func, arg1, arg2)
``` ```
<a name="templated-benchmarks-with-arguments" />
## Templated Benchmarks that take arguments
Sometimes there is a need to template benchmarks, and provide arguments to them.
```c++
template <class Q> void BM_Sequential_With_Step(benchmark::State& state, int step) {
Q q;
typename Q::value_type v;
for (auto _ : state) {
for (int i = state.range(0); i-=step; )
q.push(v);
for (int e = state.range(0); e-=step; )
q.Wait(&v);
}
// actually messages, not bytes:
state.SetBytesProcessed(
static_cast<int64_t>(state.iterations())*state.range(0));
}
BENCHMARK_TEMPLATE1_CAPTURE(BM_Sequential, WaitQueue<int>, Step1, 1)->Range(1<<0, 1<<10);
```
<a name="fixtures" /> <a name="fixtures" />
## Fixtures ## Fixtures

View File

@ -1523,7 +1523,7 @@ class Fixture : public internal::Benchmark {
// /* Registers a benchmark named "BM_takes_args/int_string_test` */ // /* Registers a benchmark named "BM_takes_args/int_string_test` */
// BENCHMARK_CAPTURE(BM_takes_args, int_string_test, 42, std::string("abc")); // BENCHMARK_CAPTURE(BM_takes_args, int_string_test, 42, std::string("abc"));
#define BENCHMARK_CAPTURE(func, test_case_name, ...) \ #define BENCHMARK_CAPTURE(func, test_case_name, ...) \
BENCHMARK_PRIVATE_DECLARE(func) = \ BENCHMARK_PRIVATE_DECLARE(_benchmark_) = \
(::benchmark::internal::RegisterBenchmarkInternal( \ (::benchmark::internal::RegisterBenchmarkInternal( \
new ::benchmark::internal::FunctionBenchmark( \ new ::benchmark::internal::FunctionBenchmark( \
#func "/" #test_case_name, \ #func "/" #test_case_name, \
@ -1560,6 +1560,31 @@ class Fixture : public internal::Benchmark {
#define BENCHMARK_TEMPLATE(n, a) BENCHMARK_TEMPLATE1(n, a) #define BENCHMARK_TEMPLATE(n, a) BENCHMARK_TEMPLATE1(n, a)
#endif #endif
#ifdef BENCHMARK_HAS_CXX11
// This will register a benchmark for a templatized function,
// with the additional arguments specified by `...`.
//
// For example:
//
// template <typename T, class ...ExtraArgs>`
// void BM_takes_args(benchmark::State& state, ExtraArgs&&... extra_args) {
// [...]
//}
// /* Registers a benchmark named "BM_takes_args<void>/int_string_test` */
// BENCHMARK_TEMPLATE1_CAPTURE(BM_takes_args, void, int_string_test, 42,
// std::string("abc"));
#define BENCHMARK_TEMPLATE1_CAPTURE(func, a, test_case_name, ...) \
BENCHMARK_CAPTURE(func<a>, test_case_name, __VA_ARGS__)
#define BENCHMARK_TEMPLATE2_CAPTURE(func, a, b, test_case_name, ...) \
BENCHMARK_PRIVATE_DECLARE(func) = \
(::benchmark::internal::RegisterBenchmarkInternal( \
new ::benchmark::internal::FunctionBenchmark( \
#func "<" #a "," #b ">" \
"/" #test_case_name, \
[](::benchmark::State& st) { func<a, b>(st, __VA_ARGS__); })))
#endif // BENCHMARK_HAS_CXX11
#define BENCHMARK_PRIVATE_DECLARE_F(BaseClass, Method) \ #define BENCHMARK_PRIVATE_DECLARE_F(BaseClass, Method) \
class BaseClass##_##Method##_Benchmark : public BaseClass { \ class BaseClass##_##Method##_Benchmark : public BaseClass { \
public: \ public: \

View File

@ -16,6 +16,7 @@
#include <sstream> #include <sstream>
#include <string> #include <string>
#include <thread> #include <thread>
#include <type_traits>
#include <utility> #include <utility>
#include <vector> #include <vector>
@ -226,6 +227,31 @@ void BM_non_template_args(benchmark::State& state, int, double) {
} }
BENCHMARK_CAPTURE(BM_non_template_args, basic_test, 0, 0); BENCHMARK_CAPTURE(BM_non_template_args, basic_test, 0, 0);
template <class T, class U, class... ExtraArgs>
void BM_template2_capture(benchmark::State& state, ExtraArgs&&... extra_args) {
static_assert(std::is_same<T, void>::value, "");
static_assert(std::is_same<U, char*>::value, "");
static_assert(std::is_same<ExtraArgs..., unsigned int>::value, "");
unsigned int dummy[sizeof...(ExtraArgs)] = {extra_args...};
assert(dummy[0] == 42);
for (auto _ : state) {
}
}
BENCHMARK_TEMPLATE2_CAPTURE(BM_template2_capture, void, char*, foo, 42U);
BENCHMARK_CAPTURE((BM_template2_capture<void, char*>), foo, 42U);
template <class T, class... ExtraArgs>
void BM_template1_capture(benchmark::State& state, ExtraArgs&&... extra_args) {
static_assert(std::is_same<T, void>::value, "");
static_assert(std::is_same<ExtraArgs..., unsigned long>::value, "");
unsigned long dummy[sizeof...(ExtraArgs)] = {extra_args...};
assert(dummy[0] == 24);
for (auto _ : state) {
}
}
BENCHMARK_TEMPLATE1_CAPTURE(BM_template1_capture, void, foo, 24UL);
BENCHMARK_CAPTURE(BM_template1_capture<void>, foo, 24UL);
#endif // BENCHMARK_HAS_CXX11 #endif // BENCHMARK_HAS_CXX11
static void BM_DenseThreadRanges(benchmark::State& st) { static void BM_DenseThreadRanges(benchmark::State& st) {