flag_benchmark.cc (8799B)
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. 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 } 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. 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 } 72 std::string AbslUnparseFlag(const OptionalString& flag) { 73 return !flag ? "" : absl::UnparseFlag(*flag); 74 } 75 76 struct UDT { 77 UDT() = default; 78 UDT(const UDT&) {} 79 UDT& operator=(const UDT&) { return *this; } 80 }; 81 // Next two functions represent Abseil Flags marshalling for UDT. 82 bool AbslParseFlag(absl::string_view, UDT*, std::string*) { return true; } 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> 201 void Accumulate(typename Accumulator<T>::type& a, const T& f) { 202 a += f; 203 } 204 void Accumulate(bool& a, bool f) { a = a || f; } 205 void Accumulate(size_t& a, const std::string& f) { a += f.size(); } 206 void Accumulate(size_t& a, const std::vector<std::string>& f) { a += f.size(); } 207 void Accumulate(bool& a, const OptionalInt& f) { a |= f.has_value(); } 208 void Accumulate(bool& a, const OptionalString& f) { a |= f.has_value(); } 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 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__