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