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