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 } 232