1 | //===----------------------------------------------------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include <string> |
10 | #include <tuple> |
11 | #include <type_traits> |
12 | #include <vector> |
13 | |
14 | #include "benchmark/benchmark.h" |
15 | #include "test_macros.h" |
16 | |
17 | namespace internal { |
18 | |
19 | template <class D, class E, size_t I> |
20 | struct EnumValue : std::integral_constant<E, static_cast<E>(I)> { |
21 | static std::string name() { return std::string("_" ) + D::Names[I]; } |
22 | }; |
23 | |
24 | template <class D, class E, size_t... Idxs> |
25 | constexpr auto makeEnumValueTuple(std::index_sequence<Idxs...>) { |
26 | return std::make_tuple(EnumValue<D, E, Idxs>{}...); |
27 | } |
28 | |
29 | template <class B> |
30 | static auto skip(const B& Bench, int) -> decltype(Bench.skip()) { |
31 | return Bench.skip(); |
32 | } |
33 | template <class B> |
34 | static auto skip(const B& Bench, char) { |
35 | return false; |
36 | } |
37 | |
38 | template <class B, class Args, size_t... Is> |
39 | void makeBenchmarkFromValuesImpl(const Args& A, std::index_sequence<Is...>) { |
40 | for (auto& V : A) { |
41 | B Bench{std::get<Is>(V)...}; |
42 | if (!internal::skip(Bench, 0)) { |
43 | benchmark::RegisterBenchmark(Bench.name().c_str(), [=](benchmark::State& S) { Bench.run(S); }); |
44 | } |
45 | } |
46 | } |
47 | |
48 | template <class B, class... Args> |
49 | void makeBenchmarkFromValues(const std::vector<std::tuple<Args...> >& A) { |
50 | makeBenchmarkFromValuesImpl<B>(A, std::index_sequence_for<Args...>()); |
51 | } |
52 | |
53 | template <template <class...> class B, class Args, class... U> |
54 | void makeBenchmarkImpl(const Args& A, std::tuple<U...> t) { |
55 | makeBenchmarkFromValues<B<U...> >(A); |
56 | } |
57 | |
58 | template <template <class...> class B, class Args, class... U, class... T, class... Tuples> |
59 | void makeBenchmarkImpl(const Args& A, std::tuple<U...>, std::tuple<T...>, Tuples... rest) { |
60 | (internal::makeBenchmarkImpl<B>(A, std::tuple<U..., T>(), rest...), ...); |
61 | } |
62 | |
63 | template <class R, class T> |
64 | void allValueCombinations(R& Result, const T& Final) { |
65 | return Result.push_back(Final); |
66 | } |
67 | |
68 | template <class R, class T, class V, class... Vs> |
69 | void allValueCombinations(R& Result, const T& Prev, const V& Value, const Vs&... Values) { |
70 | for (const auto& E : Value) { |
71 | allValueCombinations(Result, std::tuple_cat(Prev, std::make_tuple(E)), Values...); |
72 | } |
73 | } |
74 | |
75 | } // namespace internal |
76 | |
77 | // CRTP class that enables using enum types as a dimension for |
78 | // makeCartesianProductBenchmark below. |
79 | // The type passed to `B` will be a std::integral_constant<E, e>, with the |
80 | // additional static function `name()` that returns the stringified name of the |
81 | // label. |
82 | // |
83 | // Eg: |
84 | // enum class MyEnum { A, B }; |
85 | // struct AllMyEnum : EnumValuesAsTuple<AllMyEnum, MyEnum, 2> { |
86 | // static constexpr absl::string_view Names[] = {"A", "B"}; |
87 | // }; |
88 | template <class Derived, class EnumType, size_t NumLabels> |
89 | using EnumValuesAsTuple = |
90 | decltype(internal::makeEnumValueTuple<Derived, EnumType>(std::make_index_sequence<NumLabels>{})); |
91 | |
92 | // Instantiates B<T0, T1, ..., TN> where <Ti...> are the combinations in the |
93 | // cartesian product of `Tuples...`, and pass (arg0, ..., argN) as constructor |
94 | // arguments where `(argi...)` are the combination in the cartesian product of |
95 | // the runtime values of `A...`. |
96 | // B<T...> requires: |
97 | // - std::string name(args...): The name of the benchmark. |
98 | // - void run(benchmark::State&, args...): The body of the benchmark. |
99 | // It can also optionally provide: |
100 | // - bool skip(args...): When `true`, skips the combination. Default is false. |
101 | // |
102 | // Returns int to facilitate registration. The return value is unspecified. |
103 | template <template <class...> class B, class... Tuples, class... Args> |
104 | int makeCartesianProductBenchmark(const Args&... A) { |
105 | std::vector<std::tuple<typename Args::value_type...> > V; |
106 | internal::allValueCombinations(V, std::tuple<>(), A...); |
107 | internal::makeBenchmarkImpl<B>(V, std::tuple<>(), Tuples()...); |
108 | return 0; |
109 | } |
110 | |
111 | template <class B, class... Args> |
112 | int makeCartesianProductBenchmark(const Args&... A) { |
113 | std::vector<std::tuple<typename Args::value_type...> > V; |
114 | internal::allValueCombinations(V, std::tuple<>(), A...); |
115 | internal::makeBenchmarkFromValues<B>(V); |
116 | return 0; |
117 | } |
118 | |
119 | // When `opaque` is true, this function hides the runtime state of `value` from |
120 | // the optimizer. |
121 | // It returns `value`. |
122 | template <class T> |
123 | TEST_ALWAYS_INLINE inline T maybeOpaque(T value, bool opaque) { |
124 | if (opaque) |
125 | benchmark::DoNotOptimize(value); |
126 | return value; |
127 | } |
128 | |