xref: /aosp_15_r20/external/cronet/third_party/abseil-cpp/absl/flags/flag_benchmark.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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__anon6b4d5ece0111::UDT78   UDT(const UDT&) {}
operator =__anon6b4d5ece0111::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 #ifdef __llvm__
245 // To view disassembly use: gdb ${BINARY}  -batch -ex "disassemble /s $FUNC"
246 #define InvokeGetFlag(T)                                             \
247   T AbslInvokeGetFlag##T() { return absl::GetFlag(SINGLE_FLAG(T)); } \
248   int odr##T = (benchmark::DoNotOptimize(AbslInvokeGetFlag##T), 1);
249 
250 BENCHMARKED_TYPES(InvokeGetFlag)
251 #endif  // __llvm__
252