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 <cstdint> |
10 | #include <functional> |
11 | #include <memory> |
12 | #include <string> |
13 | |
14 | #include "CartesianBenchmarks.h" |
15 | #include "benchmark/benchmark.h" |
16 | #include "test_macros.h" |
17 | |
18 | namespace { |
19 | |
20 | enum class FunctionType { |
21 | Null, |
22 | FunctionPointer, |
23 | MemberFunctionPointer, |
24 | MemberPointer, |
25 | SmallTrivialFunctor, |
26 | SmallNonTrivialFunctor, |
27 | LargeTrivialFunctor, |
28 | LargeNonTrivialFunctor |
29 | }; |
30 | |
31 | struct AllFunctionTypes : EnumValuesAsTuple<AllFunctionTypes, FunctionType, 8> { |
32 | static constexpr const char* Names[] = { |
33 | "Null" , |
34 | "FuncPtr" , |
35 | "MemFuncPtr" , |
36 | "MemPtr" , |
37 | "SmallTrivialFunctor" , |
38 | "SmallNonTrivialFunctor" , |
39 | "LargeTrivialFunctor" , |
40 | "LargeNonTrivialFunctor" }; |
41 | }; |
42 | |
43 | enum class Opacity { kOpaque, kTransparent }; |
44 | |
45 | struct AllOpacity : EnumValuesAsTuple<AllOpacity, Opacity, 2> { |
46 | static constexpr const char* Names[] = {"Opaque" , "Transparent" }; |
47 | }; |
48 | |
49 | struct S { |
50 | int function() const { return 0; } |
51 | int field = 0; |
52 | }; |
53 | |
54 | int FunctionWithS(const S*) { return 0; } |
55 | |
56 | struct SmallTrivialFunctor { |
57 | int operator()(const S*) const { return 0; } |
58 | }; |
59 | struct SmallNonTrivialFunctor { |
60 | SmallNonTrivialFunctor() {} |
61 | SmallNonTrivialFunctor(const SmallNonTrivialFunctor&) {} |
62 | ~SmallNonTrivialFunctor() {} |
63 | int operator()(const S*) const { return 0; } |
64 | }; |
65 | struct LargeTrivialFunctor { |
66 | LargeTrivialFunctor() { |
67 | // Do not spend time initializing the padding. |
68 | } |
69 | int padding[16]; |
70 | int operator()(const S*) const { return 0; } |
71 | }; |
72 | struct LargeNonTrivialFunctor { |
73 | int padding[16]; |
74 | LargeNonTrivialFunctor() { |
75 | // Do not spend time initializing the padding. |
76 | } |
77 | LargeNonTrivialFunctor(const LargeNonTrivialFunctor&) {} |
78 | ~LargeNonTrivialFunctor() {} |
79 | int operator()(const S*) const { return 0; } |
80 | }; |
81 | |
82 | using Function = std::function<int(const S*)>; |
83 | |
84 | TEST_ALWAYS_INLINE |
85 | inline Function MakeFunction(FunctionType type, bool opaque = false) { |
86 | switch (type) { |
87 | case FunctionType::Null: |
88 | return nullptr; |
89 | case FunctionType::FunctionPointer: |
90 | return maybeOpaque(FunctionWithS, opaque); |
91 | case FunctionType::MemberFunctionPointer: |
92 | return maybeOpaque(&S::function, opaque); |
93 | case FunctionType::MemberPointer: |
94 | return maybeOpaque(&S::field, opaque); |
95 | case FunctionType::SmallTrivialFunctor: |
96 | return maybeOpaque(SmallTrivialFunctor{}, opaque); |
97 | case FunctionType::SmallNonTrivialFunctor: |
98 | return maybeOpaque(SmallNonTrivialFunctor{}, opaque); |
99 | case FunctionType::LargeTrivialFunctor: |
100 | return maybeOpaque(LargeTrivialFunctor{}, opaque); |
101 | case FunctionType::LargeNonTrivialFunctor: |
102 | return maybeOpaque(LargeNonTrivialFunctor{}, opaque); |
103 | } |
104 | } |
105 | |
106 | template <class Opacity, class FunctionType> |
107 | struct ConstructAndDestroy { |
108 | static void run(benchmark::State& state) { |
109 | for (auto _ : state) { |
110 | if (Opacity() == ::Opacity::kOpaque) { |
111 | benchmark::DoNotOptimize(MakeFunction(FunctionType(), true)); |
112 | } else { |
113 | MakeFunction(FunctionType()); |
114 | } |
115 | } |
116 | } |
117 | |
118 | static std::string name() { return "BM_ConstructAndDestroy" + FunctionType::name() + Opacity::name(); } |
119 | }; |
120 | |
121 | template <class FunctionType> |
122 | struct Copy { |
123 | static void run(benchmark::State& state) { |
124 | auto value = MakeFunction(FunctionType()); |
125 | for (auto _ : state) { |
126 | benchmark::DoNotOptimize(value); |
127 | auto copy = value; // NOLINT |
128 | benchmark::DoNotOptimize(copy); |
129 | } |
130 | } |
131 | |
132 | static std::string name() { return "BM_Copy" + FunctionType::name(); } |
133 | }; |
134 | |
135 | template <class FunctionType> |
136 | struct Move { |
137 | static void run(benchmark::State& state) { |
138 | Function values[2] = {MakeFunction(FunctionType())}; |
139 | int i = 0; |
140 | for (auto _ : state) { |
141 | benchmark::DoNotOptimize(values); |
142 | benchmark::DoNotOptimize(values[i ^ 1] = std::move(values[i])); |
143 | i ^= 1; |
144 | } |
145 | } |
146 | |
147 | static std::string name() { return "BM_Move" + FunctionType::name(); } |
148 | }; |
149 | |
150 | template <class Function1, class Function2> |
151 | struct Swap { |
152 | static void run(benchmark::State& state) { |
153 | Function values[2] = {MakeFunction(Function1()), MakeFunction(Function2())}; |
154 | for (auto _ : state) { |
155 | benchmark::DoNotOptimize(values); |
156 | values[0].swap(values[1]); |
157 | } |
158 | } |
159 | |
160 | static bool skip() { return Function1() > Function2(); } |
161 | |
162 | static std::string name() { return "BM_Swap" + Function1::name() + Function2::name(); } |
163 | }; |
164 | |
165 | template <class FunctionType> |
166 | struct OperatorBool { |
167 | static void run(benchmark::State& state) { |
168 | auto f = MakeFunction(FunctionType()); |
169 | for (auto _ : state) { |
170 | benchmark::DoNotOptimize(f); |
171 | benchmark::DoNotOptimize(static_cast<bool>(f)); |
172 | } |
173 | } |
174 | |
175 | static std::string name() { return "BM_OperatorBool" + FunctionType::name(); } |
176 | }; |
177 | |
178 | template <class FunctionType> |
179 | struct Invoke { |
180 | static void run(benchmark::State& state) { |
181 | S s; |
182 | const auto value = MakeFunction(FunctionType()); |
183 | for (auto _ : state) { |
184 | benchmark::DoNotOptimize(value); |
185 | benchmark::DoNotOptimize(value(&s)); |
186 | } |
187 | } |
188 | |
189 | static bool skip() { return FunctionType() == ::FunctionType::Null; } |
190 | |
191 | static std::string name() { return "BM_Invoke" + FunctionType::name(); } |
192 | }; |
193 | |
194 | template <class FunctionType> |
195 | struct InvokeInlined { |
196 | static void run(benchmark::State& state) { |
197 | S s; |
198 | for (auto _ : state) { |
199 | MakeFunction(FunctionType())(&s); |
200 | } |
201 | } |
202 | |
203 | static bool skip() { return FunctionType() == ::FunctionType::Null; } |
204 | |
205 | static std::string name() { return "BM_InvokeInlined" + FunctionType::name(); } |
206 | }; |
207 | |
208 | } // namespace |
209 | |
210 | int main(int argc, char** argv) { |
211 | benchmark::Initialize(argc: &argc, argv); |
212 | if (benchmark::ReportUnrecognizedArguments(argc, argv)) |
213 | return 1; |
214 | |
215 | makeCartesianProductBenchmark<ConstructAndDestroy, AllOpacity, AllFunctionTypes>(); |
216 | makeCartesianProductBenchmark<Copy, AllFunctionTypes>(); |
217 | makeCartesianProductBenchmark<Move, AllFunctionTypes>(); |
218 | makeCartesianProductBenchmark<Swap, AllFunctionTypes, AllFunctionTypes>(); |
219 | makeCartesianProductBenchmark<OperatorBool, AllFunctionTypes>(); |
220 | makeCartesianProductBenchmark<Invoke, AllFunctionTypes>(); |
221 | makeCartesianProductBenchmark<InvokeInlined, AllFunctionTypes>(); |
222 | benchmark::RunSpecifiedBenchmarks(); |
223 | } |
224 | |