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 {
function__anon0a7a1c970111::S53 int function() const { return 0; }
54 int field = 0;
55 };
56
FunctionWithS(const S *)57 int FunctionWithS(const S*) { return 0; }
58
59 struct SmallTrivialFunctor {
operator ()__anon0a7a1c970111::SmallTrivialFunctor60 int operator()(const S*) const { return 0; }
61 };
62 struct SmallNonTrivialFunctor {
SmallNonTrivialFunctor__anon0a7a1c970111::SmallNonTrivialFunctor63 SmallNonTrivialFunctor() {}
SmallNonTrivialFunctor__anon0a7a1c970111::SmallNonTrivialFunctor64 SmallNonTrivialFunctor(const SmallNonTrivialFunctor&) {}
~SmallNonTrivialFunctor__anon0a7a1c970111::SmallNonTrivialFunctor65 ~SmallNonTrivialFunctor() {}
operator ()__anon0a7a1c970111::SmallNonTrivialFunctor66 int operator()(const S*) const { return 0; }
67 };
68 struct LargeTrivialFunctor {
LargeTrivialFunctor__anon0a7a1c970111::LargeTrivialFunctor69 LargeTrivialFunctor() {
70 // Do not spend time initializing the padding.
71 }
72 int padding[16];
operator ()__anon0a7a1c970111::LargeTrivialFunctor73 int operator()(const S*) const { return 0; }
74 };
75 struct LargeNonTrivialFunctor {
76 int padding[16];
LargeNonTrivialFunctor__anon0a7a1c970111::LargeNonTrivialFunctor77 LargeNonTrivialFunctor() {
78 // Do not spend time initializing the padding.
79 }
LargeNonTrivialFunctor__anon0a7a1c970111::LargeNonTrivialFunctor80 LargeNonTrivialFunctor(const LargeNonTrivialFunctor&) {}
~LargeNonTrivialFunctor__anon0a7a1c970111::LargeNonTrivialFunctor81 ~LargeNonTrivialFunctor() {}
operator ()__anon0a7a1c970111::LargeNonTrivialFunctor82 int operator()(const S*) const { return 0; }
83 };
84
85 using Function = std::function<int(const S*)>;
86
87 TEST_ALWAYS_INLINE
MakeFunction(FunctionType type,bool opaque=false)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 {
run__anon0a7a1c970111::ConstructAndDestroy112 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
name__anon0a7a1c970111::ConstructAndDestroy122 static std::string name() { return "BM_ConstructAndDestroy" + FunctionType::name() + Opacity::name(); }
123 };
124
125 template <class FunctionType>
126 struct Copy {
run__anon0a7a1c970111::Copy127 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
name__anon0a7a1c970111::Copy136 static std::string name() { return "BM_Copy" + FunctionType::name(); }
137 };
138
139 template <class FunctionType>
140 struct Move {
run__anon0a7a1c970111::Move141 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
name__anon0a7a1c970111::Move151 static std::string name() { return "BM_Move" + FunctionType::name(); }
152 };
153
154 template <class Function1, class Function2>
155 struct Swap {
run__anon0a7a1c970111::Swap156 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
skip__anon0a7a1c970111::Swap164 static bool skip() { return Function1() > Function2(); }
165
name__anon0a7a1c970111::Swap166 static std::string name() { return "BM_Swap" + Function1::name() + Function2::name(); }
167 };
168
169 template <class FunctionType>
170 struct OperatorBool {
run__anon0a7a1c970111::OperatorBool171 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
name__anon0a7a1c970111::OperatorBool179 static std::string name() { return "BM_OperatorBool" + FunctionType::name(); }
180 };
181
182 template <class FunctionType>
183 struct Invoke {
run__anon0a7a1c970111::Invoke184 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
skip__anon0a7a1c970111::Invoke193 static bool skip() { return FunctionType() == ::FunctionType::Null; }
194
name__anon0a7a1c970111::Invoke195 static std::string name() { return "BM_Invoke" + FunctionType::name(); }
196 };
197
198 template <class FunctionType>
199 struct InvokeInlined {
run__anon0a7a1c970111::InvokeInlined200 static void run(benchmark::State& state) {
201 S s;
202 for (auto _ : state) {
203 MakeFunction(FunctionType())(&s);
204 }
205 }
206
skip__anon0a7a1c970111::InvokeInlined207 static bool skip() { return FunctionType() == ::FunctionType::Null; }
208
name__anon0a7a1c970111::InvokeInlined209 static std::string name() { return "BM_InvokeInlined" + FunctionType::name(); }
210 };
211
212 } // namespace
213
main(int argc,char ** argv)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