2018-10-30 23:54:22 +08:00
|
|
|
|
2016-12-31 02:01:36 +08:00
|
|
|
#include <cstdint>
|
2018-10-30 23:54:22 +08:00
|
|
|
#include <new>
|
|
|
|
#include <vector>
|
2016-12-31 02:01:36 +08:00
|
|
|
|
2019-08-21 09:59:12 +08:00
|
|
|
#include "CartesianBenchmarks.h"
|
|
|
|
#include "GenerateInput.h"
|
2018-10-30 23:54:22 +08:00
|
|
|
#include "benchmark/benchmark.h"
|
|
|
|
#include "test_macros.h"
|
2016-12-31 02:01:36 +08:00
|
|
|
|
|
|
|
constexpr std::size_t MAX_STRING_LEN = 8 << 14;
|
|
|
|
|
|
|
|
// Benchmark when there is no match.
|
|
|
|
static void BM_StringFindNoMatch(benchmark::State &state) {
|
|
|
|
std::string s1(state.range(0), '-');
|
|
|
|
std::string s2(8, '*');
|
2018-10-30 23:54:22 +08:00
|
|
|
for (auto _ : state)
|
2016-12-31 02:01:36 +08:00
|
|
|
benchmark::DoNotOptimize(s1.find(s2));
|
|
|
|
}
|
|
|
|
BENCHMARK(BM_StringFindNoMatch)->Range(10, MAX_STRING_LEN);
|
|
|
|
|
|
|
|
// Benchmark when the string matches first time.
|
|
|
|
static void BM_StringFindAllMatch(benchmark::State &state) {
|
|
|
|
std::string s1(MAX_STRING_LEN, '-');
|
|
|
|
std::string s2(state.range(0), '-');
|
2018-10-30 23:54:22 +08:00
|
|
|
for (auto _ : state)
|
2016-12-31 02:01:36 +08:00
|
|
|
benchmark::DoNotOptimize(s1.find(s2));
|
|
|
|
}
|
|
|
|
BENCHMARK(BM_StringFindAllMatch)->Range(1, MAX_STRING_LEN);
|
|
|
|
|
|
|
|
// Benchmark when the string matches somewhere in the end.
|
|
|
|
static void BM_StringFindMatch1(benchmark::State &state) {
|
|
|
|
std::string s1(MAX_STRING_LEN / 2, '*');
|
|
|
|
s1 += std::string(state.range(0), '-');
|
|
|
|
std::string s2(state.range(0), '-');
|
2018-10-30 23:54:22 +08:00
|
|
|
for (auto _ : state)
|
2016-12-31 02:01:36 +08:00
|
|
|
benchmark::DoNotOptimize(s1.find(s2));
|
|
|
|
}
|
|
|
|
BENCHMARK(BM_StringFindMatch1)->Range(1, MAX_STRING_LEN / 4);
|
|
|
|
|
|
|
|
// Benchmark when the string matches somewhere from middle to the end.
|
|
|
|
static void BM_StringFindMatch2(benchmark::State &state) {
|
|
|
|
std::string s1(MAX_STRING_LEN / 2, '*');
|
|
|
|
s1 += std::string(state.range(0), '-');
|
|
|
|
s1 += std::string(state.range(0), '*');
|
|
|
|
std::string s2(state.range(0), '-');
|
2018-10-30 23:54:22 +08:00
|
|
|
for (auto _ : state)
|
2016-12-31 02:01:36 +08:00
|
|
|
benchmark::DoNotOptimize(s1.find(s2));
|
|
|
|
}
|
|
|
|
BENCHMARK(BM_StringFindMatch2)->Range(1, MAX_STRING_LEN / 4);
|
|
|
|
|
2018-07-10 12:11:22 +08:00
|
|
|
static void BM_StringCtorDefault(benchmark::State &state) {
|
2018-10-30 23:54:22 +08:00
|
|
|
for (auto _ : state) {
|
|
|
|
std::string Default;
|
|
|
|
benchmark::DoNotOptimize(Default);
|
2018-07-10 12:11:22 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
BENCHMARK(BM_StringCtorDefault);
|
|
|
|
|
2018-10-30 23:54:22 +08:00
|
|
|
enum class Length { Empty, Small, Large, Huge };
|
|
|
|
struct AllLengths : EnumValuesAsTuple<AllLengths, Length, 4> {
|
|
|
|
static constexpr const char* Names[] = {"Empty", "Small", "Large", "Huge"};
|
|
|
|
};
|
|
|
|
|
|
|
|
enum class Opacity { Opaque, Transparent };
|
|
|
|
struct AllOpacity : EnumValuesAsTuple<AllOpacity, Opacity, 2> {
|
|
|
|
static constexpr const char* Names[] = {"Opaque", "Transparent"};
|
|
|
|
};
|
|
|
|
|
|
|
|
enum class DiffType { Control, ChangeFirst, ChangeMiddle, ChangeLast };
|
|
|
|
struct AllDiffTypes : EnumValuesAsTuple<AllDiffTypes, DiffType, 4> {
|
|
|
|
static constexpr const char* Names[] = {"Control", "ChangeFirst",
|
|
|
|
"ChangeMiddle", "ChangeLast"};
|
|
|
|
};
|
|
|
|
|
2019-04-04 01:40:51 +08:00
|
|
|
static constexpr char SmallStringLiteral[] = "012345678";
|
2019-03-22 00:06:15 +08:00
|
|
|
|
2018-10-30 23:54:22 +08:00
|
|
|
TEST_ALWAYS_INLINE const char* getSmallString(DiffType D) {
|
|
|
|
switch (D) {
|
|
|
|
case DiffType::Control:
|
2019-04-04 01:40:51 +08:00
|
|
|
return SmallStringLiteral;
|
2018-10-30 23:54:22 +08:00
|
|
|
case DiffType::ChangeFirst:
|
2019-03-22 00:06:15 +08:00
|
|
|
return "-12345678";
|
2018-10-30 23:54:22 +08:00
|
|
|
case DiffType::ChangeMiddle:
|
2019-03-22 00:06:15 +08:00
|
|
|
return "0123-5678";
|
2018-10-30 23:54:22 +08:00
|
|
|
case DiffType::ChangeLast:
|
2019-03-22 00:06:15 +08:00
|
|
|
return "01234567-";
|
2018-10-30 23:54:22 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-04 01:40:51 +08:00
|
|
|
static constexpr char LargeStringLiteral[] =
|
|
|
|
"012345678901234567890123456789012345678901234567890123456789012";
|
|
|
|
|
2018-10-30 23:54:22 +08:00
|
|
|
TEST_ALWAYS_INLINE const char* getLargeString(DiffType D) {
|
|
|
|
#define LARGE_STRING_FIRST "123456789012345678901234567890"
|
|
|
|
#define LARGE_STRING_SECOND "234567890123456789012345678901"
|
|
|
|
switch (D) {
|
|
|
|
case DiffType::Control:
|
|
|
|
return "0" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "2";
|
|
|
|
case DiffType::ChangeFirst:
|
|
|
|
return "-" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "2";
|
|
|
|
case DiffType::ChangeMiddle:
|
|
|
|
return "0" LARGE_STRING_FIRST "-" LARGE_STRING_SECOND "2";
|
|
|
|
case DiffType::ChangeLast:
|
|
|
|
return "0" LARGE_STRING_FIRST "1" LARGE_STRING_SECOND "-";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_ALWAYS_INLINE const char* getHugeString(DiffType D) {
|
|
|
|
#define HUGE_STRING0 "0123456789"
|
|
|
|
#define HUGE_STRING1 HUGE_STRING0 HUGE_STRING0 HUGE_STRING0 HUGE_STRING0
|
|
|
|
#define HUGE_STRING2 HUGE_STRING1 HUGE_STRING1 HUGE_STRING1 HUGE_STRING1
|
|
|
|
#define HUGE_STRING3 HUGE_STRING2 HUGE_STRING2 HUGE_STRING2 HUGE_STRING2
|
|
|
|
#define HUGE_STRING4 HUGE_STRING3 HUGE_STRING3 HUGE_STRING3 HUGE_STRING3
|
|
|
|
switch (D) {
|
|
|
|
case DiffType::Control:
|
|
|
|
return "0123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "0123456789";
|
|
|
|
case DiffType::ChangeFirst:
|
|
|
|
return "-123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "0123456789";
|
|
|
|
case DiffType::ChangeMiddle:
|
|
|
|
return "0123456789" HUGE_STRING4 "01234-6789" HUGE_STRING4 "0123456789";
|
|
|
|
case DiffType::ChangeLast:
|
|
|
|
return "0123456789" HUGE_STRING4 "0123456789" HUGE_STRING4 "012345678-";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-08 05:31:06 +08:00
|
|
|
TEST_ALWAYS_INLINE const char* getString(Length L,
|
|
|
|
DiffType D = DiffType::Control) {
|
|
|
|
switch (L) {
|
|
|
|
case Length::Empty:
|
|
|
|
return "";
|
|
|
|
case Length::Small:
|
|
|
|
return getSmallString(D);
|
|
|
|
case Length::Large:
|
|
|
|
return getLargeString(D);
|
|
|
|
case Length::Huge:
|
|
|
|
return getHugeString(D);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-30 23:54:22 +08:00
|
|
|
TEST_ALWAYS_INLINE std::string makeString(Length L,
|
|
|
|
DiffType D = DiffType::Control,
|
|
|
|
Opacity O = Opacity::Transparent) {
|
|
|
|
switch (L) {
|
|
|
|
case Length::Empty:
|
|
|
|
return maybeOpaque("", O == Opacity::Opaque);
|
|
|
|
case Length::Small:
|
|
|
|
return maybeOpaque(getSmallString(D), O == Opacity::Opaque);
|
|
|
|
case Length::Large:
|
|
|
|
return maybeOpaque(getLargeString(D), O == Opacity::Opaque);
|
|
|
|
case Length::Huge:
|
|
|
|
return maybeOpaque(getHugeString(D), O == Opacity::Opaque);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class Length, class Opaque>
|
|
|
|
struct StringConstructDestroyCStr {
|
|
|
|
static void run(benchmark::State& state) {
|
|
|
|
for (auto _ : state) {
|
|
|
|
benchmark::DoNotOptimize(
|
|
|
|
makeString(Length(), DiffType::Control, Opaque()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static std::string name() {
|
|
|
|
return "BM_StringConstructDestroyCStr" + Length::name() + Opaque::name();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
template <class Length, bool MeasureCopy, bool MeasureDestroy>
|
|
|
|
static void StringCopyAndDestroy(benchmark::State& state) {
|
|
|
|
static constexpr size_t NumStrings = 1024;
|
|
|
|
auto Orig = makeString(Length());
|
|
|
|
std::aligned_storage<sizeof(std::string)>::type Storage[NumStrings];
|
|
|
|
|
|
|
|
while (state.KeepRunningBatch(NumStrings)) {
|
|
|
|
if (!MeasureCopy)
|
|
|
|
state.PauseTiming();
|
|
|
|
for (size_t I = 0; I < NumStrings; ++I) {
|
|
|
|
::new (static_cast<void*>(Storage + I)) std::string(Orig);
|
|
|
|
}
|
|
|
|
if (!MeasureCopy)
|
|
|
|
state.ResumeTiming();
|
|
|
|
if (!MeasureDestroy)
|
|
|
|
state.PauseTiming();
|
|
|
|
for (size_t I = 0; I < NumStrings; ++I) {
|
|
|
|
using S = std::string;
|
|
|
|
reinterpret_cast<S*>(Storage + I)->~S();
|
|
|
|
}
|
|
|
|
if (!MeasureDestroy)
|
|
|
|
state.ResumeTiming();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class Length>
|
|
|
|
struct StringCopy {
|
|
|
|
static void run(benchmark::State& state) {
|
|
|
|
StringCopyAndDestroy<Length, true, false>(state);
|
|
|
|
}
|
|
|
|
|
|
|
|
static std::string name() { return "BM_StringCopy" + Length::name(); }
|
|
|
|
};
|
|
|
|
|
|
|
|
template <class Length>
|
|
|
|
struct StringDestroy {
|
|
|
|
static void run(benchmark::State& state) {
|
|
|
|
StringCopyAndDestroy<Length, false, true>(state);
|
|
|
|
}
|
|
|
|
|
|
|
|
static std::string name() { return "BM_StringDestroy" + Length::name(); }
|
|
|
|
};
|
|
|
|
|
|
|
|
template <class Length>
|
|
|
|
struct StringMove {
|
|
|
|
static void run(benchmark::State& state) {
|
|
|
|
// Keep two object locations and move construct back and forth.
|
2018-11-14 03:16:19 +08:00
|
|
|
std::aligned_storage<sizeof(std::string), alignof(std::string)>::type Storage[2];
|
2018-10-30 23:54:22 +08:00
|
|
|
using S = std::string;
|
|
|
|
size_t I = 0;
|
2018-11-14 03:16:19 +08:00
|
|
|
S *newS = new (static_cast<void*>(Storage)) std::string(makeString(Length()));
|
2018-10-30 23:54:22 +08:00
|
|
|
for (auto _ : state) {
|
|
|
|
// Switch locations.
|
|
|
|
I ^= 1;
|
|
|
|
benchmark::DoNotOptimize(Storage);
|
|
|
|
// Move construct into the new location,
|
2018-11-14 03:16:19 +08:00
|
|
|
S *tmpS = new (static_cast<void*>(Storage + I)) S(std::move(*newS));
|
2018-10-30 23:54:22 +08:00
|
|
|
// then destroy the old one.
|
2018-11-14 03:16:19 +08:00
|
|
|
newS->~S();
|
|
|
|
newS = tmpS;
|
2018-10-30 23:54:22 +08:00
|
|
|
}
|
2018-11-14 03:16:19 +08:00
|
|
|
newS->~S();
|
2018-10-30 23:54:22 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static std::string name() { return "BM_StringMove" + Length::name(); }
|
|
|
|
};
|
|
|
|
|
2020-01-08 05:31:06 +08:00
|
|
|
template <class Length, class Opaque>
|
|
|
|
struct StringResizeDefaultInit {
|
|
|
|
static void run(benchmark::State& state) {
|
|
|
|
constexpr bool opaque = Opaque{} == Opacity::Opaque;
|
|
|
|
constexpr int kNumStrings = 4 << 10;
|
|
|
|
size_t length = makeString(Length()).size();
|
|
|
|
std::string strings[kNumStrings];
|
|
|
|
while (state.KeepRunningBatch(kNumStrings)) {
|
|
|
|
state.PauseTiming();
|
|
|
|
for (int i = 0; i < kNumStrings; ++i) {
|
|
|
|
std::string().swap(strings[i]);
|
|
|
|
}
|
|
|
|
benchmark::DoNotOptimize(strings);
|
|
|
|
state.ResumeTiming();
|
|
|
|
for (int i = 0; i < kNumStrings; ++i) {
|
|
|
|
strings[i].__resize_default_init(maybeOpaque(length, opaque));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static std::string name() {
|
|
|
|
return "BM_StringResizeDefaultInit" + Length::name() + Opaque::name();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
template <class Length, class Opaque>
|
|
|
|
struct StringAssignStr {
|
|
|
|
static void run(benchmark::State& state) {
|
|
|
|
constexpr bool opaque = Opaque{} == Opacity::Opaque;
|
|
|
|
constexpr int kNumStrings = 4 << 10;
|
|
|
|
std::string src = makeString(Length());
|
|
|
|
std::string strings[kNumStrings];
|
|
|
|
while (state.KeepRunningBatch(kNumStrings)) {
|
|
|
|
state.PauseTiming();
|
|
|
|
for (int i = 0; i < kNumStrings; ++i) {
|
|
|
|
std::string().swap(strings[i]);
|
|
|
|
}
|
|
|
|
benchmark::DoNotOptimize(strings);
|
|
|
|
state.ResumeTiming();
|
|
|
|
for (int i = 0; i < kNumStrings; ++i) {
|
|
|
|
strings[i] = *maybeOpaque(&src, opaque);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static std::string name() {
|
|
|
|
return "BM_StringAssignStr" + Length::name() + Opaque::name();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
template <class Length, class Opaque>
|
|
|
|
struct StringAssignAsciiz {
|
|
|
|
static void run(benchmark::State& state) {
|
|
|
|
constexpr bool opaque = Opaque{} == Opacity::Opaque;
|
|
|
|
constexpr int kNumStrings = 4 << 10;
|
|
|
|
std::string strings[kNumStrings];
|
|
|
|
while (state.KeepRunningBatch(kNumStrings)) {
|
|
|
|
state.PauseTiming();
|
|
|
|
for (int i = 0; i < kNumStrings; ++i) {
|
|
|
|
std::string().swap(strings[i]);
|
|
|
|
}
|
|
|
|
benchmark::DoNotOptimize(strings);
|
|
|
|
state.ResumeTiming();
|
|
|
|
for (int i = 0; i < kNumStrings; ++i) {
|
|
|
|
strings[i] = maybeOpaque(getString(Length()), opaque);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static std::string name() {
|
|
|
|
return "BM_StringAssignAsciiz" + Length::name() + Opaque::name();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2020-01-31 05:02:22 +08:00
|
|
|
template <class Length, class Opaque>
|
|
|
|
struct StringEraseToEnd {
|
|
|
|
static void run(benchmark::State& state) {
|
|
|
|
constexpr bool opaque = Opaque{} == Opacity::Opaque;
|
|
|
|
constexpr int kNumStrings = 4 << 10;
|
|
|
|
std::string strings[kNumStrings];
|
|
|
|
const int mid = makeString(Length()).size() / 2;
|
|
|
|
while (state.KeepRunningBatch(kNumStrings)) {
|
|
|
|
state.PauseTiming();
|
|
|
|
for (int i = 0; i < kNumStrings; ++i) {
|
|
|
|
strings[i] = makeString(Length());
|
|
|
|
}
|
|
|
|
benchmark::DoNotOptimize(strings);
|
|
|
|
state.ResumeTiming();
|
|
|
|
for (int i = 0; i < kNumStrings; ++i) {
|
|
|
|
strings[i].erase(maybeOpaque(mid, opaque),
|
|
|
|
maybeOpaque(std::string::npos, opaque));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static std::string name() {
|
|
|
|
return "BM_StringEraseToEnd" + Length::name() + Opaque::name();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
template <class Length, class Opaque>
|
|
|
|
struct StringEraseWithMove {
|
|
|
|
static void run(benchmark::State& state) {
|
|
|
|
constexpr bool opaque = Opaque{} == Opacity::Opaque;
|
|
|
|
constexpr int kNumStrings = 4 << 10;
|
|
|
|
std::string strings[kNumStrings];
|
|
|
|
const int n = makeString(Length()).size() / 2;
|
|
|
|
const int pos = n / 2;
|
|
|
|
while (state.KeepRunningBatch(kNumStrings)) {
|
|
|
|
state.PauseTiming();
|
|
|
|
for (int i = 0; i < kNumStrings; ++i) {
|
|
|
|
strings[i] = makeString(Length());
|
|
|
|
}
|
|
|
|
benchmark::DoNotOptimize(strings);
|
|
|
|
state.ResumeTiming();
|
|
|
|
for (int i = 0; i < kNumStrings; ++i) {
|
|
|
|
strings[i].erase(maybeOpaque(pos, opaque), maybeOpaque(n, opaque));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static std::string name() {
|
|
|
|
return "BM_StringEraseWithMove" + Length::name() + Opaque::name();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2020-01-08 05:31:06 +08:00
|
|
|
template <class Opaque>
|
|
|
|
struct StringAssignAsciizMix {
|
|
|
|
static void run(benchmark::State& state) {
|
|
|
|
constexpr auto O = Opaque{};
|
|
|
|
constexpr auto D = DiffType::Control;
|
|
|
|
constexpr int kNumStrings = 4 << 10;
|
|
|
|
std::string strings[kNumStrings];
|
|
|
|
while (state.KeepRunningBatch(kNumStrings)) {
|
|
|
|
state.PauseTiming();
|
|
|
|
for (int i = 0; i < kNumStrings; ++i) {
|
|
|
|
std::string().swap(strings[i]);
|
|
|
|
}
|
|
|
|
benchmark::DoNotOptimize(strings);
|
|
|
|
state.ResumeTiming();
|
|
|
|
for (int i = 0; i < kNumStrings - 7; i += 8) {
|
|
|
|
strings[i + 0] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
|
|
|
|
strings[i + 1] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
|
|
|
|
strings[i + 2] = maybeOpaque(getLargeString(D), O == Opacity::Opaque);
|
|
|
|
strings[i + 3] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
|
|
|
|
strings[i + 4] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
|
|
|
|
strings[i + 5] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
|
|
|
|
strings[i + 6] = maybeOpaque(getLargeString(D), O == Opacity::Opaque);
|
|
|
|
strings[i + 7] = maybeOpaque(getSmallString(D), O == Opacity::Opaque);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static std::string name() {
|
|
|
|
return "BM_StringAssignAsciizMix" + Opaque::name();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-10-30 23:54:22 +08:00
|
|
|
enum class Relation { Eq, Less, Compare };
|
|
|
|
struct AllRelations : EnumValuesAsTuple<AllRelations, Relation, 3> {
|
|
|
|
static constexpr const char* Names[] = {"Eq", "Less", "Compare"};
|
|
|
|
};
|
|
|
|
|
|
|
|
template <class Rel, class LHLength, class RHLength, class DiffType>
|
|
|
|
struct StringRelational {
|
|
|
|
static void run(benchmark::State& state) {
|
|
|
|
auto Lhs = makeString(RHLength());
|
|
|
|
auto Rhs = makeString(LHLength(), DiffType());
|
|
|
|
for (auto _ : state) {
|
|
|
|
benchmark::DoNotOptimize(Lhs);
|
|
|
|
benchmark::DoNotOptimize(Rhs);
|
|
|
|
switch (Rel()) {
|
|
|
|
case Relation::Eq:
|
|
|
|
benchmark::DoNotOptimize(Lhs == Rhs);
|
|
|
|
break;
|
|
|
|
case Relation::Less:
|
|
|
|
benchmark::DoNotOptimize(Lhs < Rhs);
|
|
|
|
break;
|
|
|
|
case Relation::Compare:
|
|
|
|
benchmark::DoNotOptimize(Lhs.compare(Rhs));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool skip() {
|
|
|
|
// Eq is commutative, so skip half the matrix.
|
|
|
|
if (Rel() == Relation::Eq && LHLength() > RHLength())
|
|
|
|
return true;
|
|
|
|
// We only care about control when the lengths differ.
|
|
|
|
if (LHLength() != RHLength() && DiffType() != ::DiffType::Control)
|
|
|
|
return true;
|
|
|
|
// For empty, only control matters.
|
|
|
|
if (LHLength() == Length::Empty && DiffType() != ::DiffType::Control)
|
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static std::string name() {
|
|
|
|
return "BM_StringRelational" + Rel::name() + LHLength::name() +
|
|
|
|
RHLength::name() + DiffType::name();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2019-04-04 01:40:51 +08:00
|
|
|
template <class Rel, class LHLength, class RHLength, class DiffType>
|
2019-03-22 00:06:15 +08:00
|
|
|
struct StringRelationalLiteral {
|
|
|
|
static void run(benchmark::State& state) {
|
|
|
|
auto Lhs = makeString(LHLength(), DiffType());
|
|
|
|
for (auto _ : state) {
|
|
|
|
benchmark::DoNotOptimize(Lhs);
|
2019-04-04 01:40:51 +08:00
|
|
|
constexpr const char* Literal = RHLength::value == Length::Empty
|
|
|
|
? ""
|
|
|
|
: RHLength::value == Length::Small
|
|
|
|
? SmallStringLiteral
|
|
|
|
: LargeStringLiteral;
|
2019-03-22 00:06:15 +08:00
|
|
|
switch (Rel()) {
|
|
|
|
case Relation::Eq:
|
2019-04-04 01:40:51 +08:00
|
|
|
benchmark::DoNotOptimize(Lhs == Literal);
|
2019-03-22 00:06:15 +08:00
|
|
|
break;
|
|
|
|
case Relation::Less:
|
2019-04-04 01:40:51 +08:00
|
|
|
benchmark::DoNotOptimize(Lhs < Literal);
|
2019-03-22 00:06:15 +08:00
|
|
|
break;
|
|
|
|
case Relation::Compare:
|
2019-04-04 01:40:51 +08:00
|
|
|
benchmark::DoNotOptimize(Lhs.compare(Literal));
|
2019-03-22 00:06:15 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool skip() {
|
|
|
|
// Doesn't matter how they differ if they have different size.
|
2019-04-04 01:40:51 +08:00
|
|
|
if (LHLength() != RHLength() && DiffType() != ::DiffType::Control)
|
2019-03-22 00:06:15 +08:00
|
|
|
return true;
|
|
|
|
// We don't need huge. Doensn't give anything different than Large.
|
2019-04-04 01:40:51 +08:00
|
|
|
if (LHLength() == Length::Huge || RHLength() == Length::Huge)
|
2019-03-22 00:06:15 +08:00
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static std::string name() {
|
|
|
|
return "BM_StringRelationalLiteral" + Rel::name() + LHLength::name() +
|
2019-04-04 01:40:51 +08:00
|
|
|
RHLength::name() + DiffType::name();
|
2019-03-22 00:06:15 +08:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-10-30 23:54:22 +08:00
|
|
|
enum class Depth { Shallow, Deep };
|
|
|
|
struct AllDepths : EnumValuesAsTuple<AllDepths, Depth, 2> {
|
|
|
|
static constexpr const char* Names[] = {"Shallow", "Deep"};
|
|
|
|
};
|
|
|
|
|
|
|
|
enum class Temperature { Hot, Cold };
|
|
|
|
struct AllTemperatures : EnumValuesAsTuple<AllTemperatures, Temperature, 2> {
|
|
|
|
static constexpr const char* Names[] = {"Hot", "Cold"};
|
|
|
|
};
|
|
|
|
|
|
|
|
template <class Temperature, class Depth, class Length>
|
|
|
|
struct StringRead {
|
|
|
|
void run(benchmark::State& state) const {
|
|
|
|
static constexpr size_t NumStrings =
|
|
|
|
Temperature() == ::Temperature::Hot
|
|
|
|
? 1 << 10
|
|
|
|
: /* Enough strings to overflow the cache */ 1 << 20;
|
|
|
|
static_assert((NumStrings & (NumStrings - 1)) == 0,
|
|
|
|
"NumStrings should be a power of two to reduce overhead.");
|
|
|
|
|
|
|
|
std::vector<std::string> Values(NumStrings, makeString(Length()));
|
|
|
|
size_t I = 0;
|
|
|
|
for (auto _ : state) {
|
|
|
|
// Jump long enough to defeat cache locality, and use a value that is
|
|
|
|
// coprime with NumStrings to ensure we visit every element.
|
|
|
|
I = (I + 17) % NumStrings;
|
|
|
|
const auto& V = Values[I];
|
|
|
|
|
|
|
|
// Read everything first. Escaping data() through DoNotOptimize might
|
|
|
|
// cause the compiler to have to recalculate information about `V` due to
|
|
|
|
// aliasing.
|
|
|
|
const char* const Data = V.data();
|
|
|
|
const size_t Size = V.size();
|
|
|
|
benchmark::DoNotOptimize(Data);
|
|
|
|
benchmark::DoNotOptimize(Size);
|
|
|
|
if (Depth() == ::Depth::Deep) {
|
|
|
|
// Read into the payload. This mainly shows the benefit of SSO when the
|
|
|
|
// data is cold.
|
|
|
|
benchmark::DoNotOptimize(*Data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool skip() {
|
|
|
|
// Huge does not give us anything that Large doesn't have. Skip it.
|
|
|
|
if (Length() == ::Length::Huge) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string name() const {
|
|
|
|
return "BM_StringRead" + Temperature::name() + Depth::name() +
|
|
|
|
Length::name();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
void sanityCheckGeneratedStrings() {
|
|
|
|
for (auto Lhs : {Length::Empty, Length::Small, Length::Large, Length::Huge}) {
|
|
|
|
const auto LhsString = makeString(Lhs);
|
|
|
|
for (auto Rhs :
|
|
|
|
{Length::Empty, Length::Small, Length::Large, Length::Huge}) {
|
|
|
|
if (Lhs > Rhs)
|
|
|
|
continue;
|
|
|
|
const auto RhsString = makeString(Rhs);
|
|
|
|
|
|
|
|
// The smaller one must be a prefix of the larger one.
|
|
|
|
if (RhsString.find(LhsString) != 0) {
|
|
|
|
fprintf(stderr, "Invalid autogenerated strings for sizes (%d,%d).\n",
|
|
|
|
static_cast<int>(Lhs), static_cast<int>(Rhs));
|
|
|
|
std::abort();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Verify the autogenerated diffs
|
|
|
|
for (auto L : {Length::Small, Length::Large, Length::Huge}) {
|
|
|
|
const auto Control = makeString(L);
|
|
|
|
const auto Verify = [&](std::string Exp, size_t Pos) {
|
|
|
|
// Only change on the Pos char.
|
|
|
|
if (Control[Pos] != Exp[Pos]) {
|
|
|
|
Exp[Pos] = Control[Pos];
|
|
|
|
if (Control == Exp)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
fprintf(stderr, "Invalid autogenerated diff with size %d\n",
|
|
|
|
static_cast<int>(L));
|
|
|
|
std::abort();
|
|
|
|
};
|
|
|
|
Verify(makeString(L, DiffType::ChangeFirst), 0);
|
|
|
|
Verify(makeString(L, DiffType::ChangeMiddle), Control.size() / 2);
|
|
|
|
Verify(makeString(L, DiffType::ChangeLast), Control.size() - 1);
|
2018-07-10 12:11:22 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-04 01:40:51 +08:00
|
|
|
// Some small codegen thunks to easily see generated code.
|
|
|
|
bool StringEqString(const std::string& a, const std::string& b) {
|
|
|
|
return a == b;
|
|
|
|
}
|
|
|
|
bool StringEqCStr(const std::string& a, const char* b) { return a == b; }
|
|
|
|
bool CStrEqString(const char* a, const std::string& b) { return a == b; }
|
|
|
|
bool StringEqCStrLiteralEmpty(const std::string& a) {
|
|
|
|
return a == "";
|
|
|
|
}
|
|
|
|
bool StringEqCStrLiteralSmall(const std::string& a) {
|
|
|
|
return a == SmallStringLiteral;
|
|
|
|
}
|
|
|
|
bool StringEqCStrLiteralLarge(const std::string& a) {
|
|
|
|
return a == LargeStringLiteral;
|
|
|
|
}
|
|
|
|
|
2018-10-30 23:54:22 +08:00
|
|
|
int main(int argc, char** argv) {
|
|
|
|
benchmark::Initialize(&argc, argv);
|
|
|
|
if (benchmark::ReportUnrecognizedArguments(argc, argv))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
sanityCheckGeneratedStrings();
|
|
|
|
|
|
|
|
makeCartesianProductBenchmark<StringConstructDestroyCStr, AllLengths,
|
|
|
|
AllOpacity>();
|
2020-01-08 05:31:06 +08:00
|
|
|
|
|
|
|
makeCartesianProductBenchmark<StringAssignStr, AllLengths, AllOpacity>();
|
|
|
|
makeCartesianProductBenchmark<StringAssignAsciiz, AllLengths, AllOpacity>();
|
|
|
|
makeCartesianProductBenchmark<StringAssignAsciizMix, AllOpacity>();
|
|
|
|
|
2018-10-30 23:54:22 +08:00
|
|
|
makeCartesianProductBenchmark<StringCopy, AllLengths>();
|
|
|
|
makeCartesianProductBenchmark<StringMove, AllLengths>();
|
|
|
|
makeCartesianProductBenchmark<StringDestroy, AllLengths>();
|
2020-01-08 05:31:06 +08:00
|
|
|
makeCartesianProductBenchmark<StringResizeDefaultInit, AllLengths,
|
|
|
|
AllOpacity>();
|
2020-01-31 05:02:22 +08:00
|
|
|
makeCartesianProductBenchmark<StringEraseToEnd, AllLengths, AllOpacity>();
|
|
|
|
makeCartesianProductBenchmark<StringEraseWithMove, AllLengths, AllOpacity>();
|
2018-10-30 23:54:22 +08:00
|
|
|
makeCartesianProductBenchmark<StringRelational, AllRelations, AllLengths,
|
|
|
|
AllLengths, AllDiffTypes>();
|
2019-03-22 00:06:15 +08:00
|
|
|
makeCartesianProductBenchmark<StringRelationalLiteral, AllRelations,
|
2019-04-04 01:40:51 +08:00
|
|
|
AllLengths, AllLengths, AllDiffTypes>();
|
2018-10-30 23:54:22 +08:00
|
|
|
makeCartesianProductBenchmark<StringRead, AllTemperatures, AllDepths,
|
|
|
|
AllLengths>();
|
|
|
|
benchmark::RunSpecifiedBenchmarks();
|
2019-04-04 01:40:51 +08:00
|
|
|
|
|
|
|
if (argc < 0) {
|
|
|
|
// ODR-use the functions to force them being generated in the binary.
|
|
|
|
auto functions = std::make_tuple(
|
|
|
|
StringEqString, StringEqCStr, CStrEqString, StringEqCStrLiteralEmpty,
|
|
|
|
StringEqCStrLiteralSmall, StringEqCStrLiteralLarge);
|
|
|
|
printf("%p", &functions);
|
|
|
|
}
|
2018-10-30 23:54:22 +08:00
|
|
|
}
|