134 lines
4.5 KiB
C++
134 lines
4.5 KiB
C++
//===----------------------------------------------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
#include <string>
|
|
#include <tuple>
|
|
#include <type_traits>
|
|
#include <vector>
|
|
|
|
#include "benchmark/benchmark.h"
|
|
#include "test_macros.h"
|
|
|
|
namespace internal {
|
|
|
|
template <class D, class E, size_t I>
|
|
struct EnumValue : std::integral_constant<E, static_cast<E>(I)> {
|
|
static std::string name() { return std::string("_") + D::Names[I]; }
|
|
};
|
|
|
|
template <class D, class E, size_t ...Idxs>
|
|
constexpr auto makeEnumValueTuple(std::index_sequence<Idxs...>) {
|
|
return std::make_tuple(EnumValue<D, E, Idxs>{}...);
|
|
}
|
|
|
|
template <class B>
|
|
static auto skip(const B& Bench, int) -> decltype(Bench.skip()) {
|
|
return Bench.skip();
|
|
}
|
|
template <class B>
|
|
static auto skip(const B& Bench, char) {
|
|
return false;
|
|
}
|
|
|
|
template <class B, class Args, size_t... Is>
|
|
void makeBenchmarkFromValuesImpl(const Args& A, std::index_sequence<Is...>) {
|
|
for (auto& V : A) {
|
|
B Bench{std::get<Is>(V)...};
|
|
if (!internal::skip(Bench, 0)) {
|
|
benchmark::RegisterBenchmark(Bench.name().c_str(),
|
|
[=](benchmark::State& S) { Bench.run(S); });
|
|
}
|
|
}
|
|
}
|
|
|
|
template <class B, class... Args>
|
|
void makeBenchmarkFromValues(const std::vector<std::tuple<Args...> >& A) {
|
|
makeBenchmarkFromValuesImpl<B>(A, std::index_sequence_for<Args...>());
|
|
}
|
|
|
|
template <template <class...> class B, class Args, class... U>
|
|
void makeBenchmarkImpl(const Args& A, std::tuple<U...> t) {
|
|
makeBenchmarkFromValues<B<U...> >(A);
|
|
}
|
|
|
|
template <template <class...> class B, class Args, class... U,
|
|
class... T, class... Tuples>
|
|
void makeBenchmarkImpl(const Args& A, std::tuple<U...>, std::tuple<T...>,
|
|
Tuples... rest) {
|
|
(internal::makeBenchmarkImpl<B>(A, std::tuple<U..., T>(), rest...), ...);
|
|
}
|
|
|
|
template <class R, class T>
|
|
void allValueCombinations(R& Result, const T& Final) {
|
|
return Result.push_back(Final);
|
|
}
|
|
|
|
template <class R, class T, class V, class... Vs>
|
|
void allValueCombinations(R& Result, const T& Prev, const V& Value,
|
|
const Vs&... Values) {
|
|
for (const auto& E : Value) {
|
|
allValueCombinations(Result, std::tuple_cat(Prev, std::make_tuple(E)),
|
|
Values...);
|
|
}
|
|
}
|
|
|
|
} // namespace internal
|
|
|
|
// CRTP class that enables using enum types as a dimension for
|
|
// makeCartesianProductBenchmark below.
|
|
// The type passed to `B` will be a std::integral_constant<E, e>, with the
|
|
// additional static function `name()` that returns the stringified name of the
|
|
// label.
|
|
//
|
|
// Eg:
|
|
// enum class MyEnum { A, B };
|
|
// struct AllMyEnum : EnumValuesAsTuple<AllMyEnum, MyEnum, 2> {
|
|
// static constexpr absl::string_view Names[] = {"A", "B"};
|
|
// };
|
|
template <class Derived, class EnumType, size_t NumLabels>
|
|
using EnumValuesAsTuple =
|
|
decltype(internal::makeEnumValueTuple<Derived, EnumType>(
|
|
std::make_index_sequence<NumLabels>{}));
|
|
|
|
// Instantiates B<T0, T1, ..., TN> where <Ti...> are the combinations in the
|
|
// cartesian product of `Tuples...`, and pass (arg0, ..., argN) as constructor
|
|
// arguments where `(argi...)` are the combination in the cartesian product of
|
|
// the runtime values of `A...`.
|
|
// B<T...> requires:
|
|
// - std::string name(args...): The name of the benchmark.
|
|
// - void run(benchmark::State&, args...): The body of the benchmark.
|
|
// It can also optionally provide:
|
|
// - bool skip(args...): When `true`, skips the combination. Default is false.
|
|
//
|
|
// Returns int to facilitate registration. The return value is unspecified.
|
|
template <template <class...> class B, class... Tuples, class... Args>
|
|
int makeCartesianProductBenchmark(const Args&... A) {
|
|
std::vector<std::tuple<typename Args::value_type...> > V;
|
|
internal::allValueCombinations(V, std::tuple<>(), A...);
|
|
internal::makeBenchmarkImpl<B>(V, std::tuple<>(), Tuples()...);
|
|
return 0;
|
|
}
|
|
|
|
template <class B, class... Args>
|
|
int makeCartesianProductBenchmark(const Args&... A) {
|
|
std::vector<std::tuple<typename Args::value_type...> > V;
|
|
internal::allValueCombinations(V, std::tuple<>(), A...);
|
|
internal::makeBenchmarkFromValues<B>(V);
|
|
return 0;
|
|
}
|
|
|
|
// When `opaque` is true, this function hides the runtime state of `value` from
|
|
// the optimizer.
|
|
// It returns `value`.
|
|
template <class T>
|
|
TEST_ALWAYS_INLINE inline T maybeOpaque(T value, bool opaque) {
|
|
if (opaque) benchmark::DoNotOptimize(value);
|
|
return value;
|
|
}
|