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