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