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 {
function__anonf26fb4e70111::S49   int function() const { return 0; }
50   int field = 0;
51 };
52 
FunctionWithS(const S *)53 int FunctionWithS(const S*) { return 0; }
54 
55 struct SmallTrivialFunctor {
operator ()__anonf26fb4e70111::SmallTrivialFunctor56   int operator()(const S*) const { return 0; }
57 };
58 struct SmallNonTrivialFunctor {
SmallNonTrivialFunctor__anonf26fb4e70111::SmallNonTrivialFunctor59   SmallNonTrivialFunctor() {}
SmallNonTrivialFunctor__anonf26fb4e70111::SmallNonTrivialFunctor60   SmallNonTrivialFunctor(const SmallNonTrivialFunctor&) {}
~SmallNonTrivialFunctor__anonf26fb4e70111::SmallNonTrivialFunctor61   ~SmallNonTrivialFunctor() {}
operator ()__anonf26fb4e70111::SmallNonTrivialFunctor62   int operator()(const S*) const { return 0; }
63 };
64 struct LargeTrivialFunctor {
LargeTrivialFunctor__anonf26fb4e70111::LargeTrivialFunctor65   LargeTrivialFunctor() {
66       // Do not spend time initializing the padding.
67   }
68   int padding[16];
operator ()__anonf26fb4e70111::LargeTrivialFunctor69   int operator()(const S*) const { return 0; }
70 };
71 struct LargeNonTrivialFunctor {
72   int padding[16];
LargeNonTrivialFunctor__anonf26fb4e70111::LargeNonTrivialFunctor73   LargeNonTrivialFunctor() {
74       // Do not spend time initializing the padding.
75   }
LargeNonTrivialFunctor__anonf26fb4e70111::LargeNonTrivialFunctor76   LargeNonTrivialFunctor(const LargeNonTrivialFunctor&) {}
~LargeNonTrivialFunctor__anonf26fb4e70111::LargeNonTrivialFunctor77   ~LargeNonTrivialFunctor() {}
operator ()__anonf26fb4e70111::LargeNonTrivialFunctor78   int operator()(const S*) const { return 0; }
79 };
80 
81 using Function = std::function<int(const S*)>;
82 
83 TEST_ALWAYS_INLINE
MakeFunction(FunctionType type,bool opaque=false)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 {
run__anonf26fb4e70111::ConstructAndDestroy107   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 
name__anonf26fb4e70111::ConstructAndDestroy117   static std::string name() {
118     return "BM_ConstructAndDestroy" + FunctionType::name() + Opacity::name();
119   }
120 };
121 
122 template <class FunctionType>
123 struct Copy {
run__anonf26fb4e70111::Copy124   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 
name__anonf26fb4e70111::Copy133   static std::string name() { return "BM_Copy" + FunctionType::name(); }
134 };
135 
136 template <class FunctionType>
137 struct Move {
run__anonf26fb4e70111::Move138   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 
name__anonf26fb4e70111::Move148   static std::string name() {
149     return "BM_Move" + FunctionType::name();
150   }
151 };
152 
153 template <class Function1, class Function2>
154 struct Swap {
run__anonf26fb4e70111::Swap155   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 
skip__anonf26fb4e70111::Swap163   static bool skip() { return Function1() > Function2(); }
164 
name__anonf26fb4e70111::Swap165   static std::string name() {
166     return "BM_Swap" + Function1::name() + Function2::name();
167   }
168 };
169 
170 template <class FunctionType>
171 struct OperatorBool {
run__anonf26fb4e70111::OperatorBool172   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 
name__anonf26fb4e70111::OperatorBool180   static std::string name() { return "BM_OperatorBool" + FunctionType::name(); }
181 };
182 
183 template <class FunctionType>
184 struct Invoke {
run__anonf26fb4e70111::Invoke185   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 
skip__anonf26fb4e70111::Invoke194   static bool skip() { return FunctionType() == ::FunctionType::Null; }
195 
name__anonf26fb4e70111::Invoke196   static std::string name() { return "BM_Invoke" + FunctionType::name(); }
197 };
198 
199 template <class FunctionType>
200 struct InvokeInlined {
run__anonf26fb4e70111::InvokeInlined201   static void run(benchmark::State& state) {
202     S s;
203     for (auto _ : state) {
204       MakeFunction(FunctionType())(&s);
205     }
206   }
207 
skip__anonf26fb4e70111::InvokeInlined208   static bool skip() { return FunctionType() == ::FunctionType::Null; }
209 
name__anonf26fb4e70111::InvokeInlined210   static std::string name() {
211     return "BM_InvokeInlined" + FunctionType::name();
212   }
213 };
214 
215 }  // namespace
216 
main(int argc,char ** argv)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 }
232