1 //
2 // Copyright 2020 The Abseil Authors.
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // https://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15
16 #include <stdint.h>
17
18 #include <string>
19 #include <vector>
20
21 #include "absl/flags/flag.h"
22 #include "absl/flags/marshalling.h"
23 #include "absl/flags/parse.h"
24 #include "absl/flags/reflection.h"
25 #include "absl/strings/string_view.h"
26 #include "absl/time/time.h"
27 #include "absl/types/optional.h"
28 #include "benchmark/benchmark.h"
29
30 namespace {
31 using String = std::string;
32 using VectorOfStrings = std::vector<std::string>;
33 using AbslDuration = absl::Duration;
34
35 // We do not want to take over marshalling for the types absl::optional<int>,
36 // absl::optional<std::string> which we do not own. Instead we introduce unique
37 // "aliases" to these types, which we do.
38 using AbslOptionalInt = absl::optional<int>;
39 struct OptionalInt : AbslOptionalInt {
40 using AbslOptionalInt::AbslOptionalInt;
41 };
42 // Next two functions represent Abseil Flags marshalling for OptionalInt.
AbslParseFlag(absl::string_view src,OptionalInt * flag,std::string * error)43 bool AbslParseFlag(absl::string_view src, OptionalInt* flag,
44 std::string* error) {
45 int val;
46 if (src.empty())
47 flag->reset();
48 else if (!absl::ParseFlag(src, &val, error))
49 return false;
50 *flag = val;
51 return true;
52 }
AbslUnparseFlag(const OptionalInt & flag)53 std::string AbslUnparseFlag(const OptionalInt& flag) {
54 return !flag ? "" : absl::UnparseFlag(*flag);
55 }
56
57 using AbslOptionalString = absl::optional<std::string>;
58 struct OptionalString : AbslOptionalString {
59 using AbslOptionalString::AbslOptionalString;
60 };
61 // Next two functions represent Abseil Flags marshalling for OptionalString.
AbslParseFlag(absl::string_view src,OptionalString * flag,std::string * error)62 bool AbslParseFlag(absl::string_view src, OptionalString* flag,
63 std::string* error) {
64 std::string val;
65 if (src.empty())
66 flag->reset();
67 else if (!absl::ParseFlag(src, &val, error))
68 return false;
69 *flag = val;
70 return true;
71 }
AbslUnparseFlag(const OptionalString & flag)72 std::string AbslUnparseFlag(const OptionalString& flag) {
73 return !flag ? "" : absl::UnparseFlag(*flag);
74 }
75
76 struct UDT {
77 UDT() = default;
UDT__anon637744ca0111::UDT78 UDT(const UDT&) {}
operator =__anon637744ca0111::UDT79 UDT& operator=(const UDT&) { return *this; }
80 };
81 // Next two functions represent Abseil Flags marshalling for UDT.
AbslParseFlag(absl::string_view,UDT *,std::string *)82 bool AbslParseFlag(absl::string_view, UDT*, std::string*) { return true; }
AbslUnparseFlag(const UDT &)83 std::string AbslUnparseFlag(const UDT&) { return ""; }
84
85 } // namespace
86
87 #define BENCHMARKED_TYPES(A) \
88 A(bool) \
89 A(int16_t) \
90 A(uint16_t) \
91 A(int32_t) \
92 A(uint32_t) \
93 A(int64_t) \
94 A(uint64_t) \
95 A(double) \
96 A(float) \
97 A(String) \
98 A(VectorOfStrings) \
99 A(OptionalInt) \
100 A(OptionalString) \
101 A(AbslDuration) \
102 A(UDT)
103
104 #define REPLICATE_0(A, T, name, index) A(T, name, index)
105 #define REPLICATE_1(A, T, name, index) \
106 REPLICATE_0(A, T, name, index##0) REPLICATE_0(A, T, name, index##1)
107 #define REPLICATE_2(A, T, name, index) \
108 REPLICATE_1(A, T, name, index##0) REPLICATE_1(A, T, name, index##1)
109 #define REPLICATE_3(A, T, name, index) \
110 REPLICATE_2(A, T, name, index##0) REPLICATE_2(A, T, name, index##1)
111 #define REPLICATE_4(A, T, name, index) \
112 REPLICATE_3(A, T, name, index##0) REPLICATE_3(A, T, name, index##1)
113 #define REPLICATE_5(A, T, name, index) \
114 REPLICATE_4(A, T, name, index##0) REPLICATE_4(A, T, name, index##1)
115 #define REPLICATE_6(A, T, name, index) \
116 REPLICATE_5(A, T, name, index##0) REPLICATE_5(A, T, name, index##1)
117 #define REPLICATE_7(A, T, name, index) \
118 REPLICATE_6(A, T, name, index##0) REPLICATE_6(A, T, name, index##1)
119 #define REPLICATE_8(A, T, name, index) \
120 REPLICATE_7(A, T, name, index##0) REPLICATE_7(A, T, name, index##1)
121 #define REPLICATE_9(A, T, name, index) \
122 REPLICATE_8(A, T, name, index##0) REPLICATE_8(A, T, name, index##1)
123 #if defined(_MSC_VER)
124 #define REPLICATE(A, T, name) \
125 REPLICATE_7(A, T, name, 0) REPLICATE_7(A, T, name, 1)
126 #define SINGLE_FLAG(T) FLAGS_##T##_flag_00000000
127 #else
128 #define REPLICATE(A, T, name) \
129 REPLICATE_9(A, T, name, 0) REPLICATE_9(A, T, name, 1)
130 #define SINGLE_FLAG(T) FLAGS_##T##_flag_0000000000
131 #endif
132 #define REPLICATE_ALL(A, T, name) \
133 REPLICATE_9(A, T, name, 0) REPLICATE_9(A, T, name, 1)
134
135 #define COUNT(T, name, index) +1
136 constexpr size_t kNumFlags = 0 REPLICATE(COUNT, _, _);
137
138 #if defined(__clang__) && defined(__linux__)
139 // Force the flags used for benchmarks into a separate ELF section.
140 // This ensures that, even when other parts of the code might change size,
141 // the layout of the flags across cachelines is kept constant. This makes
142 // benchmark results more reproducible across unrelated code changes.
143 #pragma clang section data = ".benchmark_flags"
144 #endif
145 #define DEFINE_FLAG(T, name, index) ABSL_FLAG(T, name##_##index, {}, "");
146 #define FLAG_DEF(T) REPLICATE(DEFINE_FLAG, T, T##_flag);
147 BENCHMARKED_TYPES(FLAG_DEF)
148 #if defined(__clang__) && defined(__linux__)
149 #pragma clang section data = ""
150 #endif
151 // Register thousands of flags to bloat up the size of the registry.
152 // This mimics real life production binaries.
153 #define BLOAT_FLAG(_unused1, _unused2, index) \
154 ABSL_FLAG(int, bloat_flag_##index, 0, "");
155 REPLICATE_ALL(BLOAT_FLAG, _, _)
156
157 namespace {
158
159 #define FLAG_PTR(T, name, index) &FLAGS_##name##_##index,
160 #define FLAG_PTR_ARR(T) \
161 static constexpr absl::Flag<T>* FlagPtrs_##T[] = { \
162 REPLICATE(FLAG_PTR, T, T##_flag)};
163 BENCHMARKED_TYPES(FLAG_PTR_ARR)
164
165 #define BM_SingleGetFlag(T) \
166 void BM_SingleGetFlag_##T(benchmark::State& state) { \
167 for (auto _ : state) { \
168 benchmark::DoNotOptimize(absl::GetFlag(SINGLE_FLAG(T))); \
169 } \
170 } \
171 BENCHMARK(BM_SingleGetFlag_##T)->ThreadRange(1, 16);
172
173 BENCHMARKED_TYPES(BM_SingleGetFlag)
174
175 template <typename T>
176 struct Accumulator {
177 using type = T;
178 };
179 template <>
180 struct Accumulator<String> {
181 using type = size_t;
182 };
183 template <>
184 struct Accumulator<VectorOfStrings> {
185 using type = size_t;
186 };
187 template <>
188 struct Accumulator<OptionalInt> {
189 using type = bool;
190 };
191 template <>
192 struct Accumulator<OptionalString> {
193 using type = bool;
194 };
195 template <>
196 struct Accumulator<UDT> {
197 using type = bool;
198 };
199
200 template <typename T>
Accumulate(typename Accumulator<T>::type & a,const T & f)201 void Accumulate(typename Accumulator<T>::type& a, const T& f) {
202 a += f;
203 }
Accumulate(bool & a,bool f)204 void Accumulate(bool& a, bool f) { a = a || f; }
Accumulate(size_t & a,const std::string & f)205 void Accumulate(size_t& a, const std::string& f) { a += f.size(); }
Accumulate(size_t & a,const std::vector<std::string> & f)206 void Accumulate(size_t& a, const std::vector<std::string>& f) { a += f.size(); }
Accumulate(bool & a,const OptionalInt & f)207 void Accumulate(bool& a, const OptionalInt& f) { a |= f.has_value(); }
Accumulate(bool & a,const OptionalString & f)208 void Accumulate(bool& a, const OptionalString& f) { a |= f.has_value(); }
Accumulate(bool & a,const UDT & f)209 void Accumulate(bool& a, const UDT& f) {
210 a |= reinterpret_cast<int64_t>(&f) & 0x1;
211 }
212
213 #define BM_ManyGetFlag(T) \
214 void BM_ManyGetFlag_##T(benchmark::State& state) { \
215 Accumulator<T>::type res = {}; \
216 while (state.KeepRunningBatch(kNumFlags)) { \
217 for (auto* flag_ptr : FlagPtrs_##T) { \
218 Accumulate(res, absl::GetFlag(*flag_ptr)); \
219 } \
220 } \
221 benchmark::DoNotOptimize(res); \
222 } \
223 BENCHMARK(BM_ManyGetFlag_##T)->ThreadRange(1, 8);
224
BENCHMARKED_TYPES(BM_ManyGetFlag)225 BENCHMARKED_TYPES(BM_ManyGetFlag)
226
227 void BM_ThreadedFindCommandLineFlag(benchmark::State& state) {
228 char dummy[] = "dummy";
229 char* argv[] = {dummy};
230 // We need to ensure that flags have been parsed. That is where the registry
231 // is finalized.
232 absl::ParseCommandLine(1, argv);
233
234 while (state.KeepRunningBatch(kNumFlags)) {
235 for (auto* flag_ptr : FlagPtrs_bool) {
236 benchmark::DoNotOptimize(absl::FindCommandLineFlag(flag_ptr->Name()));
237 }
238 }
239 }
240 BENCHMARK(BM_ThreadedFindCommandLineFlag)->ThreadRange(1, 16);
241
242 } // namespace
243
244 #define InvokeGetFlag(T) \
245 T AbslInvokeGetFlag##T() { return absl::GetFlag(SINGLE_FLAG(T)); } \
246 int odr##T = (benchmark::DoNotOptimize(AbslInvokeGetFlag##T), 1);
247
248 BENCHMARKED_TYPES(InvokeGetFlag)
249
250 // To veiw disassembly use: gdb ${BINARY} -batch -ex "disassemble /s $FUNC"
251