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