numbers_benchmark.cc (8558B)
1 // Copyright 2018 The Abseil Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #include <cstdint> 16 #include <limits> 17 #include <random> 18 #include <string> 19 #include <type_traits> 20 #include <vector> 21 22 #include "absl/base/internal/raw_logging.h" 23 #include "absl/random/distributions.h" 24 #include "absl/random/random.h" 25 #include "absl/strings/numbers.h" 26 #include "absl/strings/string_view.h" 27 #include "benchmark/benchmark.h" 28 29 namespace { 30 31 template <typename T> 32 void BM_FastIntToBuffer(benchmark::State& state) { 33 const int inc = state.range(0); 34 char buf[absl::numbers_internal::kFastToBufferSize]; 35 // Use the unsigned type to increment to take advantage of well-defined 36 // modular arithmetic. 37 typename std::make_unsigned<T>::type x = 0; 38 for (auto _ : state) { 39 absl::numbers_internal::FastIntToBuffer(static_cast<T>(x), buf); 40 x += inc; 41 } 42 } 43 BENCHMARK_TEMPLATE(BM_FastIntToBuffer, int32_t)->Range(0, 1 << 15); 44 BENCHMARK_TEMPLATE(BM_FastIntToBuffer, int64_t)->Range(0, 1 << 30); 45 46 // Creates an integer that would be printed as `num_digits` repeated 7s in the 47 // given `base`. `base` must be greater than or equal to 8. 48 int64_t RepeatedSevens(int num_digits, int base) { 49 ABSL_RAW_CHECK(base >= 8, ""); 50 int64_t num = 7; 51 while (--num_digits) num = base * num + 7; 52 return num; 53 } 54 55 void BM_safe_strto32_string(benchmark::State& state) { 56 const int digits = state.range(0); 57 const int base = state.range(1); 58 std::string str(digits, '7'); // valid in octal, decimal and hex 59 int32_t value = 0; 60 for (auto _ : state) { 61 benchmark::DoNotOptimize( 62 absl::numbers_internal::safe_strto32_base(str, &value, base)); 63 } 64 ABSL_RAW_CHECK(value == RepeatedSevens(digits, base), ""); 65 } 66 BENCHMARK(BM_safe_strto32_string) 67 ->ArgPair(1, 8) 68 ->ArgPair(1, 10) 69 ->ArgPair(1, 16) 70 ->ArgPair(2, 8) 71 ->ArgPair(2, 10) 72 ->ArgPair(2, 16) 73 ->ArgPair(4, 8) 74 ->ArgPair(4, 10) 75 ->ArgPair(4, 16) 76 ->ArgPair(8, 8) 77 ->ArgPair(8, 10) 78 ->ArgPair(8, 16) 79 ->ArgPair(10, 8) 80 ->ArgPair(9, 10); 81 82 void BM_safe_strto64_string(benchmark::State& state) { 83 const int digits = state.range(0); 84 const int base = state.range(1); 85 std::string str(digits, '7'); // valid in octal, decimal and hex 86 int64_t value = 0; 87 for (auto _ : state) { 88 benchmark::DoNotOptimize( 89 absl::numbers_internal::safe_strto64_base(str, &value, base)); 90 } 91 ABSL_RAW_CHECK(value == RepeatedSevens(digits, base), ""); 92 } 93 BENCHMARK(BM_safe_strto64_string) 94 ->ArgPair(1, 8) 95 ->ArgPair(1, 10) 96 ->ArgPair(1, 16) 97 ->ArgPair(2, 8) 98 ->ArgPair(2, 10) 99 ->ArgPair(2, 16) 100 ->ArgPair(4, 8) 101 ->ArgPair(4, 10) 102 ->ArgPair(4, 16) 103 ->ArgPair(8, 8) 104 ->ArgPair(8, 10) 105 ->ArgPair(8, 16) 106 ->ArgPair(16, 8) 107 ->ArgPair(16, 10) 108 ->ArgPair(16, 16); 109 110 void BM_safe_strtou32_string(benchmark::State& state) { 111 const int digits = state.range(0); 112 const int base = state.range(1); 113 std::string str(digits, '7'); // valid in octal, decimal and hex 114 uint32_t value = 0; 115 for (auto _ : state) { 116 benchmark::DoNotOptimize( 117 absl::numbers_internal::safe_strtou32_base(str, &value, base)); 118 } 119 ABSL_RAW_CHECK(value == RepeatedSevens(digits, base), ""); 120 } 121 BENCHMARK(BM_safe_strtou32_string) 122 ->ArgPair(1, 8) 123 ->ArgPair(1, 10) 124 ->ArgPair(1, 16) 125 ->ArgPair(2, 8) 126 ->ArgPair(2, 10) 127 ->ArgPair(2, 16) 128 ->ArgPair(4, 8) 129 ->ArgPair(4, 10) 130 ->ArgPair(4, 16) 131 ->ArgPair(8, 8) 132 ->ArgPair(8, 10) 133 ->ArgPair(8, 16) 134 ->ArgPair(10, 8) 135 ->ArgPair(9, 10); 136 137 void BM_safe_strtou64_string(benchmark::State& state) { 138 const int digits = state.range(0); 139 const int base = state.range(1); 140 std::string str(digits, '7'); // valid in octal, decimal and hex 141 uint64_t value = 0; 142 for (auto _ : state) { 143 benchmark::DoNotOptimize( 144 absl::numbers_internal::safe_strtou64_base(str, &value, base)); 145 } 146 ABSL_RAW_CHECK(value == RepeatedSevens(digits, base), ""); 147 } 148 BENCHMARK(BM_safe_strtou64_string) 149 ->ArgPair(1, 8) 150 ->ArgPair(1, 10) 151 ->ArgPair(1, 16) 152 ->ArgPair(2, 8) 153 ->ArgPair(2, 10) 154 ->ArgPair(2, 16) 155 ->ArgPair(4, 8) 156 ->ArgPair(4, 10) 157 ->ArgPair(4, 16) 158 ->ArgPair(8, 8) 159 ->ArgPair(8, 10) 160 ->ArgPair(8, 16) 161 ->ArgPair(16, 8) 162 ->ArgPair(16, 10) 163 ->ArgPair(16, 16); 164 165 // Returns a vector of `num_strings` strings. Each string represents a 166 // floating point number with `num_digits` digits before the decimal point and 167 // another `num_digits` digits after. 168 std::vector<std::string> MakeFloatStrings(int num_strings, int num_digits) { 169 // For convenience, use a random number generator to generate the test data. 170 // We don't actually need random properties, so use a fixed seed. 171 std::minstd_rand0 rng(1); 172 std::uniform_int_distribution<int> random_digit('0', '9'); 173 174 std::vector<std::string> float_strings(num_strings); 175 for (std::string& s : float_strings) { 176 s.reserve(2 * num_digits + 1); 177 for (int i = 0; i < num_digits; ++i) { 178 s.push_back(static_cast<char>(random_digit(rng))); 179 } 180 s.push_back('.'); 181 for (int i = 0; i < num_digits; ++i) { 182 s.push_back(static_cast<char>(random_digit(rng))); 183 } 184 } 185 return float_strings; 186 } 187 188 template <typename StringType> 189 StringType GetStringAs(const std::string& s) { 190 return static_cast<StringType>(s); 191 } 192 template <> 193 const char* GetStringAs<const char*>(const std::string& s) { 194 return s.c_str(); 195 } 196 197 template <typename StringType> 198 std::vector<StringType> GetStringsAs(const std::vector<std::string>& strings) { 199 std::vector<StringType> result; 200 result.reserve(strings.size()); 201 for (const std::string& s : strings) { 202 result.push_back(GetStringAs<StringType>(s)); 203 } 204 return result; 205 } 206 207 template <typename T> 208 void BM_SimpleAtof(benchmark::State& state) { 209 const int num_strings = state.range(0); 210 const int num_digits = state.range(1); 211 std::vector<std::string> backing_strings = 212 MakeFloatStrings(num_strings, num_digits); 213 std::vector<T> inputs = GetStringsAs<T>(backing_strings); 214 float value; 215 for (auto _ : state) { 216 for (const T& input : inputs) { 217 benchmark::DoNotOptimize(absl::SimpleAtof(input, &value)); 218 } 219 } 220 } 221 BENCHMARK_TEMPLATE(BM_SimpleAtof, absl::string_view) 222 ->ArgPair(10, 1) 223 ->ArgPair(10, 2) 224 ->ArgPair(10, 4) 225 ->ArgPair(10, 8); 226 BENCHMARK_TEMPLATE(BM_SimpleAtof, const char*) 227 ->ArgPair(10, 1) 228 ->ArgPair(10, 2) 229 ->ArgPair(10, 4) 230 ->ArgPair(10, 8); 231 BENCHMARK_TEMPLATE(BM_SimpleAtof, std::string) 232 ->ArgPair(10, 1) 233 ->ArgPair(10, 2) 234 ->ArgPair(10, 4) 235 ->ArgPair(10, 8); 236 237 template <typename T> 238 void BM_SimpleAtod(benchmark::State& state) { 239 const int num_strings = state.range(0); 240 const int num_digits = state.range(1); 241 std::vector<std::string> backing_strings = 242 MakeFloatStrings(num_strings, num_digits); 243 std::vector<T> inputs = GetStringsAs<T>(backing_strings); 244 double value; 245 for (auto _ : state) { 246 for (const T& input : inputs) { 247 benchmark::DoNotOptimize(absl::SimpleAtod(input, &value)); 248 } 249 } 250 } 251 BENCHMARK_TEMPLATE(BM_SimpleAtod, absl::string_view) 252 ->ArgPair(10, 1) 253 ->ArgPair(10, 2) 254 ->ArgPair(10, 4) 255 ->ArgPair(10, 8); 256 BENCHMARK_TEMPLATE(BM_SimpleAtod, const char*) 257 ->ArgPair(10, 1) 258 ->ArgPair(10, 2) 259 ->ArgPair(10, 4) 260 ->ArgPair(10, 8); 261 BENCHMARK_TEMPLATE(BM_SimpleAtod, std::string) 262 ->ArgPair(10, 1) 263 ->ArgPair(10, 2) 264 ->ArgPair(10, 4) 265 ->ArgPair(10, 8); 266 267 void BM_FastHexToBufferZeroPad16(benchmark::State& state) { 268 absl::BitGen rng; 269 std::vector<uint64_t> nums; 270 nums.resize(1000); 271 auto min = std::numeric_limits<uint64_t>::min(); 272 auto max = std::numeric_limits<uint64_t>::max(); 273 for (auto& num : nums) { 274 num = absl::LogUniform(rng, min, max); 275 } 276 277 char buf[16]; 278 while (state.KeepRunningBatch(nums.size())) { 279 for (auto num : nums) { 280 auto digits = absl::numbers_internal::FastHexToBufferZeroPad16(num, buf); 281 benchmark::DoNotOptimize(digits); 282 benchmark::DoNotOptimize(buf); 283 } 284 } 285 } 286 BENCHMARK(BM_FastHexToBufferZeroPad16); 287 288 } // namespace