1
2 #include <cstdint>
3 #include <new>
4 #include <vector>
5
6 #include "CartesianBenchmarks.h"
7 #include "GenerateInput.h"
8 #include "benchmark/benchmark.h"
9 #include "test_macros.h"
10
11 constexpr std::size_t MAX_STRING_LEN = 8 << 14;
12
13 // Benchmark when there is no match.
BM_StringFindNoMatch(benchmark::State & state)14 static void BM_StringFindNoMatch(benchmark::State &state) {
15 std::string s1(state.range(0), '-');
16 std::string s2(8, '*');
17 for (auto _ : state)
18 benchmark::DoNotOptimize(s1.find(s2));
19 }
20 BENCHMARK(BM_StringFindNoMatch)->Range(10, MAX_STRING_LEN);
21
22 // Benchmark when the string matches first time.
BM_StringFindAllMatch(benchmark::State & state)23 static void BM_StringFindAllMatch(benchmark::State &state) {
24 std::string s1(MAX_STRING_LEN, '-');
25 std::string s2(state.range(0), '-');
26 for (auto _ : state)
27 benchmark::DoNotOptimize(s1.find(s2));
28 }
29 BENCHMARK(BM_StringFindAllMatch)->Range(1, MAX_STRING_LEN);
30
31 // Benchmark when the string matches somewhere in the end.
BM_StringFindMatch1(benchmark::State & state)32 static void BM_StringFindMatch1(benchmark::State &state) {
33 std::string s1(MAX_STRING_LEN / 2, '*');
34 s1 += std::string(state.range(0), '-');
35 std::string s2(state.range(0), '-');
36 for (auto _ : state)
37 benchmark::DoNotOptimize(s1.find(s2));
38 }
39 BENCHMARK(BM_StringFindMatch1)->Range(1, MAX_STRING_LEN / 4);
40
41 // Benchmark when the string matches somewhere from middle to the end.
BM_StringFindMatch2(benchmark::State & state)42 static void BM_StringFindMatch2(benchmark::State &state) {
43 std::string s1(MAX_STRING_LEN / 2, '*');
44 s1 += std::string(state.range(0), '-');
45 s1 += std::string(state.range(0), '*');
46 std::string s2(state.range(0), '-');
47 for (auto _ : state)
48 benchmark::DoNotOptimize(s1.find(s2));
49 }
50 BENCHMARK(BM_StringFindMatch2)->Range(1, MAX_STRING_LEN / 4);
51
BM_StringCtorDefault(benchmark::State & state)52 static void BM_StringCtorDefault(benchmark::State &state) {
53 for (auto _ : state) {
54 std::string Default;
55 benchmark::DoNotOptimize(Default);
56 }
57 }
58 BENCHMARK(BM_StringCtorDefault);
59
60 enum class Length { Empty, Small, Large, Huge };
61 struct AllLengths : EnumValuesAsTuple<AllLengths, Length, 4> {
62 static constexpr const char* Names[] = {"Empty", "Small", "Large", "Huge"};
63 };
64
65 enum class Opacity { Opaque, Transparent };
66 struct AllOpacity : EnumValuesAsTuple<AllOpacity, Opacity, 2> {
67 static constexpr const char* Names[] = {"Opaque", "Transparent"};
68 };
69
70 enum class DiffType { Control, ChangeFirst, ChangeMiddle, ChangeLast };
71 struct AllDiffTypes : EnumValuesAsTuple<AllDiffTypes, DiffType, 4> {
72 static constexpr const char* Names[] = {"Control", "ChangeFirst",
73 "ChangeMiddle", "ChangeLast"};
74 };
75
76 static constexpr char SmallStringLiteral[] = "012345678";
77
getSmallString(DiffType D)78 TEST_ALWAYS_INLINE const char* getSmallString(DiffType D) {
79 switch (D) {
80 case DiffType::Control:
81 return SmallStringLiteral;
82 case DiffType::ChangeFirst:
83 return "-12345678";
84 case DiffType::ChangeMiddle:
85 return "0123-5678";
86 case DiffType::ChangeLast:
87 return "01234567-";
88 }
89 }
90
91 static constexpr char LargeStringLiteral[] =
92 "012345678901234567890123456789012345678901234567890123456789012";
93
getLargeString(DiffType D)94 TEST_ALWAYS_INLINE const char* getLargeString(DiffType D) {
95 #define LARGE_STRING_FIRST "123456789012345678901234567890"
96 #define LARGE_STRING_SECOND "234567890123456789012345678901"
97 switch (D) {
98 case DiffType::Control:
99 return "0" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "2";
100 case DiffType::ChangeFirst:
101 return "-" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "2";
102 case DiffType::ChangeMiddle:
103 return "0" LARGE_STRING_FIRST "-" LARGE_STRING_SECOND "2";
104 case DiffType::ChangeLast:
105 return "0" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "-";
106 }
107 }
108
getHugeString(DiffType D)109 TEST_ALWAYS_INLINE const char* getHugeString(DiffType D) {
110 #define HUGE_STRING0 "0123456789"
111 #define HUGE_STRING1 HUGE_STRING0 HUGE_STRING0 HUGE_STRING0 HUGE_STRING0
112 #define HUGE_STRING2 HUGE_STRING1 HUGE_STRING1 HUGE_STRING1 HUGE_STRING1
113 #define HUGE_STRING3 HUGE_STRING2 HUGE_STRING2 HUGE_STRING2 HUGE_STRING2
114 #define HUGE_STRING4 HUGE_STRING3 HUGE_STRING3 HUGE_STRING3 HUGE_STRING3
115 switch (D) {
116 case DiffType::Control:
117 return "0123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "0123456789";
118 case DiffType::ChangeFirst:
119 return "-123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "0123456789";
120 case DiffType::ChangeMiddle:
121 return "0123456789" HUGE_STRING4 "01234-6789" HUGE_STRING4 "0123456789";
122 case DiffType::ChangeLast:
123 return "0123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "012345678-";
124 }
125 }
126
getString(Length L,DiffType D=DiffType::Control)127 TEST_ALWAYS_INLINE const char* getString(Length L,
128 DiffType D = DiffType::Control) {
129 switch (L) {
130 case Length::Empty:
131 return "";
132 case Length::Small:
133 return getSmallString(D);
134 case Length::Large:
135 return getLargeString(D);
136 case Length::Huge:
137 return getHugeString(D);
138 }
139 }
140
makeString(Length L,DiffType D=DiffType::Control,Opacity O=Opacity::Transparent)141 TEST_ALWAYS_INLINE std::string makeString(Length L,
142 DiffType D = DiffType::Control,
143 Opacity O = Opacity::Transparent) {
144 switch (L) {
145 case Length::Empty:
146 return maybeOpaque("", O == Opacity::Opaque);
147 case Length::Small:
148 return maybeOpaque(getSmallString(D), O == Opacity::Opaque);
149 case Length::Large:
150 return maybeOpaque(getLargeString(D), O == Opacity::Opaque);
151 case Length::Huge:
152 return maybeOpaque(getHugeString(D), O == Opacity::Opaque);
153 }
154 }
155
156 template <class Length, class Opaque>
157 struct StringConstructDestroyCStr {
runStringConstructDestroyCStr158 static void run(benchmark::State& state) {
159 for (auto _ : state) {
160 benchmark::DoNotOptimize(
161 makeString(Length(), DiffType::Control, Opaque()));
162 }
163 }
164
nameStringConstructDestroyCStr165 static std::string name() {
166 return "BM_StringConstructDestroyCStr" + Length::name() + Opaque::name();
167 }
168 };
169
170 template <class Length, bool MeasureCopy, bool MeasureDestroy>
StringCopyAndDestroy(benchmark::State & state)171 static void StringCopyAndDestroy(benchmark::State& state) {
172 static constexpr size_t NumStrings = 1024;
173 auto Orig = makeString(Length());
174 std::aligned_storage<sizeof(std::string)>::type Storage[NumStrings];
175
176 while (state.KeepRunningBatch(NumStrings)) {
177 if (!MeasureCopy)
178 state.PauseTiming();
179 for (size_t I = 0; I < NumStrings; ++I) {
180 ::new (static_cast<void*>(Storage + I)) std::string(Orig);
181 }
182 if (!MeasureCopy)
183 state.ResumeTiming();
184 if (!MeasureDestroy)
185 state.PauseTiming();
186 for (size_t I = 0; I < NumStrings; ++I) {
187 using S = std::string;
188 reinterpret_cast<S*>(Storage + I)->~S();
189 }
190 if (!MeasureDestroy)
191 state.ResumeTiming();
192 }
193 }
194
195 template <class Length>
196 struct StringCopy {
runStringCopy197 static void run(benchmark::State& state) {
198 StringCopyAndDestroy<Length, true, false>(state);
199 }
200
nameStringCopy201 static std::string name() { return "BM_StringCopy" + Length::name(); }
202 };
203
204 template <class Length>
205 struct StringDestroy {
runStringDestroy206 static void run(benchmark::State& state) {
207 StringCopyAndDestroy<Length, false, true>(state);
208 }
209
nameStringDestroy210 static std::string name() { return "BM_StringDestroy" + Length::name(); }
211 };
212
213 template <class Length>
214 struct StringMove {
runStringMove215 static void run(benchmark::State& state) {
216 // Keep two object locations and move construct back and forth.
217 std::aligned_storage<sizeof(std::string), alignof(std::string)>::type Storage[2];
218 using S = std::string;
219 size_t I = 0;
220 S *newS = new (static_cast<void*>(Storage)) std::string(makeString(Length()));
221 for (auto _ : state) {
222 // Switch locations.
223 I ^= 1;
224 benchmark::DoNotOptimize(Storage);
225 // Move construct into the new location,
226 S *tmpS = new (static_cast<void*>(Storage + I)) S(std::move(*newS));
227 // then destroy the old one.
228 newS->~S();
229 newS = tmpS;
230 }
231 newS->~S();
232 }
233
nameStringMove234 static std::string name() { return "BM_StringMove" + Length::name(); }
235 };
236
237 template <class Length, class Opaque>
238 struct StringResizeDefaultInit {
runStringResizeDefaultInit239 static void run(benchmark::State& state) {
240 constexpr bool opaque = Opaque{} == Opacity::Opaque;
241 constexpr int kNumStrings = 4 << 10;
242 size_t length = makeString(Length()).size();
243 std::string strings[kNumStrings];
244 while (state.KeepRunningBatch(kNumStrings)) {
245 state.PauseTiming();
246 for (int i = 0; i < kNumStrings; ++i) {
247 std::string().swap(strings[i]);
248 }
249 benchmark::DoNotOptimize(strings);
250 state.ResumeTiming();
251 for (int i = 0; i < kNumStrings; ++i) {
252 strings[i].__resize_default_init(maybeOpaque(length, opaque));
253 }
254 }
255 }
256
nameStringResizeDefaultInit257 static std::string name() {
258 return "BM_StringResizeDefaultInit" + Length::name() + Opaque::name();
259 }
260 };
261
262 template <class Length, class Opaque>
263 struct StringAssignStr {
runStringAssignStr264 static void run(benchmark::State& state) {
265 constexpr bool opaque = Opaque{} == Opacity::Opaque;
266 constexpr int kNumStrings = 4 << 10;
267 std::string src = makeString(Length());
268 std::string strings[kNumStrings];
269 while (state.KeepRunningBatch(kNumStrings)) {
270 state.PauseTiming();
271 for (int i = 0; i < kNumStrings; ++i) {
272 std::string().swap(strings[i]);
273 }
274 benchmark::DoNotOptimize(strings);
275 state.ResumeTiming();
276 for (int i = 0; i < kNumStrings; ++i) {
277 strings[i] = *maybeOpaque(&src, opaque);
278 }
279 }
280 }
281
nameStringAssignStr282 static std::string name() {
283 return "BM_StringAssignStr" + Length::name() + Opaque::name();
284 }
285 };
286
287 template <class Length, class Opaque>
288 struct StringAssignAsciiz {
runStringAssignAsciiz289 static void run(benchmark::State& state) {
290 constexpr bool opaque = Opaque{} == Opacity::Opaque;
291 constexpr int kNumStrings = 4 << 10;
292 std::string strings[kNumStrings];
293 while (state.KeepRunningBatch(kNumStrings)) {
294 state.PauseTiming();
295 for (int i = 0; i < kNumStrings; ++i) {
296 std::string().swap(strings[i]);
297 }
298 benchmark::DoNotOptimize(strings);
299 state.ResumeTiming();
300 for (int i = 0; i < kNumStrings; ++i) {
301 strings[i] = maybeOpaque(getString(Length()), opaque);
302 }
303 }
304 }
305
nameStringAssignAsciiz306 static std::string name() {
307 return "BM_StringAssignAsciiz" + Length::name() + Opaque::name();
308 }
309 };
310
311 template <class Length, class Opaque>
312 struct StringEraseToEnd {
runStringEraseToEnd313 static void run(benchmark::State& state) {
314 constexpr bool opaque = Opaque{} == Opacity::Opaque;
315 constexpr int kNumStrings = 4 << 10;
316 std::string strings[kNumStrings];
317 const int mid = makeString(Length()).size() / 2;
318 while (state.KeepRunningBatch(kNumStrings)) {
319 state.PauseTiming();
320 for (int i = 0; i < kNumStrings; ++i) {
321 strings[i] = makeString(Length());
322 }
323 benchmark::DoNotOptimize(strings);
324 state.ResumeTiming();
325 for (int i = 0; i < kNumStrings; ++i) {
326 strings[i].erase(maybeOpaque(mid, opaque),
327 maybeOpaque(std::string::npos, opaque));
328 }
329 }
330 }
331
nameStringEraseToEnd332 static std::string name() {
333 return "BM_StringEraseToEnd" + Length::name() + Opaque::name();
334 }
335 };
336
337 template <class Length, class Opaque>
338 struct StringEraseWithMove {
runStringEraseWithMove339 static void run(benchmark::State& state) {
340 constexpr bool opaque = Opaque{} == Opacity::Opaque;
341 constexpr int kNumStrings = 4 << 10;
342 std::string strings[kNumStrings];
343 const int n = makeString(Length()).size() / 2;
344 const int pos = n / 2;
345 while (state.KeepRunningBatch(kNumStrings)) {
346 state.PauseTiming();
347 for (int i = 0; i < kNumStrings; ++i) {
348 strings[i] = makeString(Length());
349 }
350 benchmark::DoNotOptimize(strings);
351 state.ResumeTiming();
352 for (int i = 0; i < kNumStrings; ++i) {
353 strings[i].erase(maybeOpaque(pos, opaque), maybeOpaque(n, opaque));
354 }
355 }
356 }
357
nameStringEraseWithMove358 static std::string name() {
359 return "BM_StringEraseWithMove" + Length::name() + Opaque::name();
360 }
361 };
362
363 template <class Opaque>
364 struct StringAssignAsciizMix {
runStringAssignAsciizMix365 static void run(benchmark::State& state) {
366 constexpr auto O = Opaque{};
367 constexpr auto D = DiffType::Control;
368 constexpr int kNumStrings = 4 << 10;
369 std::string strings[kNumStrings];
370 while (state.KeepRunningBatch(kNumStrings)) {
371 state.PauseTiming();
372 for (int i = 0; i < kNumStrings; ++i) {
373 std::string().swap(strings[i]);
374 }
375 benchmark::DoNotOptimize(strings);
376 state.ResumeTiming();
377 for (int i = 0; i < kNumStrings - 7; i += 8) {
378 strings[i + 0] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
379 strings[i + 1] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
380 strings[i + 2] = maybeOpaque(getLargeString(D), O == Opacity::Opaque);
381 strings[i + 3] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
382 strings[i + 4] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
383 strings[i + 5] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
384 strings[i + 6] = maybeOpaque(getLargeString(D), O == Opacity::Opaque);
385 strings[i + 7] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
386 }
387 }
388 }
389
nameStringAssignAsciizMix390 static std::string name() {
391 return "BM_StringAssignAsciizMix" + Opaque::name();
392 }
393 };
394
395 enum class Relation { Eq, Less, Compare };
396 struct AllRelations : EnumValuesAsTuple<AllRelations, Relation, 3> {
397 static constexpr const char* Names[] = {"Eq", "Less", "Compare"};
398 };
399
400 template <class Rel, class LHLength, class RHLength, class DiffType>
401 struct StringRelational {
runStringRelational402 static void run(benchmark::State& state) {
403 auto Lhs = makeString(RHLength());
404 auto Rhs = makeString(LHLength(), DiffType());
405 for (auto _ : state) {
406 benchmark::DoNotOptimize(Lhs);
407 benchmark::DoNotOptimize(Rhs);
408 switch (Rel()) {
409 case Relation::Eq:
410 benchmark::DoNotOptimize(Lhs == Rhs);
411 break;
412 case Relation::Less:
413 benchmark::DoNotOptimize(Lhs < Rhs);
414 break;
415 case Relation::Compare:
416 benchmark::DoNotOptimize(Lhs.compare(Rhs));
417 break;
418 }
419 }
420 }
421
skipStringRelational422 static bool skip() {
423 // Eq is commutative, so skip half the matrix.
424 if (Rel() == Relation::Eq && LHLength() > RHLength())
425 return true;
426 // We only care about control when the lengths differ.
427 if (LHLength() != RHLength() && DiffType() != ::DiffType::Control)
428 return true;
429 // For empty, only control matters.
430 if (LHLength() == Length::Empty && DiffType() != ::DiffType::Control)
431 return true;
432 return false;
433 }
434
nameStringRelational435 static std::string name() {
436 return "BM_StringRelational" + Rel::name() + LHLength::name() +
437 RHLength::name() + DiffType::name();
438 }
439 };
440
441 template <class Rel, class LHLength, class RHLength, class DiffType>
442 struct StringRelationalLiteral {
runStringRelationalLiteral443 static void run(benchmark::State& state) {
444 auto Lhs = makeString(LHLength(), DiffType());
445 for (auto _ : state) {
446 benchmark::DoNotOptimize(Lhs);
447 constexpr const char* Literal = RHLength::value == Length::Empty
448 ? ""
449 : RHLength::value == Length::Small
450 ? SmallStringLiteral
451 : LargeStringLiteral;
452 switch (Rel()) {
453 case Relation::Eq:
454 benchmark::DoNotOptimize(Lhs == Literal);
455 break;
456 case Relation::Less:
457 benchmark::DoNotOptimize(Lhs < Literal);
458 break;
459 case Relation::Compare:
460 benchmark::DoNotOptimize(Lhs.compare(Literal));
461 break;
462 }
463 }
464 }
465
skipStringRelationalLiteral466 static bool skip() {
467 // Doesn't matter how they differ if they have different size.
468 if (LHLength() != RHLength() && DiffType() != ::DiffType::Control)
469 return true;
470 // We don't need huge. Doensn't give anything different than Large.
471 if (LHLength() == Length::Huge || RHLength() == Length::Huge)
472 return true;
473 return false;
474 }
475
nameStringRelationalLiteral476 static std::string name() {
477 return "BM_StringRelationalLiteral" + Rel::name() + LHLength::name() +
478 RHLength::name() + DiffType::name();
479 }
480 };
481
482 enum class Depth { Shallow, Deep };
483 struct AllDepths : EnumValuesAsTuple<AllDepths, Depth, 2> {
484 static constexpr const char* Names[] = {"Shallow", "Deep"};
485 };
486
487 enum class Temperature { Hot, Cold };
488 struct AllTemperatures : EnumValuesAsTuple<AllTemperatures, Temperature, 2> {
489 static constexpr const char* Names[] = {"Hot", "Cold"};
490 };
491
492 template <class Temperature, class Depth, class Length>
493 struct StringRead {
runStringRead494 void run(benchmark::State& state) const {
495 static constexpr size_t NumStrings =
496 Temperature() == ::Temperature::Hot
497 ? 1 << 10
498 : /* Enough strings to overflow the cache */ 1 << 20;
499 static_assert((NumStrings & (NumStrings - 1)) == 0,
500 "NumStrings should be a power of two to reduce overhead.");
501
502 std::vector<std::string> Values(NumStrings, makeString(Length()));
503 size_t I = 0;
504 for (auto _ : state) {
505 // Jump long enough to defeat cache locality, and use a value that is
506 // coprime with NumStrings to ensure we visit every element.
507 I = (I + 17) % NumStrings;
508 const auto& V = Values[I];
509
510 // Read everything first. Escaping data() through DoNotOptimize might
511 // cause the compiler to have to recalculate information about `V` due to
512 // aliasing.
513 const char* const Data = V.data();
514 const size_t Size = V.size();
515 benchmark::DoNotOptimize(Data);
516 benchmark::DoNotOptimize(Size);
517 if (Depth() == ::Depth::Deep) {
518 // Read into the payload. This mainly shows the benefit of SSO when the
519 // data is cold.
520 benchmark::DoNotOptimize(*Data);
521 }
522 }
523 }
524
skipStringRead525 static bool skip() {
526 // Huge does not give us anything that Large doesn't have. Skip it.
527 if (Length() == ::Length::Huge) {
528 return true;
529 }
530 return false;
531 }
532
nameStringRead533 std::string name() const {
534 return "BM_StringRead" + Temperature::name() + Depth::name() +
535 Length::name();
536 }
537 };
538
sanityCheckGeneratedStrings()539 void sanityCheckGeneratedStrings() {
540 for (auto Lhs : {Length::Empty, Length::Small, Length::Large, Length::Huge}) {
541 const auto LhsString = makeString(Lhs);
542 for (auto Rhs :
543 {Length::Empty, Length::Small, Length::Large, Length::Huge}) {
544 if (Lhs > Rhs)
545 continue;
546 const auto RhsString = makeString(Rhs);
547
548 // The smaller one must be a prefix of the larger one.
549 if (RhsString.find(LhsString) != 0) {
550 fprintf(stderr, "Invalid autogenerated strings for sizes (%d,%d).\n",
551 static_cast<int>(Lhs), static_cast<int>(Rhs));
552 std::abort();
553 }
554 }
555 }
556 // Verify the autogenerated diffs
557 for (auto L : {Length::Small, Length::Large, Length::Huge}) {
558 const auto Control = makeString(L);
559 const auto Verify = [&](std::string Exp, size_t Pos) {
560 // Only change on the Pos char.
561 if (Control[Pos] != Exp[Pos]) {
562 Exp[Pos] = Control[Pos];
563 if (Control == Exp)
564 return;
565 }
566 fprintf(stderr, "Invalid autogenerated diff with size %d\n",
567 static_cast<int>(L));
568 std::abort();
569 };
570 Verify(makeString(L, DiffType::ChangeFirst), 0);
571 Verify(makeString(L, DiffType::ChangeMiddle), Control.size() / 2);
572 Verify(makeString(L, DiffType::ChangeLast), Control.size() - 1);
573 }
574 }
575
576 // Some small codegen thunks to easily see generated code.
StringEqString(const std::string & a,const std::string & b)577 bool StringEqString(const std::string& a, const std::string& b) {
578 return a == b;
579 }
StringEqCStr(const std::string & a,const char * b)580 bool StringEqCStr(const std::string& a, const char* b) { return a == b; }
CStrEqString(const char * a,const std::string & b)581 bool CStrEqString(const char* a, const std::string& b) { return a == b; }
StringEqCStrLiteralEmpty(const std::string & a)582 bool StringEqCStrLiteralEmpty(const std::string& a) {
583 return a == "";
584 }
StringEqCStrLiteralSmall(const std::string & a)585 bool StringEqCStrLiteralSmall(const std::string& a) {
586 return a == SmallStringLiteral;
587 }
StringEqCStrLiteralLarge(const std::string & a)588 bool StringEqCStrLiteralLarge(const std::string& a) {
589 return a == LargeStringLiteral;
590 }
591
main(int argc,char ** argv)592 int main(int argc, char** argv) {
593 benchmark::Initialize(&argc, argv);
594 if (benchmark::ReportUnrecognizedArguments(argc, argv))
595 return 1;
596
597 sanityCheckGeneratedStrings();
598
599 makeCartesianProductBenchmark<StringConstructDestroyCStr, AllLengths,
600 AllOpacity>();
601
602 makeCartesianProductBenchmark<StringAssignStr, AllLengths, AllOpacity>();
603 makeCartesianProductBenchmark<StringAssignAsciiz, AllLengths, AllOpacity>();
604 makeCartesianProductBenchmark<StringAssignAsciizMix, AllOpacity>();
605
606 makeCartesianProductBenchmark<StringCopy, AllLengths>();
607 makeCartesianProductBenchmark<StringMove, AllLengths>();
608 makeCartesianProductBenchmark<StringDestroy, AllLengths>();
609 makeCartesianProductBenchmark<StringResizeDefaultInit, AllLengths,
610 AllOpacity>();
611 makeCartesianProductBenchmark<StringEraseToEnd, AllLengths, AllOpacity>();
612 makeCartesianProductBenchmark<StringEraseWithMove, AllLengths, AllOpacity>();
613 makeCartesianProductBenchmark<StringRelational, AllRelations, AllLengths,
614 AllLengths, AllDiffTypes>();
615 makeCartesianProductBenchmark<StringRelationalLiteral, AllRelations,
616 AllLengths, AllLengths, AllDiffTypes>();
617 makeCartesianProductBenchmark<StringRead, AllTemperatures, AllDepths,
618 AllLengths>();
619 benchmark::RunSpecifiedBenchmarks();
620
621 if (argc < 0) {
622 // ODR-use the functions to force them being generated in the binary.
623 auto functions = std::make_tuple(
624 StringEqString, StringEqCStr, CStrEqString, StringEqCStrLiteralEmpty,
625 StringEqCStrLiteralSmall, StringEqCStrLiteralLarge);
626 printf("%p", &functions);
627 }
628 }
629