|  | //===----------------------------------------------------------------------===// | 
|  | // | 
|  | // 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; | 
|  | } |