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
18namespace {
19
20enum class FunctionType {
21 Null,
22 FunctionPointer,
23 MemberFunctionPointer,
24 MemberPointer,
25 SmallTrivialFunctor,
26 SmallNonTrivialFunctor,
27 LargeTrivialFunctor,
28 LargeNonTrivialFunctor
29};
30
31struct 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
43enum class Opacity { kOpaque, kTransparent };
44
45struct AllOpacity : EnumValuesAsTuple<AllOpacity, Opacity, 2> {
46 static constexpr const char* Names[] = {"Opaque", "Transparent"};
47};
48
49struct S {
50 int function() const { return 0; }
51 int field = 0;
52};
53
54int FunctionWithS(const S*) { return 0; }
55
56struct SmallTrivialFunctor {
57 int operator()(const S*) const { return 0; }
58};
59struct SmallNonTrivialFunctor {
60 SmallNonTrivialFunctor() {}
61 SmallNonTrivialFunctor(const SmallNonTrivialFunctor&) {}
62 ~SmallNonTrivialFunctor() {}
63 int operator()(const S*) const { return 0; }
64};
65struct 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};
72struct 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
82using Function = std::function<int(const S*)>;
83
84TEST_ALWAYS_INLINE
85inline 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
106template <class Opacity, class FunctionType>
107struct 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
121template <class FunctionType>
122struct 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
135template <class FunctionType>
136struct 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
150template <class Function1, class Function2>
151struct 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
165template <class FunctionType>
166struct 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
178template <class FunctionType>
179struct 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
194template <class FunctionType>
195struct 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
210int main(int argc, char** argv) {
211 benchmark::Initialize(&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