#include "absl/strings/string_view.h"
#include <algorithm>
#include <cstdint>
#include <map>
#include <random>
#include <string>
#include <unordered_set>
#include <vector>
#include "benchmark/benchmark.h"
#include "absl/base/attributes.h"
#include "absl/base/internal/raw_logging.h"
#include "absl/base/macros.h"
#include "absl/strings/str_cat.h"
namespace {
void BM_StringViewFromString(benchmark::State& state) {
std::string s(state.range(0), 'x');
std::string* ps = &s;
struct SV {
SV() = default;
explicit SV(const std::string& s) : sv(s) {}
absl::string_view sv;
} sv;
SV* psv = &sv;
benchmark::DoNotOptimize(ps);
benchmark::DoNotOptimize(psv);
for (auto _ : state) {
new (psv) SV(*ps);
benchmark::DoNotOptimize(sv);
}
}
BENCHMARK(BM_StringViewFromString)->Arg(12)->Arg(128);
ABSL_ATTRIBUTE_NOINLINE
bool NonInlinedEq(absl::string_view a, absl::string_view b) { return a == b; }
ABSL_ATTRIBUTE_NOINLINE
void DoEqualityComparisons(benchmark::State& state, absl::string_view a,
absl::string_view b) {
for (auto _ : state) {
benchmark::DoNotOptimize(a == b);
}
}
void BM_EqualIdentical(benchmark::State& state) {
std::string x(state.range(0), 'a');
DoEqualityComparisons(state, x, x);
}
BENCHMARK(BM_EqualIdentical)->DenseRange(0, 3)->Range(4, 1 << 10);
void BM_EqualSame(benchmark::State& state) {
std::string x(state.range(0), 'a');
std::string y = x;
DoEqualityComparisons(state, x, y);
}
BENCHMARK(BM_EqualSame)
->DenseRange(0, 10)
->Arg(20)
->Arg(40)
->Arg(70)
->Arg(110)
->Range(160, 4096);
void BM_EqualDifferent(benchmark::State& state) {
const int len = state.range(0);
std::string x(len, 'a');
std::string y = x;
if (len > 0) {
y[len - 1] = 'b';
}
DoEqualityComparisons(state, x, y);
}
BENCHMARK(BM_EqualDifferent)->DenseRange(0, 3)->Range(4, 1 << 10);
ABSL_ATTRIBUTE_NOINLINE
void DoConstantSizeInlinedEqualityComparisons(benchmark::State& state,
absl::string_view a) {
for (auto _ : state) {
benchmark::DoNotOptimize(a == "aaa");
benchmark::DoNotOptimize(a == "bbb");
benchmark::DoNotOptimize(a == "ccc");
benchmark::DoNotOptimize(a == "ddd");
benchmark::DoNotOptimize(a == "eee");
benchmark::DoNotOptimize(a == "fff");
benchmark::DoNotOptimize(a == "ggg");
benchmark::DoNotOptimize(a == "hhh");
}
}
void BM_EqualConstantSizeInlined(benchmark::State& state) {
std::string x(state.range(0), 'a');
DoConstantSizeInlinedEqualityComparisons(state, x);
}
BENCHMARK(BM_EqualConstantSizeInlined)->DenseRange(2, 4);
ABSL_ATTRIBUTE_NOINLINE
void DoConstantSizeNonInlinedEqualityComparisons(benchmark::State& state,
absl::string_view a) {
for (auto _ : state) {
benchmark::DoNotOptimize(NonInlinedEq(a, "aaa"));
benchmark::DoNotOptimize(NonInlinedEq(a, "bbb"));
benchmark::DoNotOptimize(NonInlinedEq(a, "ccc"));
benchmark::DoNotOptimize(NonInlinedEq(a, "ddd"));
benchmark::DoNotOptimize(NonInlinedEq(a, "eee"));
benchmark::DoNotOptimize(NonInlinedEq(a, "fff"));
benchmark::DoNotOptimize(NonInlinedEq(a, "ggg"));
benchmark::DoNotOptimize(NonInlinedEq(a, "hhh"));
}
}
void BM_EqualConstantSizeNonInlined(benchmark::State& state) {
std::string x(state.range(0), 'a');
DoConstantSizeNonInlinedEqualityComparisons(state, x);
}
BENCHMARK(BM_EqualConstantSizeNonInlined)->DenseRange(2, 4);
void BM_CompareSame(benchmark::State& state) {
const int len = state.range(0);
std::string x;
for (int i = 0; i < len; i++) {
x += 'a';
}
std::string y = x;
absl::string_view a = x;
absl::string_view b = y;
for (auto _ : state) {
benchmark::DoNotOptimize(a);
benchmark::DoNotOptimize(b);
benchmark::DoNotOptimize(a.compare(b));
}
}
BENCHMARK(BM_CompareSame)->DenseRange(0, 3)->Range(4, 1 << 10);
void BM_CompareFirstOneLess(benchmark::State& state) {
const int len = state.range(0);
std::string x(len, 'a');
std::string y = x;
y.back() = 'b';
absl::string_view a = x;
absl::string_view b = y;
for (auto _ : state) {
benchmark::DoNotOptimize(a);
benchmark::DoNotOptimize(b);
benchmark::DoNotOptimize(a.compare(b));
}
}
BENCHMARK(BM_CompareFirstOneLess)->DenseRange(1, 3)->Range(4, 1 << 10);
void BM_CompareSecondOneLess(benchmark::State& state) {
const int len = state.range(0);
std::string x(len, 'a');
std::string y = x;
x.back() = 'b';
absl::string_view a = x;
absl::string_view b = y;
for (auto _ : state) {
benchmark::DoNotOptimize(a);
benchmark::DoNotOptimize(b);
benchmark::DoNotOptimize(a.compare(b));
}
}
BENCHMARK(BM_CompareSecondOneLess)->DenseRange(1, 3)->Range(4, 1 << 10);
void BM_find_string_view_len_one(benchmark::State& state) {
std::string haystack(state.range(0), '0');
absl::string_view s(haystack);
for (auto _ : state) {
benchmark::DoNotOptimize(s.find("x")); }
}
BENCHMARK(BM_find_string_view_len_one)->Range(1, 1 << 20);
void BM_find_string_view_len_two(benchmark::State& state) {
std::string haystack(state.range(0), '0');
absl::string_view s(haystack);
for (auto _ : state) {
benchmark::DoNotOptimize(s.find("xx")); }
}
BENCHMARK(BM_find_string_view_len_two)->Range(1, 1 << 20);
void BM_find_one_char(benchmark::State& state) {
std::string haystack(state.range(0), '0');
absl::string_view s(haystack);
for (auto _ : state) {
benchmark::DoNotOptimize(s.find('x')); }
}
BENCHMARK(BM_find_one_char)->Range(1, 1 << 20);
void BM_rfind_one_char(benchmark::State& state) {
std::string haystack(state.range(0), '0');
absl::string_view s(haystack);
for (auto _ : state) {
benchmark::DoNotOptimize(s.rfind('x')); }
}
BENCHMARK(BM_rfind_one_char)->Range(1, 1 << 20);
void BM_worst_case_find_first_of(benchmark::State& state, int haystack_len) {
const int needle_len = state.range(0);
std::string needle;
for (int i = 0; i < needle_len; ++i) {
needle += 'a' + i;
}
std::string haystack(haystack_len, '0');
absl::string_view s(haystack);
for (auto _ : state) {
benchmark::DoNotOptimize(s.find_first_of(needle));
}
}
void BM_find_first_of_short(benchmark::State& state) {
BM_worst_case_find_first_of(state, 10);
}
void BM_find_first_of_medium(benchmark::State& state) {
BM_worst_case_find_first_of(state, 100);
}
void BM_find_first_of_long(benchmark::State& state) {
BM_worst_case_find_first_of(state, 1000);
}
BENCHMARK(BM_find_first_of_short)->DenseRange(0, 4)->Arg(8)->Arg(16)->Arg(32);
BENCHMARK(BM_find_first_of_medium)->DenseRange(0, 4)->Arg(8)->Arg(16)->Arg(32);
BENCHMARK(BM_find_first_of_long)->DenseRange(0, 4)->Arg(8)->Arg(16)->Arg(32);
struct EasyMap : public std::map<absl::string_view, uint64_t> {
explicit EasyMap(size_t) {}
};
template <typename Map, int WordsPerKey>
void StringViewMapBenchmark(benchmark::State& state) {
const int table_size = state.range(0);
const double kFractionOfKeysThatAreHot = 0.2;
const int kNumLookupsOfHotKeys = 20;
const int kNumLookupsOfColdKeys = 1;
const char* words[] = {"the", "quick", "brown", "fox", "jumped",
"over", "the", "lazy", "dog", "and",
"found", "a", "large", "mushroom", "and",
"a", "couple", "crickets", "eating", "pie"};
std::random_device r;
std::seed_seq seed({r(), r(), r(), r(), r(), r(), r(), r()});
std::mt19937 rng(seed);
std::vector<std::string> keys(table_size);
std::vector<int> all_indices;
const int kBlockSize = 1 << 12;
std::unordered_set<std::string> t(kBlockSize);
std::uniform_int_distribution<int> uniform(0, ABSL_ARRAYSIZE(words) - 1);
for (int i = 0; i < table_size; i++) {
all_indices.push_back(i);
do {
keys[i].clear();
for (int j = 0; j < WordsPerKey; j++) {
absl::StrAppend(&keys[i], j > 0 ? " " : "", words[uniform(rng)]);
}
} while (!t.insert(keys[i]).second);
}
std::shuffle(all_indices.begin(), all_indices.end(), rng);
const int num_hot = table_size * kFractionOfKeysThatAreHot;
const int num_cold = table_size - num_hot;
std::vector<int> hot_indices(all_indices.begin(),
all_indices.begin() + num_hot);
std::vector<int> indices;
for (int i = 0; i < kNumLookupsOfColdKeys; i++) {
indices.insert(indices.end(), all_indices.begin(), all_indices.end());
}
for (int i = 0; i < kNumLookupsOfHotKeys - kNumLookupsOfColdKeys; i++) {
indices.insert(indices.end(), hot_indices.begin(), hot_indices.end());
}
std::shuffle(indices.begin(), indices.end(), rng);
ABSL_RAW_CHECK(
num_cold * kNumLookupsOfColdKeys + num_hot * kNumLookupsOfHotKeys ==
indices.size(),
"");
std::vector<std::string> test_strings(indices.size());
for (int i = 0; i < indices.size(); i++) {
test_strings[i] = keys[indices[i]];
}
for (auto _ : state) {
Map h(table_size);
for (int i = 0; i < table_size; i++) {
h[keys[i]] = i * 2;
}
ABSL_RAW_CHECK(h.size() == table_size, "");
uint64_t sum = 0;
for (int i = 0; i < indices.size(); i++) {
sum += h[test_strings[i]];
}
benchmark::DoNotOptimize(sum);
}
}
void BM_StdMap_4(benchmark::State& state) {
StringViewMapBenchmark<EasyMap, 4>(state);
}
BENCHMARK(BM_StdMap_4)->Range(1 << 10, 1 << 16);
void BM_StdMap_8(benchmark::State& state) {
StringViewMapBenchmark<EasyMap, 8>(state);
}
BENCHMARK(BM_StdMap_8)->Range(1 << 10, 1 << 16);
void BM_CopyToStringNative(benchmark::State& state) {
std::string src(state.range(0), 'x');
absl::string_view sv(src);
std::string dst;
for (auto _ : state) {
dst.assign(sv.begin(), sv.end());
}
}
BENCHMARK(BM_CopyToStringNative)->Range(1 << 3, 1 << 12);
void BM_AppendToStringNative(benchmark::State& state) {
std::string src(state.range(0), 'x');
absl::string_view sv(src);
std::string dst;
for (auto _ : state) {
dst.clear();
dst.insert(dst.end(), sv.begin(), sv.end());
}
}
BENCHMARK(BM_AppendToStringNative)->Range(1 << 3, 1 << 12);
}