str_cat_benchmark.cc (8626B)
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 <array> 16 #include <cstdint> 17 #include <cstdio> 18 #include <cstdlib> 19 #include <cstring> 20 #include <string> 21 #include <tuple> 22 #include <utility> 23 24 #include "absl/random/log_uniform_int_distribution.h" 25 #include "absl/random/random.h" 26 #include "absl/strings/str_cat.h" 27 #include "absl/strings/str_format.h" 28 #include "absl/strings/string_view.h" 29 #include "absl/strings/substitute.h" 30 #include "benchmark/benchmark.h" 31 32 namespace { 33 34 const char kStringOne[] = "Once Upon A Time, "; 35 const char kStringTwo[] = "There was a string benchmark"; 36 37 // We want to include negative numbers in the benchmark, so this function 38 // is used to count 0, 1, -1, 2, -2, 3, -3, ... 39 inline int IncrementAlternatingSign(int i) { 40 return i > 0 ? -i : 1 - i; 41 } 42 43 void BM_Sum_By_StrCat(benchmark::State& state) { 44 int i = 0; 45 char foo[100]; 46 for (auto _ : state) { 47 // NOLINTNEXTLINE(runtime/printf) 48 strcpy(foo, absl::StrCat(kStringOne, i, kStringTwo, i * 65536ULL).c_str()); 49 int sum = 0; 50 for (char* f = &foo[0]; *f != 0; ++f) { 51 sum += *f; 52 } 53 benchmark::DoNotOptimize(sum); 54 i = IncrementAlternatingSign(i); 55 } 56 } 57 BENCHMARK(BM_Sum_By_StrCat); 58 59 void BM_StrCat_By_snprintf(benchmark::State& state) { 60 int i = 0; 61 char on_stack[1000]; 62 for (auto _ : state) { 63 snprintf(on_stack, sizeof(on_stack), "%s %s:%d", kStringOne, kStringTwo, i); 64 i = IncrementAlternatingSign(i); 65 } 66 } 67 BENCHMARK(BM_StrCat_By_snprintf); 68 69 void BM_StrCat_By_Strings(benchmark::State& state) { 70 int i = 0; 71 for (auto _ : state) { 72 std::string result = 73 std::string(kStringOne) + " " + kStringTwo + ":" + absl::StrCat(i); 74 benchmark::DoNotOptimize(result); 75 i = IncrementAlternatingSign(i); 76 } 77 } 78 BENCHMARK(BM_StrCat_By_Strings); 79 80 void BM_StrCat_By_StringOpPlus(benchmark::State& state) { 81 int i = 0; 82 for (auto _ : state) { 83 std::string result = kStringOne; 84 result += " "; 85 result += kStringTwo; 86 result += ":"; 87 result += absl::StrCat(i); 88 benchmark::DoNotOptimize(result); 89 i = IncrementAlternatingSign(i); 90 } 91 } 92 BENCHMARK(BM_StrCat_By_StringOpPlus); 93 94 void BM_StrCat_By_StrCat(benchmark::State& state) { 95 int i = 0; 96 for (auto _ : state) { 97 std::string result = absl::StrCat(kStringOne, " ", kStringTwo, ":", i); 98 benchmark::DoNotOptimize(result); 99 i = IncrementAlternatingSign(i); 100 } 101 } 102 BENCHMARK(BM_StrCat_By_StrCat); 103 104 void BM_HexCat_By_StrCat(benchmark::State& state) { 105 int i = 0; 106 for (auto _ : state) { 107 std::string result = 108 absl::StrCat(kStringOne, " ", absl::Hex(int64_t{i} + 0x10000000)); 109 benchmark::DoNotOptimize(result); 110 i = IncrementAlternatingSign(i); 111 } 112 } 113 BENCHMARK(BM_HexCat_By_StrCat); 114 115 void BM_HexCat_By_StrFormat(benchmark::State& state) { 116 int i = 0; 117 for (auto _ : state) { 118 std::string result = 119 absl::StrFormat("%s %x", kStringOne, int64_t{i} + 0x10000000); 120 benchmark::DoNotOptimize(result); 121 i = IncrementAlternatingSign(i); 122 } 123 } 124 BENCHMARK(BM_HexCat_By_StrFormat); 125 126 void BM_HexCat_By_Substitute(benchmark::State& state) { 127 int i = 0; 128 for (auto _ : state) { 129 std::string result = absl::Substitute( 130 "$0 $1", kStringOne, reinterpret_cast<void*>(int64_t{i} + 0x10000000)); 131 benchmark::DoNotOptimize(result); 132 i = IncrementAlternatingSign(i); 133 } 134 } 135 BENCHMARK(BM_HexCat_By_Substitute); 136 137 void BM_FloatToString_By_StrCat(benchmark::State& state) { 138 int i = 0; 139 float foo = 0.0f; 140 for (auto _ : state) { 141 std::string result = absl::StrCat(foo += 1.001f, " != ", int64_t{i}); 142 benchmark::DoNotOptimize(result); 143 i = IncrementAlternatingSign(i); 144 } 145 } 146 BENCHMARK(BM_FloatToString_By_StrCat); 147 148 void BM_DoubleToString_By_SixDigits(benchmark::State& state) { 149 int i = 0; 150 double foo = 0.0; 151 for (auto _ : state) { 152 std::string result = 153 absl::StrCat(absl::SixDigits(foo += 1.001), " != ", int64_t{i}); 154 benchmark::DoNotOptimize(result); 155 i = IncrementAlternatingSign(i); 156 } 157 } 158 BENCHMARK(BM_DoubleToString_By_SixDigits); 159 160 void BM_FloatToString_By_StrFormat(benchmark::State& state) { 161 int i = 0; 162 float foo = 0.0f; 163 for (auto _ : state) { 164 std::string result = 165 absl::StrFormat("%f != %lld", foo += 1.001f, int64_t{i}); 166 benchmark::DoNotOptimize(result); 167 i = IncrementAlternatingSign(i); 168 } 169 } 170 BENCHMARK(BM_FloatToString_By_StrFormat); 171 172 template <typename Table, size_t... Index> 173 void BM_StrAppendImpl(benchmark::State& state, Table table, size_t total_bytes, 174 std::index_sequence<Index...>) { 175 for (auto s : state) { 176 const size_t table_size = table.size(); 177 size_t i = 0; 178 std::string result; 179 while (result.size() < total_bytes) { 180 absl::StrAppend(&result, std::get<Index>(table[i])...); 181 benchmark::DoNotOptimize(result); 182 ++i; 183 i -= i >= table_size ? table_size : 0; 184 } 185 } 186 } 187 188 template <typename Array> 189 void BM_StrAppend(benchmark::State& state, Array&& table) { 190 const size_t total_bytes = state.range(0); 191 const int chunks_at_a_time = state.range(1); 192 193 switch (chunks_at_a_time) { 194 case 1: 195 return BM_StrAppendImpl(state, std::forward<Array>(table), total_bytes, 196 std::make_index_sequence<1>()); 197 case 2: 198 return BM_StrAppendImpl(state, std::forward<Array>(table), total_bytes, 199 std::make_index_sequence<2>()); 200 case 4: 201 return BM_StrAppendImpl(state, std::forward<Array>(table), total_bytes, 202 std::make_index_sequence<4>()); 203 case 8: 204 return BM_StrAppendImpl(state, std::forward<Array>(table), total_bytes, 205 std::make_index_sequence<8>()); 206 default: 207 std::abort(); 208 } 209 } 210 211 void BM_StrAppendStr(benchmark::State& state) { 212 using T = absl::string_view; 213 using Row = std::tuple<T, T, T, T, T, T, T, T>; 214 constexpr absl::string_view kChunk = "0123456789"; 215 Row row = {kChunk, kChunk, kChunk, kChunk, kChunk, kChunk, kChunk, kChunk}; 216 return BM_StrAppend(state, std::array<Row, 1>({row})); 217 } 218 219 template <typename T> 220 void BM_StrAppendInt(benchmark::State& state) { 221 absl::BitGen rng; 222 absl::log_uniform_int_distribution<T> dist; 223 std::array<std::tuple<T, T, T, T, T, T, T, T>, (1 << 7)> table; 224 for (size_t i = 0; i < table.size(); ++i) { 225 table[i] = {dist(rng), dist(rng), dist(rng), dist(rng), 226 dist(rng), dist(rng), dist(rng), dist(rng)}; 227 } 228 return BM_StrAppend(state, table); 229 } 230 231 template <typename B> 232 void StrAppendConfig(B* benchmark) { 233 for (int bytes : {10, 100, 1000, 10000}) { 234 for (int chunks : {1, 2, 4, 8}) { 235 // Only add the ones that divide properly. Otherwise we are over counting. 236 if (bytes % (10 * chunks) == 0) { 237 benchmark->Args({bytes, chunks}); 238 } 239 } 240 } 241 } 242 243 BENCHMARK(BM_StrAppendStr)->Apply(StrAppendConfig); 244 BENCHMARK(BM_StrAppendInt<int64_t>)->Apply(StrAppendConfig); 245 BENCHMARK(BM_StrAppendInt<uint64_t>)->Apply(StrAppendConfig); 246 BENCHMARK(BM_StrAppendInt<int32_t>)->Apply(StrAppendConfig); 247 BENCHMARK(BM_StrAppendInt<uint32_t>)->Apply(StrAppendConfig); 248 249 template <typename... Chunks> 250 void BM_StrCatImpl(benchmark::State& state, 251 Chunks... chunks) { 252 for (auto s : state) { 253 std::string result = absl::StrCat(chunks...); 254 benchmark::DoNotOptimize(result); 255 } 256 } 257 258 void BM_StrCat(benchmark::State& state) { 259 const int chunks_at_a_time = state.range(0); 260 const absl::string_view kChunk = "0123456789"; 261 262 switch (chunks_at_a_time) { 263 case 1: 264 return BM_StrCatImpl(state, kChunk); 265 case 2: 266 return BM_StrCatImpl(state, kChunk, kChunk); 267 case 3: 268 return BM_StrCatImpl(state, kChunk, kChunk, kChunk); 269 case 4: 270 return BM_StrCatImpl(state, kChunk, kChunk, kChunk, kChunk); 271 default: 272 std::abort(); 273 } 274 } 275 276 BENCHMARK(BM_StrCat)->Arg(1)->Arg(2)->Arg(3)->Arg(4); 277 278 void BM_StrCat_int(benchmark::State& state) { 279 int i = 0; 280 for (auto s : state) { 281 std::string result = absl::StrCat(i); 282 benchmark::DoNotOptimize(result); 283 i = IncrementAlternatingSign(i); 284 } 285 } 286 287 BENCHMARK(BM_StrCat_int); 288 289 } // namespace