190705006SSebastian Pop
274583444SSamuel Benzaquen #include <cstdint>
374583444SSamuel Benzaquen #include <new>
474583444SSamuel Benzaquen #include <vector>
574583444SSamuel Benzaquen
6f938755aSNico Weber #include "CartesianBenchmarks.h"
7f938755aSNico Weber #include "GenerateInput.h"
874583444SSamuel Benzaquen #include "benchmark/benchmark.h"
974583444SSamuel Benzaquen #include "test_macros.h"
1090705006SSebastian Pop
1190705006SSebastian Pop constexpr std::size_t MAX_STRING_LEN = 8 << 14;
1290705006SSebastian Pop
1390705006SSebastian Pop // Benchmark when there is no match.
BM_StringFindNoMatch(benchmark::State & state)1490705006SSebastian Pop static void BM_StringFindNoMatch(benchmark::State &state) {
1590705006SSebastian Pop std::string s1(state.range(0), '-');
1690705006SSebastian Pop std::string s2(8, '*');
1774583444SSamuel Benzaquen for (auto _ : state)
1890705006SSebastian Pop benchmark::DoNotOptimize(s1.find(s2));
1990705006SSebastian Pop }
2090705006SSebastian Pop BENCHMARK(BM_StringFindNoMatch)->Range(10, MAX_STRING_LEN);
2190705006SSebastian Pop
2290705006SSebastian Pop // Benchmark when the string matches first time.
BM_StringFindAllMatch(benchmark::State & state)2390705006SSebastian Pop static void BM_StringFindAllMatch(benchmark::State &state) {
2490705006SSebastian Pop std::string s1(MAX_STRING_LEN, '-');
2590705006SSebastian Pop std::string s2(state.range(0), '-');
2674583444SSamuel Benzaquen for (auto _ : state)
2790705006SSebastian Pop benchmark::DoNotOptimize(s1.find(s2));
2890705006SSebastian Pop }
2990705006SSebastian Pop BENCHMARK(BM_StringFindAllMatch)->Range(1, MAX_STRING_LEN);
3090705006SSebastian Pop
3190705006SSebastian Pop // Benchmark when the string matches somewhere in the end.
BM_StringFindMatch1(benchmark::State & state)3290705006SSebastian Pop static void BM_StringFindMatch1(benchmark::State &state) {
3390705006SSebastian Pop std::string s1(MAX_STRING_LEN / 2, '*');
3490705006SSebastian Pop s1 += std::string(state.range(0), '-');
3590705006SSebastian Pop std::string s2(state.range(0), '-');
3674583444SSamuel Benzaquen for (auto _ : state)
3790705006SSebastian Pop benchmark::DoNotOptimize(s1.find(s2));
3890705006SSebastian Pop }
3990705006SSebastian Pop BENCHMARK(BM_StringFindMatch1)->Range(1, MAX_STRING_LEN / 4);
4090705006SSebastian Pop
4190705006SSebastian Pop // Benchmark when the string matches somewhere from middle to the end.
BM_StringFindMatch2(benchmark::State & state)4290705006SSebastian Pop static void BM_StringFindMatch2(benchmark::State &state) {
4390705006SSebastian Pop std::string s1(MAX_STRING_LEN / 2, '*');
4490705006SSebastian Pop s1 += std::string(state.range(0), '-');
4590705006SSebastian Pop s1 += std::string(state.range(0), '*');
4690705006SSebastian Pop std::string s2(state.range(0), '-');
4774583444SSamuel Benzaquen for (auto _ : state)
4890705006SSebastian Pop benchmark::DoNotOptimize(s1.find(s2));
4990705006SSebastian Pop }
5090705006SSebastian Pop BENCHMARK(BM_StringFindMatch2)->Range(1, MAX_STRING_LEN / 4);
5190705006SSebastian Pop
BM_StringCtorDefault(benchmark::State & state)5209701324SEric Fiselier static void BM_StringCtorDefault(benchmark::State &state) {
5374583444SSamuel Benzaquen for (auto _ : state) {
5409701324SEric Fiselier std::string Default;
5574583444SSamuel Benzaquen benchmark::DoNotOptimize(Default);
5609701324SEric Fiselier }
5709701324SEric Fiselier }
5809701324SEric Fiselier BENCHMARK(BM_StringCtorDefault);
5909701324SEric Fiselier
6074583444SSamuel Benzaquen enum class Length { Empty, Small, Large, Huge };
6174583444SSamuel Benzaquen struct AllLengths : EnumValuesAsTuple<AllLengths, Length, 4> {
6274583444SSamuel Benzaquen static constexpr const char* Names[] = {"Empty", "Small", "Large", "Huge"};
6374583444SSamuel Benzaquen };
6409701324SEric Fiselier
6574583444SSamuel Benzaquen enum class Opacity { Opaque, Transparent };
6674583444SSamuel Benzaquen struct AllOpacity : EnumValuesAsTuple<AllOpacity, Opacity, 2> {
6774583444SSamuel Benzaquen static constexpr const char* Names[] = {"Opaque", "Transparent"};
6874583444SSamuel Benzaquen };
6974583444SSamuel Benzaquen
7074583444SSamuel Benzaquen enum class DiffType { Control, ChangeFirst, ChangeMiddle, ChangeLast };
7174583444SSamuel Benzaquen struct AllDiffTypes : EnumValuesAsTuple<AllDiffTypes, DiffType, 4> {
7274583444SSamuel Benzaquen static constexpr const char* Names[] = {"Control", "ChangeFirst",
7374583444SSamuel Benzaquen "ChangeMiddle", "ChangeLast"};
7474583444SSamuel Benzaquen };
7574583444SSamuel Benzaquen
7627a83e99SSamuel Benzaquen static constexpr char SmallStringLiteral[] = "012345678";
779b7aa02bSSamuel Benzaquen
getSmallString(DiffType D)7874583444SSamuel Benzaquen TEST_ALWAYS_INLINE const char* getSmallString(DiffType D) {
7974583444SSamuel Benzaquen switch (D) {
8074583444SSamuel Benzaquen case DiffType::Control:
8127a83e99SSamuel Benzaquen return SmallStringLiteral;
8274583444SSamuel Benzaquen case DiffType::ChangeFirst:
839b7aa02bSSamuel Benzaquen return "-12345678";
8474583444SSamuel Benzaquen case DiffType::ChangeMiddle:
859b7aa02bSSamuel Benzaquen return "0123-5678";
8674583444SSamuel Benzaquen case DiffType::ChangeLast:
879b7aa02bSSamuel Benzaquen return "01234567-";
8874583444SSamuel Benzaquen }
8974583444SSamuel Benzaquen }
9074583444SSamuel Benzaquen
9127a83e99SSamuel Benzaquen static constexpr char LargeStringLiteral[] =
9227a83e99SSamuel Benzaquen "012345678901234567890123456789012345678901234567890123456789012";
9327a83e99SSamuel Benzaquen
getLargeString(DiffType D)9474583444SSamuel Benzaquen TEST_ALWAYS_INLINE const char* getLargeString(DiffType D) {
9574583444SSamuel Benzaquen #define LARGE_STRING_FIRST "123456789012345678901234567890"
9674583444SSamuel Benzaquen #define LARGE_STRING_SECOND "234567890123456789012345678901"
9774583444SSamuel Benzaquen switch (D) {
9874583444SSamuel Benzaquen case DiffType::Control:
9974583444SSamuel Benzaquen return "0" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "2";
10074583444SSamuel Benzaquen case DiffType::ChangeFirst:
10174583444SSamuel Benzaquen return "-" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "2";
10274583444SSamuel Benzaquen case DiffType::ChangeMiddle:
10374583444SSamuel Benzaquen return "0" LARGE_STRING_FIRST "-" LARGE_STRING_SECOND "2";
10474583444SSamuel Benzaquen case DiffType::ChangeLast:
10574583444SSamuel Benzaquen return "0" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "-";
10674583444SSamuel Benzaquen }
10774583444SSamuel Benzaquen }
10874583444SSamuel Benzaquen
getHugeString(DiffType D)10974583444SSamuel Benzaquen TEST_ALWAYS_INLINE const char* getHugeString(DiffType D) {
11074583444SSamuel Benzaquen #define HUGE_STRING0 "0123456789"
11174583444SSamuel Benzaquen #define HUGE_STRING1 HUGE_STRING0 HUGE_STRING0 HUGE_STRING0 HUGE_STRING0
11274583444SSamuel Benzaquen #define HUGE_STRING2 HUGE_STRING1 HUGE_STRING1 HUGE_STRING1 HUGE_STRING1
11374583444SSamuel Benzaquen #define HUGE_STRING3 HUGE_STRING2 HUGE_STRING2 HUGE_STRING2 HUGE_STRING2
11474583444SSamuel Benzaquen #define HUGE_STRING4 HUGE_STRING3 HUGE_STRING3 HUGE_STRING3 HUGE_STRING3
11574583444SSamuel Benzaquen switch (D) {
11674583444SSamuel Benzaquen case DiffType::Control:
11774583444SSamuel Benzaquen return "0123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "0123456789";
11874583444SSamuel Benzaquen case DiffType::ChangeFirst:
11974583444SSamuel Benzaquen return "-123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "0123456789";
12074583444SSamuel Benzaquen case DiffType::ChangeMiddle:
12174583444SSamuel Benzaquen return "0123456789" HUGE_STRING4 "01234-6789" HUGE_STRING4 "0123456789";
12274583444SSamuel Benzaquen case DiffType::ChangeLast:
12374583444SSamuel Benzaquen return "0123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "012345678-";
12474583444SSamuel Benzaquen }
12574583444SSamuel Benzaquen }
12674583444SSamuel Benzaquen
getString(Length L,DiffType D=DiffType::Control)1270c5102bdSEric Fiselier TEST_ALWAYS_INLINE const char* getString(Length L,
1280c5102bdSEric Fiselier DiffType D = DiffType::Control) {
1290c5102bdSEric Fiselier switch (L) {
1300c5102bdSEric Fiselier case Length::Empty:
1310c5102bdSEric Fiselier return "";
1320c5102bdSEric Fiselier case Length::Small:
1330c5102bdSEric Fiselier return getSmallString(D);
1340c5102bdSEric Fiselier case Length::Large:
1350c5102bdSEric Fiselier return getLargeString(D);
1360c5102bdSEric Fiselier case Length::Huge:
1370c5102bdSEric Fiselier return getHugeString(D);
1380c5102bdSEric Fiselier }
1390c5102bdSEric Fiselier }
1400c5102bdSEric Fiselier
makeString(Length L,DiffType D=DiffType::Control,Opacity O=Opacity::Transparent)14174583444SSamuel Benzaquen TEST_ALWAYS_INLINE std::string makeString(Length L,
14274583444SSamuel Benzaquen DiffType D = DiffType::Control,
14374583444SSamuel Benzaquen Opacity O = Opacity::Transparent) {
14474583444SSamuel Benzaquen switch (L) {
14574583444SSamuel Benzaquen case Length::Empty:
14674583444SSamuel Benzaquen return maybeOpaque("", O == Opacity::Opaque);
14774583444SSamuel Benzaquen case Length::Small:
14874583444SSamuel Benzaquen return maybeOpaque(getSmallString(D), O == Opacity::Opaque);
14974583444SSamuel Benzaquen case Length::Large:
15074583444SSamuel Benzaquen return maybeOpaque(getLargeString(D), O == Opacity::Opaque);
15174583444SSamuel Benzaquen case Length::Huge:
15274583444SSamuel Benzaquen return maybeOpaque(getHugeString(D), O == Opacity::Opaque);
15374583444SSamuel Benzaquen }
15474583444SSamuel Benzaquen }
15574583444SSamuel Benzaquen
15674583444SSamuel Benzaquen template <class Length, class Opaque>
15774583444SSamuel Benzaquen struct StringConstructDestroyCStr {
runStringConstructDestroyCStr15874583444SSamuel Benzaquen static void run(benchmark::State& state) {
15974583444SSamuel Benzaquen for (auto _ : state) {
16074583444SSamuel Benzaquen benchmark::DoNotOptimize(
16174583444SSamuel Benzaquen makeString(Length(), DiffType::Control, Opaque()));
16274583444SSamuel Benzaquen }
16374583444SSamuel Benzaquen }
16474583444SSamuel Benzaquen
nameStringConstructDestroyCStr16574583444SSamuel Benzaquen static std::string name() {
16674583444SSamuel Benzaquen return "BM_StringConstructDestroyCStr" + Length::name() + Opaque::name();
16774583444SSamuel Benzaquen }
16874583444SSamuel Benzaquen };
16974583444SSamuel Benzaquen
17074583444SSamuel Benzaquen template <class Length, bool MeasureCopy, bool MeasureDestroy>
StringCopyAndDestroy(benchmark::State & state)17174583444SSamuel Benzaquen static void StringCopyAndDestroy(benchmark::State& state) {
17274583444SSamuel Benzaquen static constexpr size_t NumStrings = 1024;
17374583444SSamuel Benzaquen auto Orig = makeString(Length());
17474583444SSamuel Benzaquen std::aligned_storage<sizeof(std::string)>::type Storage[NumStrings];
17574583444SSamuel Benzaquen
17674583444SSamuel Benzaquen while (state.KeepRunningBatch(NumStrings)) {
17774583444SSamuel Benzaquen if (!MeasureCopy)
17874583444SSamuel Benzaquen state.PauseTiming();
17974583444SSamuel Benzaquen for (size_t I = 0; I < NumStrings; ++I) {
18074583444SSamuel Benzaquen ::new (static_cast<void*>(Storage + I)) std::string(Orig);
18174583444SSamuel Benzaquen }
18274583444SSamuel Benzaquen if (!MeasureCopy)
18374583444SSamuel Benzaquen state.ResumeTiming();
18474583444SSamuel Benzaquen if (!MeasureDestroy)
18574583444SSamuel Benzaquen state.PauseTiming();
18674583444SSamuel Benzaquen for (size_t I = 0; I < NumStrings; ++I) {
18774583444SSamuel Benzaquen using S = std::string;
18874583444SSamuel Benzaquen reinterpret_cast<S*>(Storage + I)->~S();
18974583444SSamuel Benzaquen }
19074583444SSamuel Benzaquen if (!MeasureDestroy)
19174583444SSamuel Benzaquen state.ResumeTiming();
19274583444SSamuel Benzaquen }
19374583444SSamuel Benzaquen }
19474583444SSamuel Benzaquen
19574583444SSamuel Benzaquen template <class Length>
19674583444SSamuel Benzaquen struct StringCopy {
runStringCopy19774583444SSamuel Benzaquen static void run(benchmark::State& state) {
19874583444SSamuel Benzaquen StringCopyAndDestroy<Length, true, false>(state);
19974583444SSamuel Benzaquen }
20074583444SSamuel Benzaquen
nameStringCopy20174583444SSamuel Benzaquen static std::string name() { return "BM_StringCopy" + Length::name(); }
20274583444SSamuel Benzaquen };
20374583444SSamuel Benzaquen
20474583444SSamuel Benzaquen template <class Length>
20574583444SSamuel Benzaquen struct StringDestroy {
runStringDestroy20674583444SSamuel Benzaquen static void run(benchmark::State& state) {
20774583444SSamuel Benzaquen StringCopyAndDestroy<Length, false, true>(state);
20874583444SSamuel Benzaquen }
20974583444SSamuel Benzaquen
nameStringDestroy21074583444SSamuel Benzaquen static std::string name() { return "BM_StringDestroy" + Length::name(); }
21174583444SSamuel Benzaquen };
21274583444SSamuel Benzaquen
21374583444SSamuel Benzaquen template <class Length>
21474583444SSamuel Benzaquen struct StringMove {
runStringMove21574583444SSamuel Benzaquen static void run(benchmark::State& state) {
21674583444SSamuel Benzaquen // Keep two object locations and move construct back and forth.
2173bea50aeSEric Fiselier std::aligned_storage<sizeof(std::string), alignof(std::string)>::type Storage[2];
21874583444SSamuel Benzaquen using S = std::string;
21974583444SSamuel Benzaquen size_t I = 0;
2203bea50aeSEric Fiselier S *newS = new (static_cast<void*>(Storage)) std::string(makeString(Length()));
22174583444SSamuel Benzaquen for (auto _ : state) {
22274583444SSamuel Benzaquen // Switch locations.
22374583444SSamuel Benzaquen I ^= 1;
22474583444SSamuel Benzaquen benchmark::DoNotOptimize(Storage);
22574583444SSamuel Benzaquen // Move construct into the new location,
2263bea50aeSEric Fiselier S *tmpS = new (static_cast<void*>(Storage + I)) S(std::move(*newS));
22774583444SSamuel Benzaquen // then destroy the old one.
2283bea50aeSEric Fiselier newS->~S();
2293bea50aeSEric Fiselier newS = tmpS;
23074583444SSamuel Benzaquen }
2313bea50aeSEric Fiselier newS->~S();
23274583444SSamuel Benzaquen }
23374583444SSamuel Benzaquen
nameStringMove23474583444SSamuel Benzaquen static std::string name() { return "BM_StringMove" + Length::name(); }
23574583444SSamuel Benzaquen };
23674583444SSamuel Benzaquen
2370c5102bdSEric Fiselier template <class Length, class Opaque>
2380c5102bdSEric Fiselier struct StringResizeDefaultInit {
runStringResizeDefaultInit2390c5102bdSEric Fiselier static void run(benchmark::State& state) {
2400c5102bdSEric Fiselier constexpr bool opaque = Opaque{} == Opacity::Opaque;
2410c5102bdSEric Fiselier constexpr int kNumStrings = 4 << 10;
2420c5102bdSEric Fiselier size_t length = makeString(Length()).size();
2430c5102bdSEric Fiselier std::string strings[kNumStrings];
2440c5102bdSEric Fiselier while (state.KeepRunningBatch(kNumStrings)) {
2450c5102bdSEric Fiselier state.PauseTiming();
2460c5102bdSEric Fiselier for (int i = 0; i < kNumStrings; ++i) {
2470c5102bdSEric Fiselier std::string().swap(strings[i]);
2480c5102bdSEric Fiselier }
2490c5102bdSEric Fiselier benchmark::DoNotOptimize(strings);
2500c5102bdSEric Fiselier state.ResumeTiming();
2510c5102bdSEric Fiselier for (int i = 0; i < kNumStrings; ++i) {
2520c5102bdSEric Fiselier strings[i].__resize_default_init(maybeOpaque(length, opaque));
2530c5102bdSEric Fiselier }
2540c5102bdSEric Fiselier }
2550c5102bdSEric Fiselier }
2560c5102bdSEric Fiselier
nameStringResizeDefaultInit2570c5102bdSEric Fiselier static std::string name() {
2580c5102bdSEric Fiselier return "BM_StringResizeDefaultInit" + Length::name() + Opaque::name();
2590c5102bdSEric Fiselier }
2600c5102bdSEric Fiselier };
2610c5102bdSEric Fiselier
2620c5102bdSEric Fiselier template <class Length, class Opaque>
2630c5102bdSEric Fiselier struct StringAssignStr {
runStringAssignStr2640c5102bdSEric Fiselier static void run(benchmark::State& state) {
2650c5102bdSEric Fiselier constexpr bool opaque = Opaque{} == Opacity::Opaque;
2660c5102bdSEric Fiselier constexpr int kNumStrings = 4 << 10;
2670c5102bdSEric Fiselier std::string src = makeString(Length());
2680c5102bdSEric Fiselier std::string strings[kNumStrings];
2690c5102bdSEric Fiselier while (state.KeepRunningBatch(kNumStrings)) {
2700c5102bdSEric Fiselier state.PauseTiming();
2710c5102bdSEric Fiselier for (int i = 0; i < kNumStrings; ++i) {
2720c5102bdSEric Fiselier std::string().swap(strings[i]);
2730c5102bdSEric Fiselier }
2740c5102bdSEric Fiselier benchmark::DoNotOptimize(strings);
2750c5102bdSEric Fiselier state.ResumeTiming();
2760c5102bdSEric Fiselier for (int i = 0; i < kNumStrings; ++i) {
2770c5102bdSEric Fiselier strings[i] = *maybeOpaque(&src, opaque);
2780c5102bdSEric Fiselier }
2790c5102bdSEric Fiselier }
2800c5102bdSEric Fiselier }
2810c5102bdSEric Fiselier
nameStringAssignStr2820c5102bdSEric Fiselier static std::string name() {
2830c5102bdSEric Fiselier return "BM_StringAssignStr" + Length::name() + Opaque::name();
2840c5102bdSEric Fiselier }
2850c5102bdSEric Fiselier };
2860c5102bdSEric Fiselier
2870c5102bdSEric Fiselier template <class Length, class Opaque>
2880c5102bdSEric Fiselier struct StringAssignAsciiz {
runStringAssignAsciiz2890c5102bdSEric Fiselier static void run(benchmark::State& state) {
2900c5102bdSEric Fiselier constexpr bool opaque = Opaque{} == Opacity::Opaque;
2910c5102bdSEric Fiselier constexpr int kNumStrings = 4 << 10;
2920c5102bdSEric Fiselier std::string strings[kNumStrings];
2930c5102bdSEric Fiselier while (state.KeepRunningBatch(kNumStrings)) {
2940c5102bdSEric Fiselier state.PauseTiming();
2950c5102bdSEric Fiselier for (int i = 0; i < kNumStrings; ++i) {
2960c5102bdSEric Fiselier std::string().swap(strings[i]);
2970c5102bdSEric Fiselier }
2980c5102bdSEric Fiselier benchmark::DoNotOptimize(strings);
2990c5102bdSEric Fiselier state.ResumeTiming();
3000c5102bdSEric Fiselier for (int i = 0; i < kNumStrings; ++i) {
3010c5102bdSEric Fiselier strings[i] = maybeOpaque(getString(Length()), opaque);
3020c5102bdSEric Fiselier }
3030c5102bdSEric Fiselier }
3040c5102bdSEric Fiselier }
3050c5102bdSEric Fiselier
nameStringAssignAsciiz3060c5102bdSEric Fiselier static std::string name() {
3070c5102bdSEric Fiselier return "BM_StringAssignAsciiz" + Length::name() + Opaque::name();
3080c5102bdSEric Fiselier }
3090c5102bdSEric Fiselier };
3100c5102bdSEric Fiselier
311*c4b8c3ddSMartijn Vels template <class Length, class Opaque>
312*c4b8c3ddSMartijn Vels struct StringEraseToEnd {
runStringEraseToEnd313*c4b8c3ddSMartijn Vels static void run(benchmark::State& state) {
314*c4b8c3ddSMartijn Vels constexpr bool opaque = Opaque{} == Opacity::Opaque;
315*c4b8c3ddSMartijn Vels constexpr int kNumStrings = 4 << 10;
316*c4b8c3ddSMartijn Vels std::string strings[kNumStrings];
317*c4b8c3ddSMartijn Vels const int mid = makeString(Length()).size() / 2;
318*c4b8c3ddSMartijn Vels while (state.KeepRunningBatch(kNumStrings)) {
319*c4b8c3ddSMartijn Vels state.PauseTiming();
320*c4b8c3ddSMartijn Vels for (int i = 0; i < kNumStrings; ++i) {
321*c4b8c3ddSMartijn Vels strings[i] = makeString(Length());
322*c4b8c3ddSMartijn Vels }
323*c4b8c3ddSMartijn Vels benchmark::DoNotOptimize(strings);
324*c4b8c3ddSMartijn Vels state.ResumeTiming();
325*c4b8c3ddSMartijn Vels for (int i = 0; i < kNumStrings; ++i) {
326*c4b8c3ddSMartijn Vels strings[i].erase(maybeOpaque(mid, opaque),
327*c4b8c3ddSMartijn Vels maybeOpaque(std::string::npos, opaque));
328*c4b8c3ddSMartijn Vels }
329*c4b8c3ddSMartijn Vels }
330*c4b8c3ddSMartijn Vels }
331*c4b8c3ddSMartijn Vels
nameStringEraseToEnd332*c4b8c3ddSMartijn Vels static std::string name() {
333*c4b8c3ddSMartijn Vels return "BM_StringEraseToEnd" + Length::name() + Opaque::name();
334*c4b8c3ddSMartijn Vels }
335*c4b8c3ddSMartijn Vels };
336*c4b8c3ddSMartijn Vels
337*c4b8c3ddSMartijn Vels template <class Length, class Opaque>
338*c4b8c3ddSMartijn Vels struct StringEraseWithMove {
runStringEraseWithMove339*c4b8c3ddSMartijn Vels static void run(benchmark::State& state) {
340*c4b8c3ddSMartijn Vels constexpr bool opaque = Opaque{} == Opacity::Opaque;
341*c4b8c3ddSMartijn Vels constexpr int kNumStrings = 4 << 10;
342*c4b8c3ddSMartijn Vels std::string strings[kNumStrings];
343*c4b8c3ddSMartijn Vels const int n = makeString(Length()).size() / 2;
344*c4b8c3ddSMartijn Vels const int pos = n / 2;
345*c4b8c3ddSMartijn Vels while (state.KeepRunningBatch(kNumStrings)) {
346*c4b8c3ddSMartijn Vels state.PauseTiming();
347*c4b8c3ddSMartijn Vels for (int i = 0; i < kNumStrings; ++i) {
348*c4b8c3ddSMartijn Vels strings[i] = makeString(Length());
349*c4b8c3ddSMartijn Vels }
350*c4b8c3ddSMartijn Vels benchmark::DoNotOptimize(strings);
351*c4b8c3ddSMartijn Vels state.ResumeTiming();
352*c4b8c3ddSMartijn Vels for (int i = 0; i < kNumStrings; ++i) {
353*c4b8c3ddSMartijn Vels strings[i].erase(maybeOpaque(pos, opaque), maybeOpaque(n, opaque));
354*c4b8c3ddSMartijn Vels }
355*c4b8c3ddSMartijn Vels }
356*c4b8c3ddSMartijn Vels }
357*c4b8c3ddSMartijn Vels
nameStringEraseWithMove358*c4b8c3ddSMartijn Vels static std::string name() {
359*c4b8c3ddSMartijn Vels return "BM_StringEraseWithMove" + Length::name() + Opaque::name();
360*c4b8c3ddSMartijn Vels }
361*c4b8c3ddSMartijn Vels };
362*c4b8c3ddSMartijn Vels
3630c5102bdSEric Fiselier template <class Opaque>
3640c5102bdSEric Fiselier struct StringAssignAsciizMix {
runStringAssignAsciizMix3650c5102bdSEric Fiselier static void run(benchmark::State& state) {
3660c5102bdSEric Fiselier constexpr auto O = Opaque{};
3670c5102bdSEric Fiselier constexpr auto D = DiffType::Control;
3680c5102bdSEric Fiselier constexpr int kNumStrings = 4 << 10;
3690c5102bdSEric Fiselier std::string strings[kNumStrings];
3700c5102bdSEric Fiselier while (state.KeepRunningBatch(kNumStrings)) {
3710c5102bdSEric Fiselier state.PauseTiming();
3720c5102bdSEric Fiselier for (int i = 0; i < kNumStrings; ++i) {
3730c5102bdSEric Fiselier std::string().swap(strings[i]);
3740c5102bdSEric Fiselier }
3750c5102bdSEric Fiselier benchmark::DoNotOptimize(strings);
3760c5102bdSEric Fiselier state.ResumeTiming();
3770c5102bdSEric Fiselier for (int i = 0; i < kNumStrings - 7; i += 8) {
3780c5102bdSEric Fiselier strings[i + 0] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
3790c5102bdSEric Fiselier strings[i + 1] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
3800c5102bdSEric Fiselier strings[i + 2] = maybeOpaque(getLargeString(D), O == Opacity::Opaque);
3810c5102bdSEric Fiselier strings[i + 3] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
3820c5102bdSEric Fiselier strings[i + 4] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
3830c5102bdSEric Fiselier strings[i + 5] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
3840c5102bdSEric Fiselier strings[i + 6] = maybeOpaque(getLargeString(D), O == Opacity::Opaque);
3850c5102bdSEric Fiselier strings[i + 7] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
3860c5102bdSEric Fiselier }
3870c5102bdSEric Fiselier }
3880c5102bdSEric Fiselier }
3890c5102bdSEric Fiselier
nameStringAssignAsciizMix3900c5102bdSEric Fiselier static std::string name() {
3910c5102bdSEric Fiselier return "BM_StringAssignAsciizMix" + Opaque::name();
3920c5102bdSEric Fiselier }
3930c5102bdSEric Fiselier };
3940c5102bdSEric Fiselier
39574583444SSamuel Benzaquen enum class Relation { Eq, Less, Compare };
39674583444SSamuel Benzaquen struct AllRelations : EnumValuesAsTuple<AllRelations, Relation, 3> {
39774583444SSamuel Benzaquen static constexpr const char* Names[] = {"Eq", "Less", "Compare"};
39874583444SSamuel Benzaquen };
39974583444SSamuel Benzaquen
40074583444SSamuel Benzaquen template <class Rel, class LHLength, class RHLength, class DiffType>
40174583444SSamuel Benzaquen struct StringRelational {
runStringRelational40274583444SSamuel Benzaquen static void run(benchmark::State& state) {
40374583444SSamuel Benzaquen auto Lhs = makeString(RHLength());
40474583444SSamuel Benzaquen auto Rhs = makeString(LHLength(), DiffType());
40574583444SSamuel Benzaquen for (auto _ : state) {
40674583444SSamuel Benzaquen benchmark::DoNotOptimize(Lhs);
40774583444SSamuel Benzaquen benchmark::DoNotOptimize(Rhs);
40874583444SSamuel Benzaquen switch (Rel()) {
40974583444SSamuel Benzaquen case Relation::Eq:
41074583444SSamuel Benzaquen benchmark::DoNotOptimize(Lhs == Rhs);
41174583444SSamuel Benzaquen break;
41274583444SSamuel Benzaquen case Relation::Less:
41374583444SSamuel Benzaquen benchmark::DoNotOptimize(Lhs < Rhs);
41474583444SSamuel Benzaquen break;
41574583444SSamuel Benzaquen case Relation::Compare:
41674583444SSamuel Benzaquen benchmark::DoNotOptimize(Lhs.compare(Rhs));
41774583444SSamuel Benzaquen break;
41874583444SSamuel Benzaquen }
41974583444SSamuel Benzaquen }
42074583444SSamuel Benzaquen }
42174583444SSamuel Benzaquen
skipStringRelational42274583444SSamuel Benzaquen static bool skip() {
42374583444SSamuel Benzaquen // Eq is commutative, so skip half the matrix.
42474583444SSamuel Benzaquen if (Rel() == Relation::Eq && LHLength() > RHLength())
42574583444SSamuel Benzaquen return true;
42674583444SSamuel Benzaquen // We only care about control when the lengths differ.
42774583444SSamuel Benzaquen if (LHLength() != RHLength() && DiffType() != ::DiffType::Control)
42874583444SSamuel Benzaquen return true;
42974583444SSamuel Benzaquen // For empty, only control matters.
43074583444SSamuel Benzaquen if (LHLength() == Length::Empty && DiffType() != ::DiffType::Control)
43174583444SSamuel Benzaquen return true;
43274583444SSamuel Benzaquen return false;
43374583444SSamuel Benzaquen }
43474583444SSamuel Benzaquen
nameStringRelational43574583444SSamuel Benzaquen static std::string name() {
43674583444SSamuel Benzaquen return "BM_StringRelational" + Rel::name() + LHLength::name() +
43774583444SSamuel Benzaquen RHLength::name() + DiffType::name();
43874583444SSamuel Benzaquen }
43974583444SSamuel Benzaquen };
44074583444SSamuel Benzaquen
44127a83e99SSamuel Benzaquen template <class Rel, class LHLength, class RHLength, class DiffType>
4429b7aa02bSSamuel Benzaquen struct StringRelationalLiteral {
runStringRelationalLiteral4439b7aa02bSSamuel Benzaquen static void run(benchmark::State& state) {
4449b7aa02bSSamuel Benzaquen auto Lhs = makeString(LHLength(), DiffType());
4459b7aa02bSSamuel Benzaquen for (auto _ : state) {
4469b7aa02bSSamuel Benzaquen benchmark::DoNotOptimize(Lhs);
44727a83e99SSamuel Benzaquen constexpr const char* Literal = RHLength::value == Length::Empty
44827a83e99SSamuel Benzaquen ? ""
44927a83e99SSamuel Benzaquen : RHLength::value == Length::Small
45027a83e99SSamuel Benzaquen ? SmallStringLiteral
45127a83e99SSamuel Benzaquen : LargeStringLiteral;
4529b7aa02bSSamuel Benzaquen switch (Rel()) {
4539b7aa02bSSamuel Benzaquen case Relation::Eq:
45427a83e99SSamuel Benzaquen benchmark::DoNotOptimize(Lhs == Literal);
4559b7aa02bSSamuel Benzaquen break;
4569b7aa02bSSamuel Benzaquen case Relation::Less:
45727a83e99SSamuel Benzaquen benchmark::DoNotOptimize(Lhs < Literal);
4589b7aa02bSSamuel Benzaquen break;
4599b7aa02bSSamuel Benzaquen case Relation::Compare:
46027a83e99SSamuel Benzaquen benchmark::DoNotOptimize(Lhs.compare(Literal));
4619b7aa02bSSamuel Benzaquen break;
4629b7aa02bSSamuel Benzaquen }
4639b7aa02bSSamuel Benzaquen }
4649b7aa02bSSamuel Benzaquen }
4659b7aa02bSSamuel Benzaquen
skipStringRelationalLiteral4669b7aa02bSSamuel Benzaquen static bool skip() {
4679b7aa02bSSamuel Benzaquen // Doesn't matter how they differ if they have different size.
46827a83e99SSamuel Benzaquen if (LHLength() != RHLength() && DiffType() != ::DiffType::Control)
4699b7aa02bSSamuel Benzaquen return true;
4709b7aa02bSSamuel Benzaquen // We don't need huge. Doensn't give anything different than Large.
47127a83e99SSamuel Benzaquen if (LHLength() == Length::Huge || RHLength() == Length::Huge)
4729b7aa02bSSamuel Benzaquen return true;
4739b7aa02bSSamuel Benzaquen return false;
4749b7aa02bSSamuel Benzaquen }
4759b7aa02bSSamuel Benzaquen
nameStringRelationalLiteral4769b7aa02bSSamuel Benzaquen static std::string name() {
4779b7aa02bSSamuel Benzaquen return "BM_StringRelationalLiteral" + Rel::name() + LHLength::name() +
47827a83e99SSamuel Benzaquen RHLength::name() + DiffType::name();
4799b7aa02bSSamuel Benzaquen }
4809b7aa02bSSamuel Benzaquen };
4819b7aa02bSSamuel Benzaquen
48274583444SSamuel Benzaquen enum class Depth { Shallow, Deep };
48374583444SSamuel Benzaquen struct AllDepths : EnumValuesAsTuple<AllDepths, Depth, 2> {
48474583444SSamuel Benzaquen static constexpr const char* Names[] = {"Shallow", "Deep"};
48574583444SSamuel Benzaquen };
48674583444SSamuel Benzaquen
48774583444SSamuel Benzaquen enum class Temperature { Hot, Cold };
48874583444SSamuel Benzaquen struct AllTemperatures : EnumValuesAsTuple<AllTemperatures, Temperature, 2> {
48974583444SSamuel Benzaquen static constexpr const char* Names[] = {"Hot", "Cold"};
49074583444SSamuel Benzaquen };
49174583444SSamuel Benzaquen
49274583444SSamuel Benzaquen template <class Temperature, class Depth, class Length>
49374583444SSamuel Benzaquen struct StringRead {
runStringRead49474583444SSamuel Benzaquen void run(benchmark::State& state) const {
49574583444SSamuel Benzaquen static constexpr size_t NumStrings =
49674583444SSamuel Benzaquen Temperature() == ::Temperature::Hot
49774583444SSamuel Benzaquen ? 1 << 10
49874583444SSamuel Benzaquen : /* Enough strings to overflow the cache */ 1 << 20;
49974583444SSamuel Benzaquen static_assert((NumStrings & (NumStrings - 1)) == 0,
50074583444SSamuel Benzaquen "NumStrings should be a power of two to reduce overhead.");
50174583444SSamuel Benzaquen
50274583444SSamuel Benzaquen std::vector<std::string> Values(NumStrings, makeString(Length()));
50374583444SSamuel Benzaquen size_t I = 0;
50474583444SSamuel Benzaquen for (auto _ : state) {
50574583444SSamuel Benzaquen // Jump long enough to defeat cache locality, and use a value that is
50674583444SSamuel Benzaquen // coprime with NumStrings to ensure we visit every element.
50774583444SSamuel Benzaquen I = (I + 17) % NumStrings;
50874583444SSamuel Benzaquen const auto& V = Values[I];
50974583444SSamuel Benzaquen
51074583444SSamuel Benzaquen // Read everything first. Escaping data() through DoNotOptimize might
51174583444SSamuel Benzaquen // cause the compiler to have to recalculate information about `V` due to
51274583444SSamuel Benzaquen // aliasing.
51374583444SSamuel Benzaquen const char* const Data = V.data();
51474583444SSamuel Benzaquen const size_t Size = V.size();
51574583444SSamuel Benzaquen benchmark::DoNotOptimize(Data);
51674583444SSamuel Benzaquen benchmark::DoNotOptimize(Size);
51774583444SSamuel Benzaquen if (Depth() == ::Depth::Deep) {
51874583444SSamuel Benzaquen // Read into the payload. This mainly shows the benefit of SSO when the
51974583444SSamuel Benzaquen // data is cold.
52074583444SSamuel Benzaquen benchmark::DoNotOptimize(*Data);
52174583444SSamuel Benzaquen }
52274583444SSamuel Benzaquen }
52374583444SSamuel Benzaquen }
52474583444SSamuel Benzaquen
skipStringRead52574583444SSamuel Benzaquen static bool skip() {
52674583444SSamuel Benzaquen // Huge does not give us anything that Large doesn't have. Skip it.
52774583444SSamuel Benzaquen if (Length() == ::Length::Huge) {
52874583444SSamuel Benzaquen return true;
52974583444SSamuel Benzaquen }
53074583444SSamuel Benzaquen return false;
53174583444SSamuel Benzaquen }
53274583444SSamuel Benzaquen
nameStringRead53374583444SSamuel Benzaquen std::string name() const {
53474583444SSamuel Benzaquen return "BM_StringRead" + Temperature::name() + Depth::name() +
53574583444SSamuel Benzaquen Length::name();
53674583444SSamuel Benzaquen }
53774583444SSamuel Benzaquen };
53874583444SSamuel Benzaquen
sanityCheckGeneratedStrings()53974583444SSamuel Benzaquen void sanityCheckGeneratedStrings() {
54074583444SSamuel Benzaquen for (auto Lhs : {Length::Empty, Length::Small, Length::Large, Length::Huge}) {
54174583444SSamuel Benzaquen const auto LhsString = makeString(Lhs);
54274583444SSamuel Benzaquen for (auto Rhs :
54374583444SSamuel Benzaquen {Length::Empty, Length::Small, Length::Large, Length::Huge}) {
54474583444SSamuel Benzaquen if (Lhs > Rhs)
54574583444SSamuel Benzaquen continue;
54674583444SSamuel Benzaquen const auto RhsString = makeString(Rhs);
54774583444SSamuel Benzaquen
54874583444SSamuel Benzaquen // The smaller one must be a prefix of the larger one.
54974583444SSamuel Benzaquen if (RhsString.find(LhsString) != 0) {
55074583444SSamuel Benzaquen fprintf(stderr, "Invalid autogenerated strings for sizes (%d,%d).\n",
55174583444SSamuel Benzaquen static_cast<int>(Lhs), static_cast<int>(Rhs));
55274583444SSamuel Benzaquen std::abort();
55374583444SSamuel Benzaquen }
55474583444SSamuel Benzaquen }
55574583444SSamuel Benzaquen }
55674583444SSamuel Benzaquen // Verify the autogenerated diffs
55774583444SSamuel Benzaquen for (auto L : {Length::Small, Length::Large, Length::Huge}) {
55874583444SSamuel Benzaquen const auto Control = makeString(L);
55974583444SSamuel Benzaquen const auto Verify = [&](std::string Exp, size_t Pos) {
56074583444SSamuel Benzaquen // Only change on the Pos char.
56174583444SSamuel Benzaquen if (Control[Pos] != Exp[Pos]) {
56274583444SSamuel Benzaquen Exp[Pos] = Control[Pos];
56374583444SSamuel Benzaquen if (Control == Exp)
56474583444SSamuel Benzaquen return;
56574583444SSamuel Benzaquen }
56674583444SSamuel Benzaquen fprintf(stderr, "Invalid autogenerated diff with size %d\n",
56774583444SSamuel Benzaquen static_cast<int>(L));
56874583444SSamuel Benzaquen std::abort();
56974583444SSamuel Benzaquen };
57074583444SSamuel Benzaquen Verify(makeString(L, DiffType::ChangeFirst), 0);
57174583444SSamuel Benzaquen Verify(makeString(L, DiffType::ChangeMiddle), Control.size() / 2);
57274583444SSamuel Benzaquen Verify(makeString(L, DiffType::ChangeLast), Control.size() - 1);
57374583444SSamuel Benzaquen }
57474583444SSamuel Benzaquen }
57574583444SSamuel Benzaquen
57627a83e99SSamuel Benzaquen // Some small codegen thunks to easily see generated code.
StringEqString(const std::string & a,const std::string & b)57727a83e99SSamuel Benzaquen bool StringEqString(const std::string& a, const std::string& b) {
57827a83e99SSamuel Benzaquen return a == b;
57927a83e99SSamuel Benzaquen }
StringEqCStr(const std::string & a,const char * b)58027a83e99SSamuel Benzaquen bool StringEqCStr(const std::string& a, const char* b) { return a == b; }
CStrEqString(const char * a,const std::string & b)58127a83e99SSamuel Benzaquen bool CStrEqString(const char* a, const std::string& b) { return a == b; }
StringEqCStrLiteralEmpty(const std::string & a)58227a83e99SSamuel Benzaquen bool StringEqCStrLiteralEmpty(const std::string& a) {
58327a83e99SSamuel Benzaquen return a == "";
58427a83e99SSamuel Benzaquen }
StringEqCStrLiteralSmall(const std::string & a)58527a83e99SSamuel Benzaquen bool StringEqCStrLiteralSmall(const std::string& a) {
58627a83e99SSamuel Benzaquen return a == SmallStringLiteral;
58727a83e99SSamuel Benzaquen }
StringEqCStrLiteralLarge(const std::string & a)58827a83e99SSamuel Benzaquen bool StringEqCStrLiteralLarge(const std::string& a) {
58927a83e99SSamuel Benzaquen return a == LargeStringLiteral;
59027a83e99SSamuel Benzaquen }
59127a83e99SSamuel Benzaquen
main(int argc,char ** argv)59274583444SSamuel Benzaquen int main(int argc, char** argv) {
59374583444SSamuel Benzaquen benchmark::Initialize(&argc, argv);
59474583444SSamuel Benzaquen if (benchmark::ReportUnrecognizedArguments(argc, argv))
59574583444SSamuel Benzaquen return 1;
59674583444SSamuel Benzaquen
59774583444SSamuel Benzaquen sanityCheckGeneratedStrings();
59874583444SSamuel Benzaquen
59974583444SSamuel Benzaquen makeCartesianProductBenchmark<StringConstructDestroyCStr, AllLengths,
60074583444SSamuel Benzaquen AllOpacity>();
6010c5102bdSEric Fiselier
6020c5102bdSEric Fiselier makeCartesianProductBenchmark<StringAssignStr, AllLengths, AllOpacity>();
6030c5102bdSEric Fiselier makeCartesianProductBenchmark<StringAssignAsciiz, AllLengths, AllOpacity>();
6040c5102bdSEric Fiselier makeCartesianProductBenchmark<StringAssignAsciizMix, AllOpacity>();
6050c5102bdSEric Fiselier
60674583444SSamuel Benzaquen makeCartesianProductBenchmark<StringCopy, AllLengths>();
60774583444SSamuel Benzaquen makeCartesianProductBenchmark<StringMove, AllLengths>();
60874583444SSamuel Benzaquen makeCartesianProductBenchmark<StringDestroy, AllLengths>();
6090c5102bdSEric Fiselier makeCartesianProductBenchmark<StringResizeDefaultInit, AllLengths,
6100c5102bdSEric Fiselier AllOpacity>();
611*c4b8c3ddSMartijn Vels makeCartesianProductBenchmark<StringEraseToEnd, AllLengths, AllOpacity>();
612*c4b8c3ddSMartijn Vels makeCartesianProductBenchmark<StringEraseWithMove, AllLengths, AllOpacity>();
61374583444SSamuel Benzaquen makeCartesianProductBenchmark<StringRelational, AllRelations, AllLengths,
61474583444SSamuel Benzaquen AllLengths, AllDiffTypes>();
6159b7aa02bSSamuel Benzaquen makeCartesianProductBenchmark<StringRelationalLiteral, AllRelations,
61627a83e99SSamuel Benzaquen AllLengths, AllLengths, AllDiffTypes>();
61774583444SSamuel Benzaquen makeCartesianProductBenchmark<StringRead, AllTemperatures, AllDepths,
61874583444SSamuel Benzaquen AllLengths>();
61974583444SSamuel Benzaquen benchmark::RunSpecifiedBenchmarks();
62027a83e99SSamuel Benzaquen
62127a83e99SSamuel Benzaquen if (argc < 0) {
62227a83e99SSamuel Benzaquen // ODR-use the functions to force them being generated in the binary.
62327a83e99SSamuel Benzaquen auto functions = std::make_tuple(
62427a83e99SSamuel Benzaquen StringEqString, StringEqCStr, CStrEqString, StringEqCStrLiteralEmpty,
62527a83e99SSamuel Benzaquen StringEqCStrLiteralSmall, StringEqCStrLiteralLarge);
62627a83e99SSamuel Benzaquen printf("%p", &functions);
62727a83e99SSamuel Benzaquen }
62874583444SSamuel Benzaquen }
629