146035553Spatrick
246035553Spatrick #include <cstdint>
346035553Spatrick #include <new>
446035553Spatrick #include <vector>
546035553Spatrick
646035553Spatrick #include "CartesianBenchmarks.h"
746035553Spatrick #include "GenerateInput.h"
846035553Spatrick #include "benchmark/benchmark.h"
946035553Spatrick #include "test_macros.h"
1046035553Spatrick
1146035553Spatrick constexpr std::size_t MAX_STRING_LEN = 8 << 14;
1246035553Spatrick
1346035553Spatrick // Benchmark when there is no match.
BM_StringFindNoMatch(benchmark::State & state)1446035553Spatrick static void BM_StringFindNoMatch(benchmark::State &state) {
1546035553Spatrick std::string s1(state.range(0), '-');
1646035553Spatrick std::string s2(8, '*');
1746035553Spatrick for (auto _ : state)
1846035553Spatrick benchmark::DoNotOptimize(s1.find(s2));
1946035553Spatrick }
2046035553Spatrick BENCHMARK(BM_StringFindNoMatch)->Range(10, MAX_STRING_LEN);
2146035553Spatrick
2246035553Spatrick // Benchmark when the string matches first time.
BM_StringFindAllMatch(benchmark::State & state)2346035553Spatrick static void BM_StringFindAllMatch(benchmark::State &state) {
2446035553Spatrick std::string s1(MAX_STRING_LEN, '-');
2546035553Spatrick std::string s2(state.range(0), '-');
2646035553Spatrick for (auto _ : state)
2746035553Spatrick benchmark::DoNotOptimize(s1.find(s2));
2846035553Spatrick }
2946035553Spatrick BENCHMARK(BM_StringFindAllMatch)->Range(1, MAX_STRING_LEN);
3046035553Spatrick
3146035553Spatrick // Benchmark when the string matches somewhere in the end.
BM_StringFindMatch1(benchmark::State & state)3246035553Spatrick static void BM_StringFindMatch1(benchmark::State &state) {
3346035553Spatrick std::string s1(MAX_STRING_LEN / 2, '*');
3446035553Spatrick s1 += std::string(state.range(0), '-');
3546035553Spatrick std::string s2(state.range(0), '-');
3646035553Spatrick for (auto _ : state)
3746035553Spatrick benchmark::DoNotOptimize(s1.find(s2));
3846035553Spatrick }
3946035553Spatrick BENCHMARK(BM_StringFindMatch1)->Range(1, MAX_STRING_LEN / 4);
4046035553Spatrick
4146035553Spatrick // Benchmark when the string matches somewhere from middle to the end.
BM_StringFindMatch2(benchmark::State & state)4246035553Spatrick static void BM_StringFindMatch2(benchmark::State &state) {
4346035553Spatrick std::string s1(MAX_STRING_LEN / 2, '*');
4446035553Spatrick s1 += std::string(state.range(0), '-');
4546035553Spatrick s1 += std::string(state.range(0), '*');
4646035553Spatrick std::string s2(state.range(0), '-');
4746035553Spatrick for (auto _ : state)
4846035553Spatrick benchmark::DoNotOptimize(s1.find(s2));
4946035553Spatrick }
5046035553Spatrick BENCHMARK(BM_StringFindMatch2)->Range(1, MAX_STRING_LEN / 4);
5146035553Spatrick
BM_StringCtorDefault(benchmark::State & state)5246035553Spatrick static void BM_StringCtorDefault(benchmark::State &state) {
5346035553Spatrick for (auto _ : state) {
5446035553Spatrick std::string Default;
5546035553Spatrick benchmark::DoNotOptimize(Default);
5646035553Spatrick }
5746035553Spatrick }
5846035553Spatrick BENCHMARK(BM_StringCtorDefault);
5946035553Spatrick
6046035553Spatrick enum class Length { Empty, Small, Large, Huge };
6146035553Spatrick struct AllLengths : EnumValuesAsTuple<AllLengths, Length, 4> {
6246035553Spatrick static constexpr const char* Names[] = {"Empty", "Small", "Large", "Huge"};
6346035553Spatrick };
6446035553Spatrick
6546035553Spatrick enum class Opacity { Opaque, Transparent };
6646035553Spatrick struct AllOpacity : EnumValuesAsTuple<AllOpacity, Opacity, 2> {
6746035553Spatrick static constexpr const char* Names[] = {"Opaque", "Transparent"};
6846035553Spatrick };
6946035553Spatrick
7046035553Spatrick enum class DiffType { Control, ChangeFirst, ChangeMiddle, ChangeLast };
7146035553Spatrick struct AllDiffTypes : EnumValuesAsTuple<AllDiffTypes, DiffType, 4> {
7246035553Spatrick static constexpr const char* Names[] = {"Control", "ChangeFirst",
7346035553Spatrick "ChangeMiddle", "ChangeLast"};
7446035553Spatrick };
7546035553Spatrick
7646035553Spatrick static constexpr char SmallStringLiteral[] = "012345678";
7746035553Spatrick
getSmallString(DiffType D)7846035553Spatrick TEST_ALWAYS_INLINE const char* getSmallString(DiffType D) {
7946035553Spatrick switch (D) {
8046035553Spatrick case DiffType::Control:
8146035553Spatrick return SmallStringLiteral;
8246035553Spatrick case DiffType::ChangeFirst:
8346035553Spatrick return "-12345678";
8446035553Spatrick case DiffType::ChangeMiddle:
8546035553Spatrick return "0123-5678";
8646035553Spatrick case DiffType::ChangeLast:
8746035553Spatrick return "01234567-";
8846035553Spatrick }
8946035553Spatrick }
9046035553Spatrick
9146035553Spatrick static constexpr char LargeStringLiteral[] =
9246035553Spatrick "012345678901234567890123456789012345678901234567890123456789012";
9346035553Spatrick
getLargeString(DiffType D)9446035553Spatrick TEST_ALWAYS_INLINE const char* getLargeString(DiffType D) {
9546035553Spatrick #define LARGE_STRING_FIRST "123456789012345678901234567890"
9646035553Spatrick #define LARGE_STRING_SECOND "234567890123456789012345678901"
9746035553Spatrick switch (D) {
9846035553Spatrick case DiffType::Control:
9946035553Spatrick return "0" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "2";
10046035553Spatrick case DiffType::ChangeFirst:
10146035553Spatrick return "-" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "2";
10246035553Spatrick case DiffType::ChangeMiddle:
10346035553Spatrick return "0" LARGE_STRING_FIRST "-" LARGE_STRING_SECOND "2";
10446035553Spatrick case DiffType::ChangeLast:
10546035553Spatrick return "0" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "-";
10646035553Spatrick }
10746035553Spatrick }
10846035553Spatrick
getHugeString(DiffType D)10946035553Spatrick TEST_ALWAYS_INLINE const char* getHugeString(DiffType D) {
11046035553Spatrick #define HUGE_STRING0 "0123456789"
11146035553Spatrick #define HUGE_STRING1 HUGE_STRING0 HUGE_STRING0 HUGE_STRING0 HUGE_STRING0
11246035553Spatrick #define HUGE_STRING2 HUGE_STRING1 HUGE_STRING1 HUGE_STRING1 HUGE_STRING1
11346035553Spatrick #define HUGE_STRING3 HUGE_STRING2 HUGE_STRING2 HUGE_STRING2 HUGE_STRING2
11446035553Spatrick #define HUGE_STRING4 HUGE_STRING3 HUGE_STRING3 HUGE_STRING3 HUGE_STRING3
11546035553Spatrick switch (D) {
11646035553Spatrick case DiffType::Control:
11746035553Spatrick return "0123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "0123456789";
11846035553Spatrick case DiffType::ChangeFirst:
11946035553Spatrick return "-123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "0123456789";
12046035553Spatrick case DiffType::ChangeMiddle:
12146035553Spatrick return "0123456789" HUGE_STRING4 "01234-6789" HUGE_STRING4 "0123456789";
12246035553Spatrick case DiffType::ChangeLast:
12346035553Spatrick return "0123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "012345678-";
12446035553Spatrick }
12546035553Spatrick }
12646035553Spatrick
getString(Length L,DiffType D=DiffType::Control)12746035553Spatrick TEST_ALWAYS_INLINE const char* getString(Length L,
12846035553Spatrick DiffType D = DiffType::Control) {
12946035553Spatrick switch (L) {
13046035553Spatrick case Length::Empty:
13146035553Spatrick return "";
13246035553Spatrick case Length::Small:
13346035553Spatrick return getSmallString(D);
13446035553Spatrick case Length::Large:
13546035553Spatrick return getLargeString(D);
13646035553Spatrick case Length::Huge:
13746035553Spatrick return getHugeString(D);
13846035553Spatrick }
13946035553Spatrick }
14046035553Spatrick
makeString(Length L,DiffType D=DiffType::Control,Opacity O=Opacity::Transparent)14146035553Spatrick TEST_ALWAYS_INLINE std::string makeString(Length L,
14246035553Spatrick DiffType D = DiffType::Control,
14346035553Spatrick Opacity O = Opacity::Transparent) {
14446035553Spatrick switch (L) {
14546035553Spatrick case Length::Empty:
14646035553Spatrick return maybeOpaque("", O == Opacity::Opaque);
14746035553Spatrick case Length::Small:
14846035553Spatrick return maybeOpaque(getSmallString(D), O == Opacity::Opaque);
14946035553Spatrick case Length::Large:
15046035553Spatrick return maybeOpaque(getLargeString(D), O == Opacity::Opaque);
15146035553Spatrick case Length::Huge:
15246035553Spatrick return maybeOpaque(getHugeString(D), O == Opacity::Opaque);
15346035553Spatrick }
15446035553Spatrick }
15546035553Spatrick
15646035553Spatrick template <class Length, class Opaque>
15746035553Spatrick struct StringConstructDestroyCStr {
runStringConstructDestroyCStr15846035553Spatrick static void run(benchmark::State& state) {
15946035553Spatrick for (auto _ : state) {
16046035553Spatrick benchmark::DoNotOptimize(
16146035553Spatrick makeString(Length(), DiffType::Control, Opaque()));
16246035553Spatrick }
16346035553Spatrick }
16446035553Spatrick
nameStringConstructDestroyCStr16546035553Spatrick static std::string name() {
16646035553Spatrick return "BM_StringConstructDestroyCStr" + Length::name() + Opaque::name();
16746035553Spatrick }
16846035553Spatrick };
16946035553Spatrick
17046035553Spatrick template <class Length, bool MeasureCopy, bool MeasureDestroy>
StringCopyAndDestroy(benchmark::State & state)17146035553Spatrick static void StringCopyAndDestroy(benchmark::State& state) {
17246035553Spatrick static constexpr size_t NumStrings = 1024;
17346035553Spatrick auto Orig = makeString(Length());
17446035553Spatrick std::aligned_storage<sizeof(std::string)>::type Storage[NumStrings];
17546035553Spatrick
17646035553Spatrick while (state.KeepRunningBatch(NumStrings)) {
17746035553Spatrick if (!MeasureCopy)
17846035553Spatrick state.PauseTiming();
17946035553Spatrick for (size_t I = 0; I < NumStrings; ++I) {
18046035553Spatrick ::new (static_cast<void*>(Storage + I)) std::string(Orig);
18146035553Spatrick }
18246035553Spatrick if (!MeasureCopy)
18346035553Spatrick state.ResumeTiming();
18446035553Spatrick if (!MeasureDestroy)
18546035553Spatrick state.PauseTiming();
18646035553Spatrick for (size_t I = 0; I < NumStrings; ++I) {
18746035553Spatrick using S = std::string;
18846035553Spatrick reinterpret_cast<S*>(Storage + I)->~S();
18946035553Spatrick }
19046035553Spatrick if (!MeasureDestroy)
19146035553Spatrick state.ResumeTiming();
19246035553Spatrick }
19346035553Spatrick }
19446035553Spatrick
19546035553Spatrick template <class Length>
19646035553Spatrick struct StringCopy {
runStringCopy19746035553Spatrick static void run(benchmark::State& state) {
19846035553Spatrick StringCopyAndDestroy<Length, true, false>(state);
19946035553Spatrick }
20046035553Spatrick
nameStringCopy20146035553Spatrick static std::string name() { return "BM_StringCopy" + Length::name(); }
20246035553Spatrick };
20346035553Spatrick
20446035553Spatrick template <class Length>
20546035553Spatrick struct StringDestroy {
runStringDestroy20646035553Spatrick static void run(benchmark::State& state) {
20746035553Spatrick StringCopyAndDestroy<Length, false, true>(state);
20846035553Spatrick }
20946035553Spatrick
nameStringDestroy21046035553Spatrick static std::string name() { return "BM_StringDestroy" + Length::name(); }
21146035553Spatrick };
21246035553Spatrick
21346035553Spatrick template <class Length>
21446035553Spatrick struct StringMove {
runStringMove21546035553Spatrick static void run(benchmark::State& state) {
21646035553Spatrick // Keep two object locations and move construct back and forth.
21746035553Spatrick std::aligned_storage<sizeof(std::string), alignof(std::string)>::type Storage[2];
21846035553Spatrick using S = std::string;
21946035553Spatrick size_t I = 0;
22046035553Spatrick S *newS = new (static_cast<void*>(Storage)) std::string(makeString(Length()));
22146035553Spatrick for (auto _ : state) {
22246035553Spatrick // Switch locations.
22346035553Spatrick I ^= 1;
22446035553Spatrick benchmark::DoNotOptimize(Storage);
22546035553Spatrick // Move construct into the new location,
22646035553Spatrick S *tmpS = new (static_cast<void*>(Storage + I)) S(std::move(*newS));
22746035553Spatrick // then destroy the old one.
22846035553Spatrick newS->~S();
22946035553Spatrick newS = tmpS;
23046035553Spatrick }
23146035553Spatrick newS->~S();
23246035553Spatrick }
23346035553Spatrick
nameStringMove23446035553Spatrick static std::string name() { return "BM_StringMove" + Length::name(); }
23546035553Spatrick };
23646035553Spatrick
23746035553Spatrick template <class Length, class Opaque>
23846035553Spatrick struct StringResizeDefaultInit {
runStringResizeDefaultInit23946035553Spatrick static void run(benchmark::State& state) {
24046035553Spatrick constexpr bool opaque = Opaque{} == Opacity::Opaque;
24146035553Spatrick constexpr int kNumStrings = 4 << 10;
24246035553Spatrick size_t length = makeString(Length()).size();
24346035553Spatrick std::string strings[kNumStrings];
24446035553Spatrick while (state.KeepRunningBatch(kNumStrings)) {
24546035553Spatrick state.PauseTiming();
24646035553Spatrick for (int i = 0; i < kNumStrings; ++i) {
24746035553Spatrick std::string().swap(strings[i]);
24846035553Spatrick }
24946035553Spatrick benchmark::DoNotOptimize(strings);
25046035553Spatrick state.ResumeTiming();
25146035553Spatrick for (int i = 0; i < kNumStrings; ++i) {
25246035553Spatrick strings[i].__resize_default_init(maybeOpaque(length, opaque));
25346035553Spatrick }
25446035553Spatrick }
25546035553Spatrick }
25646035553Spatrick
nameStringResizeDefaultInit25746035553Spatrick static std::string name() {
25846035553Spatrick return "BM_StringResizeDefaultInit" + Length::name() + Opaque::name();
25946035553Spatrick }
26046035553Spatrick };
26146035553Spatrick
26246035553Spatrick template <class Length, class Opaque>
26346035553Spatrick struct StringAssignStr {
runStringAssignStr26446035553Spatrick static void run(benchmark::State& state) {
26546035553Spatrick constexpr bool opaque = Opaque{} == Opacity::Opaque;
26646035553Spatrick constexpr int kNumStrings = 4 << 10;
26746035553Spatrick std::string src = makeString(Length());
26846035553Spatrick std::string strings[kNumStrings];
26946035553Spatrick while (state.KeepRunningBatch(kNumStrings)) {
27046035553Spatrick state.PauseTiming();
27146035553Spatrick for (int i = 0; i < kNumStrings; ++i) {
27246035553Spatrick std::string().swap(strings[i]);
27346035553Spatrick }
27446035553Spatrick benchmark::DoNotOptimize(strings);
27546035553Spatrick state.ResumeTiming();
27646035553Spatrick for (int i = 0; i < kNumStrings; ++i) {
27746035553Spatrick strings[i] = *maybeOpaque(&src, opaque);
27846035553Spatrick }
27946035553Spatrick }
28046035553Spatrick }
28146035553Spatrick
nameStringAssignStr28246035553Spatrick static std::string name() {
28346035553Spatrick return "BM_StringAssignStr" + Length::name() + Opaque::name();
28446035553Spatrick }
28546035553Spatrick };
28646035553Spatrick
28746035553Spatrick template <class Length, class Opaque>
28846035553Spatrick struct StringAssignAsciiz {
runStringAssignAsciiz28946035553Spatrick static void run(benchmark::State& state) {
29046035553Spatrick constexpr bool opaque = Opaque{} == Opacity::Opaque;
29146035553Spatrick constexpr int kNumStrings = 4 << 10;
29246035553Spatrick std::string strings[kNumStrings];
29346035553Spatrick while (state.KeepRunningBatch(kNumStrings)) {
29446035553Spatrick state.PauseTiming();
29546035553Spatrick for (int i = 0; i < kNumStrings; ++i) {
29646035553Spatrick std::string().swap(strings[i]);
29746035553Spatrick }
29846035553Spatrick benchmark::DoNotOptimize(strings);
29946035553Spatrick state.ResumeTiming();
30046035553Spatrick for (int i = 0; i < kNumStrings; ++i) {
30146035553Spatrick strings[i] = maybeOpaque(getString(Length()), opaque);
30246035553Spatrick }
30346035553Spatrick }
30446035553Spatrick }
30546035553Spatrick
nameStringAssignAsciiz30646035553Spatrick static std::string name() {
30746035553Spatrick return "BM_StringAssignAsciiz" + Length::name() + Opaque::name();
30846035553Spatrick }
30946035553Spatrick };
31046035553Spatrick
311*037e7968Spatrick template <class Length, class Opaque>
312*037e7968Spatrick struct StringEraseToEnd {
runStringEraseToEnd313*037e7968Spatrick static void run(benchmark::State& state) {
314*037e7968Spatrick constexpr bool opaque = Opaque{} == Opacity::Opaque;
315*037e7968Spatrick constexpr int kNumStrings = 4 << 10;
316*037e7968Spatrick std::string strings[kNumStrings];
317*037e7968Spatrick const int mid = makeString(Length()).size() / 2;
318*037e7968Spatrick while (state.KeepRunningBatch(kNumStrings)) {
319*037e7968Spatrick state.PauseTiming();
320*037e7968Spatrick for (int i = 0; i < kNumStrings; ++i) {
321*037e7968Spatrick strings[i] = makeString(Length());
322*037e7968Spatrick }
323*037e7968Spatrick benchmark::DoNotOptimize(strings);
324*037e7968Spatrick state.ResumeTiming();
325*037e7968Spatrick for (int i = 0; i < kNumStrings; ++i) {
326*037e7968Spatrick strings[i].erase(maybeOpaque(mid, opaque),
327*037e7968Spatrick maybeOpaque(std::string::npos, opaque));
328*037e7968Spatrick }
329*037e7968Spatrick }
330*037e7968Spatrick }
331*037e7968Spatrick
nameStringEraseToEnd332*037e7968Spatrick static std::string name() {
333*037e7968Spatrick return "BM_StringEraseToEnd" + Length::name() + Opaque::name();
334*037e7968Spatrick }
335*037e7968Spatrick };
336*037e7968Spatrick
337*037e7968Spatrick template <class Length, class Opaque>
338*037e7968Spatrick struct StringEraseWithMove {
runStringEraseWithMove339*037e7968Spatrick static void run(benchmark::State& state) {
340*037e7968Spatrick constexpr bool opaque = Opaque{} == Opacity::Opaque;
341*037e7968Spatrick constexpr int kNumStrings = 4 << 10;
342*037e7968Spatrick std::string strings[kNumStrings];
343*037e7968Spatrick const int n = makeString(Length()).size() / 2;
344*037e7968Spatrick const int pos = n / 2;
345*037e7968Spatrick while (state.KeepRunningBatch(kNumStrings)) {
346*037e7968Spatrick state.PauseTiming();
347*037e7968Spatrick for (int i = 0; i < kNumStrings; ++i) {
348*037e7968Spatrick strings[i] = makeString(Length());
349*037e7968Spatrick }
350*037e7968Spatrick benchmark::DoNotOptimize(strings);
351*037e7968Spatrick state.ResumeTiming();
352*037e7968Spatrick for (int i = 0; i < kNumStrings; ++i) {
353*037e7968Spatrick strings[i].erase(maybeOpaque(pos, opaque), maybeOpaque(n, opaque));
354*037e7968Spatrick }
355*037e7968Spatrick }
356*037e7968Spatrick }
357*037e7968Spatrick
nameStringEraseWithMove358*037e7968Spatrick static std::string name() {
359*037e7968Spatrick return "BM_StringEraseWithMove" + Length::name() + Opaque::name();
360*037e7968Spatrick }
361*037e7968Spatrick };
362*037e7968Spatrick
36346035553Spatrick template <class Opaque>
36446035553Spatrick struct StringAssignAsciizMix {
runStringAssignAsciizMix36546035553Spatrick static void run(benchmark::State& state) {
36646035553Spatrick constexpr auto O = Opaque{};
36746035553Spatrick constexpr auto D = DiffType::Control;
36846035553Spatrick constexpr int kNumStrings = 4 << 10;
36946035553Spatrick std::string strings[kNumStrings];
37046035553Spatrick while (state.KeepRunningBatch(kNumStrings)) {
37146035553Spatrick state.PauseTiming();
37246035553Spatrick for (int i = 0; i < kNumStrings; ++i) {
37346035553Spatrick std::string().swap(strings[i]);
37446035553Spatrick }
37546035553Spatrick benchmark::DoNotOptimize(strings);
37646035553Spatrick state.ResumeTiming();
37746035553Spatrick for (int i = 0; i < kNumStrings - 7; i += 8) {
37846035553Spatrick strings[i + 0] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
37946035553Spatrick strings[i + 1] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
38046035553Spatrick strings[i + 2] = maybeOpaque(getLargeString(D), O == Opacity::Opaque);
38146035553Spatrick strings[i + 3] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
38246035553Spatrick strings[i + 4] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
38346035553Spatrick strings[i + 5] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
38446035553Spatrick strings[i + 6] = maybeOpaque(getLargeString(D), O == Opacity::Opaque);
38546035553Spatrick strings[i + 7] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
38646035553Spatrick }
38746035553Spatrick }
38846035553Spatrick }
38946035553Spatrick
nameStringAssignAsciizMix39046035553Spatrick static std::string name() {
39146035553Spatrick return "BM_StringAssignAsciizMix" + Opaque::name();
39246035553Spatrick }
39346035553Spatrick };
39446035553Spatrick
39546035553Spatrick enum class Relation { Eq, Less, Compare };
39646035553Spatrick struct AllRelations : EnumValuesAsTuple<AllRelations, Relation, 3> {
39746035553Spatrick static constexpr const char* Names[] = {"Eq", "Less", "Compare"};
39846035553Spatrick };
39946035553Spatrick
40046035553Spatrick template <class Rel, class LHLength, class RHLength, class DiffType>
40146035553Spatrick struct StringRelational {
runStringRelational40246035553Spatrick static void run(benchmark::State& state) {
40346035553Spatrick auto Lhs = makeString(RHLength());
40446035553Spatrick auto Rhs = makeString(LHLength(), DiffType());
40546035553Spatrick for (auto _ : state) {
40646035553Spatrick benchmark::DoNotOptimize(Lhs);
40746035553Spatrick benchmark::DoNotOptimize(Rhs);
40846035553Spatrick switch (Rel()) {
40946035553Spatrick case Relation::Eq:
41046035553Spatrick benchmark::DoNotOptimize(Lhs == Rhs);
41146035553Spatrick break;
41246035553Spatrick case Relation::Less:
41346035553Spatrick benchmark::DoNotOptimize(Lhs < Rhs);
41446035553Spatrick break;
41546035553Spatrick case Relation::Compare:
41646035553Spatrick benchmark::DoNotOptimize(Lhs.compare(Rhs));
41746035553Spatrick break;
41846035553Spatrick }
41946035553Spatrick }
42046035553Spatrick }
42146035553Spatrick
skipStringRelational42246035553Spatrick static bool skip() {
42346035553Spatrick // Eq is commutative, so skip half the matrix.
42446035553Spatrick if (Rel() == Relation::Eq && LHLength() > RHLength())
42546035553Spatrick return true;
42646035553Spatrick // We only care about control when the lengths differ.
42746035553Spatrick if (LHLength() != RHLength() && DiffType() != ::DiffType::Control)
42846035553Spatrick return true;
42946035553Spatrick // For empty, only control matters.
43046035553Spatrick if (LHLength() == Length::Empty && DiffType() != ::DiffType::Control)
43146035553Spatrick return true;
43246035553Spatrick return false;
43346035553Spatrick }
43446035553Spatrick
nameStringRelational43546035553Spatrick static std::string name() {
43646035553Spatrick return "BM_StringRelational" + Rel::name() + LHLength::name() +
43746035553Spatrick RHLength::name() + DiffType::name();
43846035553Spatrick }
43946035553Spatrick };
44046035553Spatrick
44146035553Spatrick template <class Rel, class LHLength, class RHLength, class DiffType>
44246035553Spatrick struct StringRelationalLiteral {
runStringRelationalLiteral44346035553Spatrick static void run(benchmark::State& state) {
44446035553Spatrick auto Lhs = makeString(LHLength(), DiffType());
44546035553Spatrick for (auto _ : state) {
44646035553Spatrick benchmark::DoNotOptimize(Lhs);
44746035553Spatrick constexpr const char* Literal = RHLength::value == Length::Empty
44846035553Spatrick ? ""
44946035553Spatrick : RHLength::value == Length::Small
45046035553Spatrick ? SmallStringLiteral
45146035553Spatrick : LargeStringLiteral;
45246035553Spatrick switch (Rel()) {
45346035553Spatrick case Relation::Eq:
45446035553Spatrick benchmark::DoNotOptimize(Lhs == Literal);
45546035553Spatrick break;
45646035553Spatrick case Relation::Less:
45746035553Spatrick benchmark::DoNotOptimize(Lhs < Literal);
45846035553Spatrick break;
45946035553Spatrick case Relation::Compare:
46046035553Spatrick benchmark::DoNotOptimize(Lhs.compare(Literal));
46146035553Spatrick break;
46246035553Spatrick }
46346035553Spatrick }
46446035553Spatrick }
46546035553Spatrick
skipStringRelationalLiteral46646035553Spatrick static bool skip() {
46746035553Spatrick // Doesn't matter how they differ if they have different size.
46846035553Spatrick if (LHLength() != RHLength() && DiffType() != ::DiffType::Control)
46946035553Spatrick return true;
47046035553Spatrick // We don't need huge. Doensn't give anything different than Large.
47146035553Spatrick if (LHLength() == Length::Huge || RHLength() == Length::Huge)
47246035553Spatrick return true;
47346035553Spatrick return false;
47446035553Spatrick }
47546035553Spatrick
nameStringRelationalLiteral47646035553Spatrick static std::string name() {
47746035553Spatrick return "BM_StringRelationalLiteral" + Rel::name() + LHLength::name() +
47846035553Spatrick RHLength::name() + DiffType::name();
47946035553Spatrick }
48046035553Spatrick };
48146035553Spatrick
48246035553Spatrick enum class Depth { Shallow, Deep };
48346035553Spatrick struct AllDepths : EnumValuesAsTuple<AllDepths, Depth, 2> {
48446035553Spatrick static constexpr const char* Names[] = {"Shallow", "Deep"};
48546035553Spatrick };
48646035553Spatrick
48746035553Spatrick enum class Temperature { Hot, Cold };
48846035553Spatrick struct AllTemperatures : EnumValuesAsTuple<AllTemperatures, Temperature, 2> {
48946035553Spatrick static constexpr const char* Names[] = {"Hot", "Cold"};
49046035553Spatrick };
49146035553Spatrick
49246035553Spatrick template <class Temperature, class Depth, class Length>
49346035553Spatrick struct StringRead {
runStringRead49446035553Spatrick void run(benchmark::State& state) const {
49546035553Spatrick static constexpr size_t NumStrings =
49646035553Spatrick Temperature() == ::Temperature::Hot
49746035553Spatrick ? 1 << 10
49846035553Spatrick : /* Enough strings to overflow the cache */ 1 << 20;
49946035553Spatrick static_assert((NumStrings & (NumStrings - 1)) == 0,
50046035553Spatrick "NumStrings should be a power of two to reduce overhead.");
50146035553Spatrick
50246035553Spatrick std::vector<std::string> Values(NumStrings, makeString(Length()));
50346035553Spatrick size_t I = 0;
50446035553Spatrick for (auto _ : state) {
50546035553Spatrick // Jump long enough to defeat cache locality, and use a value that is
50646035553Spatrick // coprime with NumStrings to ensure we visit every element.
50746035553Spatrick I = (I + 17) % NumStrings;
50846035553Spatrick const auto& V = Values[I];
50946035553Spatrick
51046035553Spatrick // Read everything first. Escaping data() through DoNotOptimize might
51146035553Spatrick // cause the compiler to have to recalculate information about `V` due to
51246035553Spatrick // aliasing.
51346035553Spatrick const char* const Data = V.data();
51446035553Spatrick const size_t Size = V.size();
51546035553Spatrick benchmark::DoNotOptimize(Data);
51646035553Spatrick benchmark::DoNotOptimize(Size);
51746035553Spatrick if (Depth() == ::Depth::Deep) {
51846035553Spatrick // Read into the payload. This mainly shows the benefit of SSO when the
51946035553Spatrick // data is cold.
52046035553Spatrick benchmark::DoNotOptimize(*Data);
52146035553Spatrick }
52246035553Spatrick }
52346035553Spatrick }
52446035553Spatrick
skipStringRead52546035553Spatrick static bool skip() {
52646035553Spatrick // Huge does not give us anything that Large doesn't have. Skip it.
52746035553Spatrick if (Length() == ::Length::Huge) {
52846035553Spatrick return true;
52946035553Spatrick }
53046035553Spatrick return false;
53146035553Spatrick }
53246035553Spatrick
nameStringRead53346035553Spatrick std::string name() const {
53446035553Spatrick return "BM_StringRead" + Temperature::name() + Depth::name() +
53546035553Spatrick Length::name();
53646035553Spatrick }
53746035553Spatrick };
53846035553Spatrick
sanityCheckGeneratedStrings()53946035553Spatrick void sanityCheckGeneratedStrings() {
54046035553Spatrick for (auto Lhs : {Length::Empty, Length::Small, Length::Large, Length::Huge}) {
54146035553Spatrick const auto LhsString = makeString(Lhs);
54246035553Spatrick for (auto Rhs :
54346035553Spatrick {Length::Empty, Length::Small, Length::Large, Length::Huge}) {
54446035553Spatrick if (Lhs > Rhs)
54546035553Spatrick continue;
54646035553Spatrick const auto RhsString = makeString(Rhs);
54746035553Spatrick
54846035553Spatrick // The smaller one must be a prefix of the larger one.
54946035553Spatrick if (RhsString.find(LhsString) != 0) {
55046035553Spatrick fprintf(stderr, "Invalid autogenerated strings for sizes (%d,%d).\n",
55146035553Spatrick static_cast<int>(Lhs), static_cast<int>(Rhs));
55246035553Spatrick std::abort();
55346035553Spatrick }
55446035553Spatrick }
55546035553Spatrick }
55646035553Spatrick // Verify the autogenerated diffs
55746035553Spatrick for (auto L : {Length::Small, Length::Large, Length::Huge}) {
55846035553Spatrick const auto Control = makeString(L);
55946035553Spatrick const auto Verify = [&](std::string Exp, size_t Pos) {
56046035553Spatrick // Only change on the Pos char.
56146035553Spatrick if (Control[Pos] != Exp[Pos]) {
56246035553Spatrick Exp[Pos] = Control[Pos];
56346035553Spatrick if (Control == Exp)
56446035553Spatrick return;
56546035553Spatrick }
56646035553Spatrick fprintf(stderr, "Invalid autogenerated diff with size %d\n",
56746035553Spatrick static_cast<int>(L));
56846035553Spatrick std::abort();
56946035553Spatrick };
57046035553Spatrick Verify(makeString(L, DiffType::ChangeFirst), 0);
57146035553Spatrick Verify(makeString(L, DiffType::ChangeMiddle), Control.size() / 2);
57246035553Spatrick Verify(makeString(L, DiffType::ChangeLast), Control.size() - 1);
57346035553Spatrick }
57446035553Spatrick }
57546035553Spatrick
57646035553Spatrick // Some small codegen thunks to easily see generated code.
StringEqString(const std::string & a,const std::string & b)57746035553Spatrick bool StringEqString(const std::string& a, const std::string& b) {
57846035553Spatrick return a == b;
57946035553Spatrick }
StringEqCStr(const std::string & a,const char * b)58046035553Spatrick bool StringEqCStr(const std::string& a, const char* b) { return a == b; }
CStrEqString(const char * a,const std::string & b)58146035553Spatrick bool CStrEqString(const char* a, const std::string& b) { return a == b; }
StringEqCStrLiteralEmpty(const std::string & a)58246035553Spatrick bool StringEqCStrLiteralEmpty(const std::string& a) {
58346035553Spatrick return a == "";
58446035553Spatrick }
StringEqCStrLiteralSmall(const std::string & a)58546035553Spatrick bool StringEqCStrLiteralSmall(const std::string& a) {
58646035553Spatrick return a == SmallStringLiteral;
58746035553Spatrick }
StringEqCStrLiteralLarge(const std::string & a)58846035553Spatrick bool StringEqCStrLiteralLarge(const std::string& a) {
58946035553Spatrick return a == LargeStringLiteral;
59046035553Spatrick }
59146035553Spatrick
main(int argc,char ** argv)59246035553Spatrick int main(int argc, char** argv) {
59346035553Spatrick benchmark::Initialize(&argc, argv);
59446035553Spatrick if (benchmark::ReportUnrecognizedArguments(argc, argv))
59546035553Spatrick return 1;
59646035553Spatrick
59746035553Spatrick sanityCheckGeneratedStrings();
59846035553Spatrick
59946035553Spatrick makeCartesianProductBenchmark<StringConstructDestroyCStr, AllLengths,
60046035553Spatrick AllOpacity>();
60146035553Spatrick
60246035553Spatrick makeCartesianProductBenchmark<StringAssignStr, AllLengths, AllOpacity>();
60346035553Spatrick makeCartesianProductBenchmark<StringAssignAsciiz, AllLengths, AllOpacity>();
60446035553Spatrick makeCartesianProductBenchmark<StringAssignAsciizMix, AllOpacity>();
60546035553Spatrick
60646035553Spatrick makeCartesianProductBenchmark<StringCopy, AllLengths>();
60746035553Spatrick makeCartesianProductBenchmark<StringMove, AllLengths>();
60846035553Spatrick makeCartesianProductBenchmark<StringDestroy, AllLengths>();
60946035553Spatrick makeCartesianProductBenchmark<StringResizeDefaultInit, AllLengths,
61046035553Spatrick AllOpacity>();
611*037e7968Spatrick makeCartesianProductBenchmark<StringEraseToEnd, AllLengths, AllOpacity>();
612*037e7968Spatrick makeCartesianProductBenchmark<StringEraseWithMove, AllLengths, AllOpacity>();
61346035553Spatrick makeCartesianProductBenchmark<StringRelational, AllRelations, AllLengths,
61446035553Spatrick AllLengths, AllDiffTypes>();
61546035553Spatrick makeCartesianProductBenchmark<StringRelationalLiteral, AllRelations,
61646035553Spatrick AllLengths, AllLengths, AllDiffTypes>();
61746035553Spatrick makeCartesianProductBenchmark<StringRead, AllTemperatures, AllDepths,
61846035553Spatrick AllLengths>();
61946035553Spatrick benchmark::RunSpecifiedBenchmarks();
62046035553Spatrick
62146035553Spatrick if (argc < 0) {
62246035553Spatrick // ODR-use the functions to force them being generated in the binary.
62346035553Spatrick auto functions = std::make_tuple(
62446035553Spatrick StringEqString, StringEqCStr, CStrEqString, StringEqCStrLiteralEmpty,
62546035553Spatrick StringEqCStrLiteralSmall, StringEqCStrLiteralLarge);
62646035553Spatrick printf("%p", &functions);
62746035553Spatrick }
62846035553Spatrick }
629