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
21namespace {
22
23enum class FunctionType {
24 Null,
25 FunctionPointer,
26 MemberFunctionPointer,
27 MemberPointer,
28 SmallTrivialFunctor,
29 SmallNonTrivialFunctor,
30 LargeTrivialFunctor,
31 LargeNonTrivialFunctor
32};
33
34struct 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
46enum class Opacity { kOpaque, kTransparent };
47
48struct AllOpacity : EnumValuesAsTuple<AllOpacity, Opacity, 2> {
49 static constexpr const char* Names[] = {"Opaque", "Transparent"};
50};
51
52struct S {
53 int function() const { return 0; }
54 int field = 0;
55};
56
57int FunctionWithS(const S*) { return 0; }
58
59struct SmallTrivialFunctor {
60 int operator()(const S*) const { return 0; }
61};
62struct SmallNonTrivialFunctor {
63 SmallNonTrivialFunctor() {}
64 SmallNonTrivialFunctor(const SmallNonTrivialFunctor&) {}
65 ~SmallNonTrivialFunctor() {}
66 int operator()(const S*) const { return 0; }
67};
68struct 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};
75struct 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
85using Function = std::function<int(const S*)>;
86
87TEST_ALWAYS_INLINE
88inline 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
110template <class Opacity, class FunctionType>
111struct 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
125template <class FunctionType>
126struct 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
139template <class FunctionType>
140struct 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
154template <class Function1, class Function2>
155struct 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
169template <class FunctionType>
170struct 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
182template <class FunctionType>
183struct 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
198template <class FunctionType>
199struct 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
214int 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

source code of libcxx/test/benchmarks/function.bench.cpp