-- 1bc4d36e13fb9175ea8cdaa00213aa9d4417c669 by Andy Getzendanner <durandal@google.com>: Fix pointer format specifier in documentation Import of https://github.com/abseil/abseil-cpp/pull/614 PiperOrigin-RevId: 293227540 -- c7b43b30493c4fb5f2ec3264672b08bfe1ea3709 by Abseil Team <absl-team@google.com>: Internal change. PiperOrigin-RevId: 293160245 -- 64439365e2b4a0b5e51ae0a7dafdb15912402dfd by Shahriar Rouf <nafi@google.com>: Add benchmarks for string_view: BM_CompareFirstOneLess and BM_CompareSecondOneLess. PiperOrigin-RevId: 293031676 -- b273b420cab24a6e3f487430987e09f4eb1caec4 by Greg Falcon <gfalcon@google.com>: Remove an unreachable line from charconv.cc. Fixes github issue #613. PiperOrigin-RevId: 292980167 -- 70babb5f7a3d9fdd00a2b3085c3c2b9fe0265c79 by Gennadiy Rozental <rogeeff@google.com>: Move GetFlag implementation into FlagImpl. This change will allow us to hide details of GetFlag overloads inside implementation detais. Eventually we'll migrate to a different implementation. No semantic changes in this CL. PiperOrigin-RevId: 292930847 -- 94bee7b7cc31e0167ee4b953281c1e78c96a574a by Abseil Team <absl-team@google.com>: Clarification in absl::Exponential documentation. PiperOrigin-RevId: 292912672 -- d6916d30c5c1d3ee9ae46d69ec0a166a760c99c7 by Derek Mauro <dmauro@google.com>: Make AtomicHook constant-initializable on Clang for Windows. Only mark AtomicHook as constant-initializable on platforms where it is actually constant-initializable. PiperOrigin-RevId: 292655939 GitOrigin-RevId: 1bc4d36e13fb9175ea8cdaa00213aa9d4417c669 Change-Id: I090b231a0ca0d92868e494ab5b3fa86c902889d5
		
			
				
	
	
		
			381 lines
		
	
	
	
		
			13 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			381 lines
		
	
	
	
		
			13 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| // Copyright 2018 The Abseil Authors.
 | |
| //
 | |
| // Licensed under the Apache License, Version 2.0 (the "License");
 | |
| // you may not use this file except in compliance with the License.
 | |
| // You may obtain a copy of the License at
 | |
| //
 | |
| //      https://www.apache.org/licenses/LICENSE-2.0
 | |
| //
 | |
| // Unless required by applicable law or agreed to in writing, software
 | |
| // distributed under the License is distributed on an "AS IS" BASIS,
 | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
| // See the License for the specific language governing permissions and
 | |
| // limitations under the License.
 | |
| 
 | |
| #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);
 | |
| 
 | |
| // Provide a forcibly out-of-line wrapper for operator== that can be used in
 | |
| // benchmarks to measure the impact of inlining.
 | |
| ABSL_ATTRIBUTE_NOINLINE
 | |
| bool NonInlinedEq(absl::string_view a, absl::string_view b) { return a == b; }
 | |
| 
 | |
| // We use functions that cannot be inlined to perform the comparison loops so
 | |
| // that inlining of the operator== can't optimize away *everything*.
 | |
| 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);
 | |
| 
 | |
| // This benchmark is intended to check that important simplifications can be
 | |
| // made with absl::string_view comparisons against constant strings. The idea is
 | |
| // that if constant strings cause redundant components of the comparison, the
 | |
| // compiler should detect and eliminate them. Here we use 8 different strings,
 | |
| // each with the same size. Provided our comparison makes the implementation
 | |
| // inline-able by the compiler, it should fold all of these away into a single
 | |
| // size check once per loop iteration.
 | |
| 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);
 | |
| }
 | |
| // We only need to check for size of 3, and <> 3 as this benchmark only has to
 | |
| // do with size differences.
 | |
| BENCHMARK(BM_EqualConstantSizeInlined)->DenseRange(2, 4);
 | |
| 
 | |
| // This benchmark exists purely to give context to the above timings: this is
 | |
| // what they would look like if the compiler is completely unable to simplify
 | |
| // between two comparisons when they are comparing against constant strings.
 | |
| ABSL_ATTRIBUTE_NOINLINE
 | |
| void DoConstantSizeNonInlinedEqualityComparisons(benchmark::State& state,
 | |
|                                                  absl::string_view a) {
 | |
|   for (auto _ : state) {
 | |
|     // Force these out-of-line to compare with the above function.
 | |
|     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);
 | |
| }
 | |
| // We only need to check for size of 3, and <> 3 as this benchmark only has to
 | |
| // do with size differences.
 | |
| 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"));  // not present; length 1
 | |
|   }
 | |
| }
 | |
| 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"));  // not present; length 2
 | |
|   }
 | |
| }
 | |
| 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'));  // not present
 | |
|   }
 | |
| }
 | |
| 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'));  // not present
 | |
|   }
 | |
| }
 | |
| 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');  // 1000 zeros.
 | |
| 
 | |
|   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) {}
 | |
| };
 | |
| 
 | |
| // This templated benchmark helper function is intended to stress operator== or
 | |
| // operator< in a realistic test.  It surely isn't entirely realistic, but it's
 | |
| // a start.  The test creates a map of type Map, a template arg, and populates
 | |
| // it with table_size key/value pairs. Each key has WordsPerKey words.  After
 | |
| // creating the map, a number of lookups are done in random order.  Some keys
 | |
| // are used much more frequently than others in this phase of the test.
 | |
| 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"};
 | |
|   // Create some keys that consist of words in random order.
 | |
|   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);
 | |
|   }
 | |
| 
 | |
|   // Create a list of strings to lookup: a permutation of the array of
 | |
|   // keys we just created, with repeats.  "Hot" keys get repeated more.
 | |
|   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(),
 | |
|       "");
 | |
|   // After constructing the array we probe it with absl::string_views built from
 | |
|   // test_strings.  This means operator== won't see equal pointers, so
 | |
|   // it'll have to check for equal lengths and equal characters.
 | |
|   std::vector<std::string> test_strings(indices.size());
 | |
|   for (int i = 0; i < indices.size(); i++) {
 | |
|     test_strings[i] = keys[indices[i]];
 | |
|   }
 | |
| 
 | |
|   // Run the benchmark. It includes map construction but is mostly
 | |
|   // map lookups.
 | |
|   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);
 | |
| 
 | |
| }  // namespace
 |