Add BENCHMARK_NAMED macro for named benchmarks without lambda (#2135)
* Add BENCHMARK_NAMED macro for named benchmarks without lambda
Closes #2128. BENCHMARK_CAPTURE creates a lambda even when no arguments
are captured, causing compiler/linker scalability issues with thousands
of benchmarks. BENCHMARK_NAMED provides the same func/name format but
passes the function pointer directly (no lambda), consistent with the
existing BENCHMARK macro.
* Move BENCHMARK_NAMED test to register_benchmark_test with name assertions
* Add Benjamin King to AUTHORS and CONTRIBUTORS
* Fix clang-format: remove trailing spaces from BENCHMARK_NAMED macro
* Fix clang-format: remove extra blank line left after test removal
---------
Co-authored-by: Roman Lebedev <lebedev.ri@gmail.com>
diff --git a/AUTHORS b/AUTHORS
index 11d28f7..f3f29d6 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -12,6 +12,7 @@
Alex Steele <steeleal123@gmail.com>
Andriy Berestovskyy <berestovskyy@gmail.com>
Arne Beer <arne@twobeer.de>
+Benjamin King <kingbenja5@gmail.com>
Carto
Cezary Skrzyński <czars1988@gmail.com>
Christian Wassermann <christian_wassermann@web.de>
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index 52e49cc..447e720 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -28,6 +28,7 @@
Andriy Berestovskyy <berestovskyy@gmail.com>
Arne Beer <arne@twobeer.de>
Bátor Tallér <bator.taller@shapr3d.com>
+Benjamin King <kingbenja5@gmail.com>
Billy Robert O'Neal III <billy.oneal@gmail.com> <bion@microsoft.com>
Cezary Skrzyński <czars1988@gmail.com>
Chris Kennelly <ckennelly@google.com> <ckennelly@ckennelly.com>
diff --git a/docs/user_guide.md b/docs/user_guide.md
index 997737f..b2e6975 100644
--- a/docs/user_guide.md
+++ b/docs/user_guide.md
@@ -491,6 +491,25 @@
Note that elements of `...args` may refer to global variables. Users should
avoid modifying global state inside of a benchmark.
+### Naming a Benchmark Without Capturing Arguments
+
+If you only need to give a benchmark a custom name (without passing extra
+arguments), use `BENCHMARK_NAMED(func, test_case_name)`. Unlike
+`BENCHMARK_CAPTURE`, this macro does not create a lambda, which avoids
+compiler and linker scalability issues when registering thousands of
+benchmarks.
+
+```c++
+void BM_Foo(benchmark::State& state) {
+ for (auto _ : state) {}
+}
+// Registers a benchmark named "BM_Foo/my_variant"
+BENCHMARK_NAMED(BM_Foo, my_variant);
+```
+
+Use `BENCHMARK_CAPTURE` when you need to pass extra arguments; use
+`BENCHMARK_NAMED` when you only need the name.
+
<a name="asymptotic-complexity" />
## Calculating Asymptotic Complexity (Big O)
diff --git a/include/benchmark/benchmark.h b/include/benchmark/benchmark.h
index f7d1341..0963e70 100644
--- a/include/benchmark/benchmark.h
+++ b/include/benchmark/benchmark.h
@@ -1560,6 +1560,28 @@
#func "/" #test_case_name, \
[](::benchmark::State& st) { func(st, __VA_ARGS__); })))
+// Register a benchmark named `func/test_case_name` which invokes `func`
+// directly (no lambda, no extra arguments). Use this instead of
+// BENCHMARK_CAPTURE when you only need a custom name and do not need to
+// pass additional arguments. This avoids the lambda overhead that causes
+// compiler and linker scalability issues when registering large numbers of
+// benchmarks.
+//
+// For example:
+//
+// void BM_Foo(benchmark::State& state) {
+// for (auto _ : state) {}
+// }
+// /* Registers a benchmark named "BM_Foo/my_variant" */
+// BENCHMARK_NAMED(BM_Foo, my_variant);
+#define BENCHMARK_NAMED(func, test_case_name) \
+ BENCHMARK_PRIVATE_DECLARE(_benchmark_) = \
+ (::benchmark::internal::RegisterBenchmarkInternal( \
+ ::benchmark::internal::make_unique< \
+ ::benchmark::internal::FunctionBenchmark>( \
+ #func "/" #test_case_name, \
+ static_cast<::benchmark::internal::Function*>(func))))
+
// This will register a benchmark for a templatized function. For example:
//
// template<int arg>
diff --git a/test/register_benchmark_test.cc b/test/register_benchmark_test.cc
index 3e39437..0ebd8f3 100644
--- a/test/register_benchmark_test.cc
+++ b/test/register_benchmark_test.cc
@@ -105,6 +105,20 @@
// No need to add cases because we don't expect them to run.
//----------------------------------------------------------------------------//
+// Test BENCHMARK_NAMED: verifies name format "func/test_case_name" and that
+// chaining (e.g. ->Threads()) works, without introducing a lambda.
+//----------------------------------------------------------------------------//
+void BM_named(benchmark::State& state) {
+ for (auto _ : state) {
+ }
+}
+BENCHMARK_NAMED(BM_named, variant_a);
+BENCHMARK_NAMED(BM_named, variant_b);
+BENCHMARK_NAMED(BM_named, variant_c)->Threads(2);
+ADD_CASES({"BM_named/variant_a"}, {"BM_named/variant_b"},
+ {"BM_named/variant_c/threads:2"});
+
+//----------------------------------------------------------------------------//
// Test RegisterBenchmark with different callable types
//----------------------------------------------------------------------------//