2 * Copyright 2017 Facebook, Inc.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <glog/logging.h>
21 #include <folly/Benchmark.h>
22 #include <folly/String.h>
23 #include <folly/container/Foreach.h>
24 #include <folly/gen/Base.h>
25 #include <folly/gen/String.h>
27 using namespace folly;
28 using namespace folly::gen;
36 static std::atomic<int> testSize(1000);
37 static vector<fbstring> testStrVector
38 = seq(1, testSize.load())
41 static auto testFileContent = from(testStrVector) | unsplit('\n');
43 const char* const kLine = "The quick brown fox jumped over the lazy dog.\n";
44 const size_t kLineCount = 10000;
46 const size_t kSmallLineSize = 17;
47 std::vector<std::string> smallLines;
49 void initStringResplitterBenchmark() {
50 bigLines.reserve(kLineCount * strlen(kLine));
51 for (size_t i = 0; i < kLineCount; ++i) {
54 size_t remaining = bigLines.size();
57 size_t n = std::min(kSmallLineSize, remaining);
58 smallLines.push_back(bigLines.substr(pos, n));
64 size_t len(folly::StringPiece s) { return s.size(); }
68 BENCHMARK(StringResplitter_Big, iters) {
71 s += from({bigLines}) | resplit('\n') | map(&len) | sum;
73 folly::doNotOptimizeAway(s);
76 BENCHMARK_RELATIVE(StringResplitter_Small, iters) {
79 s += from(smallLines) | resplit('\n') | map(&len) | sum;
81 folly::doNotOptimizeAway(s);
86 BENCHMARK(StringSplit_Old, iters) {
88 std::string line(kLine);
90 std::vector<StringPiece> parts;
91 split(' ', line, parts);
94 folly::doNotOptimizeAway(s);
98 BENCHMARK_RELATIVE(StringSplit_Gen_Vector, iters) {
100 StringPiece line(kLine);
102 s += (split(line, ' ') | as<vector>()).size();
104 folly::doNotOptimizeAway(s);
107 BENCHMARK_DRAW_LINE()
109 BENCHMARK(StringSplit_Old_ReuseVector, iters) {
111 std::string line(kLine);
112 std::vector<StringPiece> parts;
115 split(' ', line, parts);
118 folly::doNotOptimizeAway(s);
121 BENCHMARK_RELATIVE(StringSplit_Gen_ReuseVector, iters) {
123 StringPiece line(kLine);
124 std::vector<StringPiece> parts;
127 split(line, ' ') | appendTo(parts);
130 folly::doNotOptimizeAway(s);
133 BENCHMARK_RELATIVE(StringSplit_Gen, iters) {
135 StringPiece line(kLine);
137 s += split(line, ' ') | count;
139 folly::doNotOptimizeAway(s);
142 BENCHMARK_RELATIVE(StringSplit_Gen_Take, iters) {
144 StringPiece line(kLine);
146 s += split(line, ' ') | take(10) | count;
148 folly::doNotOptimizeAway(s);
151 BENCHMARK_DRAW_LINE()
153 BENCHMARK(StringUnsplit_Old, iters) {
157 join(',', testStrVector, joined);
160 folly::doNotOptimizeAway(s);
163 BENCHMARK_RELATIVE(StringUnsplit_Old_ReusedBuffer, iters) {
168 join(',', testStrVector, joined);
171 folly::doNotOptimizeAway(s);
174 BENCHMARK_RELATIVE(StringUnsplit_Gen, iters) {
176 StringPiece line(kLine);
178 fbstring joined = from(testStrVector) | unsplit(',');
181 folly::doNotOptimizeAway(s);
184 BENCHMARK_RELATIVE(StringUnsplit_Gen_ReusedBuffer, iters) {
189 from(testStrVector) | unsplit(',', &buffer);
192 folly::doNotOptimizeAway(s);
195 BENCHMARK_DRAW_LINE()
197 void StringUnsplit_Gen(size_t iters, size_t joinSize) {
198 std::vector<fbstring> v;
200 FOR_EACH_RANGE(i, 0, joinSize) {
201 v.push_back(to<fbstring>(rand()));
208 from(v) | unsplit(',', &buffer);
211 folly::doNotOptimizeAway(s);
214 BENCHMARK_PARAM(StringUnsplit_Gen, 1000)
215 BENCHMARK_RELATIVE_PARAM(StringUnsplit_Gen, 2000)
216 BENCHMARK_RELATIVE_PARAM(StringUnsplit_Gen, 4000)
217 BENCHMARK_RELATIVE_PARAM(StringUnsplit_Gen, 8000)
219 BENCHMARK_DRAW_LINE()
220 void Lines_Gen(size_t iters, int joinSize) {
222 StringPiece content = testFileContent;
223 for (size_t i = 0; i < iters; ++i) {
224 s += lines(content.subpiece(0, joinSize)) | take(100) | count;
226 folly::doNotOptimizeAway(s);
229 BENCHMARK_PARAM(Lines_Gen, 1e3)
230 BENCHMARK_RELATIVE_PARAM(Lines_Gen, 2e3)
231 BENCHMARK_RELATIVE_PARAM(Lines_Gen, 3e3)
233 BENCHMARK_DRAW_LINE()
236 = seq<size_t>(1, 1000)
237 | mapped([](size_t i) {
238 return folly::to<fbstring>(i, ' ', i * i, ' ', i * i * i);
242 BENCHMARK(Records_EachToTuple, iters) {
244 for (size_t i = 0; i < iters; i += 1000) {
245 s += split(records, '\n')
246 | eachToTuple<int, size_t, StringPiece>(' ')
250 folly::doNotOptimizeAway(s);
253 BENCHMARK_RELATIVE(Records_VectorStringPieceReused, iters) {
255 std::vector<StringPiece> fields;
256 for (size_t i = 0; i < iters; i += 1000) {
257 s += split(records, '\n')
258 | mapped([&](StringPiece line) {
260 folly::split(' ', line, fields);
261 CHECK(fields.size() == 3);
262 return std::make_tuple(
263 folly::to<int>(fields[0]),
264 folly::to<size_t>(fields[1]),
265 StringPiece(fields[2]));
270 folly::doNotOptimizeAway(s);
273 BENCHMARK_RELATIVE(Records_VectorStringPiece, iters) {
275 for (size_t i = 0; i < iters; i += 1000) {
276 s += split(records, '\n')
277 | mapped([](StringPiece line) {
278 std::vector<StringPiece> fields;
279 folly::split(' ', line, fields);
280 CHECK(fields.size() == 3);
281 return std::make_tuple(
282 folly::to<int>(fields[0]),
283 folly::to<size_t>(fields[1]),
284 StringPiece(fields[2]));
289 folly::doNotOptimizeAway(s);
292 BENCHMARK_RELATIVE(Records_VectorString, iters) {
294 for (size_t i = 0; i < iters; i += 1000) {
295 s += split(records, '\n')
296 | mapped([](StringPiece line) {
297 std::vector<std::string> fields;
298 folly::split(' ', line, fields);
299 CHECK(fields.size() == 3);
300 return std::make_tuple(
301 folly::to<int>(fields[0]),
302 folly::to<size_t>(fields[1]),
303 StringPiece(fields[2]));
308 folly::doNotOptimizeAway(s);
311 // Results from an Intel(R) Xeon(R) CPU E5-2660 0 @ 2.20GHz
312 // ============================================================================
313 // folly/gen/test/StringBenchmark.cpp relative time/iter iters/s
314 // ============================================================================
315 // StringResplitter_Big 108.58us 9.21K
316 // StringResplitter_Small 10.60% 1.02ms 976.48
317 // ----------------------------------------------------------------------------
318 // StringSplit_Old 357.82ns 2.79M
319 // StringSplit_Gen_Vector 105.10% 340.46ns 2.94M
320 // ----------------------------------------------------------------------------
321 // StringSplit_Old_ReuseVector 96.45ns 10.37M
322 // StringSplit_Gen_ReuseVector 124.01% 77.78ns 12.86M
323 // StringSplit_Gen 140.10% 68.85ns 14.52M
324 // StringSplit_Gen_Take 122.97% 78.44ns 12.75M
325 // ----------------------------------------------------------------------------
326 // StringUnsplit_Old 42.99us 23.26K
327 // StringUnsplit_Old_ReusedBuffer 100.48% 42.79us 23.37K
328 // StringUnsplit_Gen 96.37% 44.61us 22.42K
329 // StringUnsplit_Gen_ReusedBuffer 116.96% 36.76us 27.20K
330 // ----------------------------------------------------------------------------
331 // StringUnsplit_Gen(1000) 44.71us 22.37K
332 // StringUnsplit_Gen(2000) 49.28% 90.72us 11.02K
333 // StringUnsplit_Gen(4000) 24.05% 185.91us 5.38K
334 // StringUnsplit_Gen(8000) 12.23% 365.42us 2.74K
335 // ----------------------------------------------------------------------------
336 // Records_EachToTuple 101.43us 9.86K
337 // Records_VectorStringPieceReused 93.72% 108.22us 9.24K
338 // Records_VectorStringPiece 37.14% 273.11us 3.66K
339 // Records_VectorString 16.70% 607.47us 1.65K
340 // ============================================================================
342 int main(int argc, char *argv[]) {
343 gflags::ParseCommandLineFlags(&argc, &argv, true);
344 initStringResplitterBenchmark();