diff options
author | Abseil Team <absl-team@google.com> | 2020-02-25 22:27:31 +0100 |
---|---|---|
committer | CJ Johnson <johnsoncj@google.com> | 2020-02-25 17:56:58 -0500 |
commit | b832dce8489ef7b6231384909fd9b68d5a5ff2b7 (patch) | |
tree | 3ad4be9a9a4105366be714da9458e076a77be18f /absl/container/btree_benchmark.cc | |
parent | aa844899c937bde5d2b24f276b59997e5b668bde (diff) |
Creation of LTS branch "lts_2020_02_25"20200225
- 0033c9ea91a52ade7c6b725aa2ef3cbe15463421 Fix build on FreeBSD/powerpc (#616) by kgotlinux <60880393+kgotlinux@users.noreply.github.com>
- 0d5ce2797eb695aee7e019e25323251ef6ffc277 Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- b69c7d880caddfc25bf348dbcfe9d45fdd8bc6e6 Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- 2a5633fc077a58528cdbfe78720f3f6bfdc6044d Merge "Export of internal Abseil changes" by Xiaoyi Zhang <zhangxy@google.com>
- f9b3d6e493c1b6ab3dbdab71c5f8fa849db4abaf Add RISCV support to GetProgramCounter() (#621) by Khem Raj <raj.khem@gmail.com>
- 0232c87f21c26718aa3eb2d86678070f3b498a4e Add missing ABSL_HAVE_VDSO_SUPPORT conditional (#622) by Sinan Kaya <41809318+franksinankaya@users.noreply.github.com>
- 3c814105108680997d0821077694f663693b5382 Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- c44657f55692eddf5504156645d1f4ec7b3acabd Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- 98eb410c93ad059f9bba1bf43f5bb916fc92a5ea Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- bf78e977309c4cb946914b456404141ddac1c302 Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- d95d1567165d449e4c213ea31a15cbb112a9865f Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- 24713a7036a81498334807fa5c7ad3cb7c643711 Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- 72382c21fefed981b4b8a2a1b82e2d231c2c2e39 Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- 08a7e7bf972c8451855a5022f2faf3d3655db015 Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- 36bcd9599b3f48c99357ba61cf33584889306d6a Fix pointer format specifier in documentation (#614) by Andre Nguyen <andre-nguyen@users.noreply.github.com>
- 0f86336b6939ea673cc1cbe29189286cae67d63a Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- c512f118dde6ffd51cb7d8ac8804bbaf4d266c3a Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- 37dd2562ec830d547a1524bb306be313ac3f2556 Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- 44427702614d7b86b064ba06a390f5eb2f85dbf6 fix: Add support for more ARM processors detection (#608) by Andre Nguyen <andre-nguyen@users.noreply.github.com>
- 159bf2bf6d1cc8087e02468d071e94d1177d1bae Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- a2e6adecc294dc4cd98cc285a9134ce58e0f2ad0 Use https links. (#586) by nlewycky <nicholas@mxc.ca>
- 564001ae506a17c51fa1223684a78f05f91d3d91 Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- b3aaac8a37c467a1125c794196caa90d0957bdc3 Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- 63ee2f8877915a3565c29707dba8fe4d7822596a Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- a048203a881f11f4b7b8df5fb563aec85522f8db Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- 1de0166368e2ae67347f92099d6dca3ab3a4a496 Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- ad904b6cd3906ddf79878003d92b7bc08d7786ae Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- 29235139149790f5afc430c11cec8f1eb1677607 Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- bf86cfe165ef7d70dfe68f0b8fc0c018bc79a577 Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- 12bc53e0318d80569270a5b26ccbc62b52022b89 Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- 1e39f8626a4dadec1f56920b999dd4c3cfae333e Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- 77f87009a34c745255bd84d8f2647040d831a2b3 Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- d659fe54b35ab9b8e35c72e50a4b8814167d5a84 Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- a4b757b5d42694306a9de853cee0a5fba9c7bbe9 Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- 0514227d2547793b23e209809276375e41c76617 Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- 7f4fe64af80fe3c84db8ea938276c3690573c45e Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- 16d9fd58a51c6083234e2e9f8f50e49ed5ed02e4 Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- bcaae6009c0833b73c6fa7bdd972921d8081a724 Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- 8ba96a8244bbe334d09542e92d566673a65c1f78 Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- 2103fd9acdf58279f739860bff3f8c9f4b845605 Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- 3df7b52a6ada51a72a23796b844549a7b282f1b8 Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- fa8c75182fbfdeddb2485fc0d53baeda3f40b7a3 Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- 85092b4b648ca729c6263c4a302a41dfff28705e Fix Conan builds (#400) by Adrian Ostrowski <adr.ostrowski@gmail.com>
- e96ae2203b80d5ae2e0b7abe0c77b722b224b16d Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- 20de2db748ca0471cfb61cb53e813dd12938c12b Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- 846e5dbedac123d12455adcfe6f53c8b5dcbfeef Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- 8207907f4f7fbaeeaa2b7340c76875e06fd345ba Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- 078b89b3c046d230ef3ad39494e5852184eb528b Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- 19b021cb3ff23048dfbe236a4e611925d8930831 Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- ecc0033b54847f6c9ee37dbb0be8aa17e5b6d37b Always enable proper symbolize implementation on Windows ... by Loo Rong Jie <loorongjie@gmail.com>
- 2796d500aea5a31d26b8b24a33fab7a1c8fa2f32 Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- e4c8d0eb8ef4acb5d7a4252b3b87feb391ef7e41 Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- a15364ce4d88534ae2295127e5d8e32aefb6b446 Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- ab3552a18964e7063c8324f45b3896a6a20b08a8 Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- e9f9000c7c80993cb589d011616b7a8016e42f4a Fix ABSL_WAITER_MODE detection for mingw (#342) by Joe Sylve <Joe.Sylve@gmail.com>
- abea769b551f7a100f540967cb95debdb0080df8 Fix ABSL_HAVE_ALARM check on mingw (#341) by Joe Sylve <Joe.Sylve@gmail.com>
- 25597bdfc148e91e27678ec30efa52f4fc8c164f Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- aad33fefaa8f744d71ce747a53717b835bdf8e84 Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- 8fe7214fe2d7a45ecc4d85f6a524c6b1532426de Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- debac94cfb5a0fa75d1d97c399682bd1c72e3191 Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- 882b3501a31eb0e4ae4213afb91a0e43feda7d5f Fix spelling errors (#384) by Sungmann Cho <55860394+chosungmann@users.noreply.github.com>
- 502efe6d7841bff82b1aeef5500491fe9a48c635 Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- ccdd1d57b6386ebc26fb0c7d99b604672437c124 Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- ddf8e52a2918dd0ccec75d3e2426125fa3926724 Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- 6ec136281086b71da32b5fb068bd6e46b78a5c79 Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- ac78ffc3bc0a8b295cab9a03817760fd460df2a1 Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- 5374c56e5196320681993869e3126b51edac2a43 Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- 97c1664b4bbab5f78fac2b151ab02656268fb34b Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- 325fd7b042ff4ec34f7dd32e602cd81ad0e24b22 Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- 83c1d65c90a92aa49632b9ac5a793214bb0768bc Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- eb6b7bd23bc0815bfd784e1a74021ce166765280 Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- 9ddac555b7861dc178d0dbe758a1cfbed718784b Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- 1948f6f967e34db9793cfa8b4bcbaf370d039fd8 Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- a0d1e098c2f99694fa399b175a7ccf920762030e Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- 2d2d7fbc283315b676159716376e739d3d23ed94 Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- 0302d1e5fa4fcdd1763b7d1bb3212943b1ae911d supppress unused variable warning for gcc (#372) by Martin <pizzard@users.noreply.github.com>
- 262d74ba81b1fc4d71f459555cde8ecb39786d68 Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- f0afae0d49af3e15a7169e019634d7719143d94d Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- 0e7afdcbd24c7e5b7cab4e0217d8886f1525b520 Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- 9a41ffdd3a0ccbcdd29c4e3886b28e06f2cd9c66 Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- 36910d3d7e9fccadd6603f232d0c4f54dcd47c7e [bazel] Add fixes for --incompatible_load_cc_rules_from_b... by Yannic <contact@yannic-bonenberger.com>
- aae8143cf9aa611f70d7ea9b95b8b8b383b2271a Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- d9aa92d7fb324314f9df487ac23d32a25650b742 Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- 321ab5303023c86cd15d9ddc5740fb4b4fde32e1 Export of internal Abseil changes by Abseil Team <absl-team@google.com>
- 4ef574064e75b86f115549e9eb4c7e806781b3ab Export of internal Abseil changes by Abseil Team <absl-team@google.com>
GitOrigin-RevId: 0033c9ea91a52ade7c6b725aa2ef3cbe15463421
Change-Id: I8a2b70063cb3ab40c6943a6db0fe40cae71ed8d7
Diffstat (limited to 'absl/container/btree_benchmark.cc')
-rw-r--r-- | absl/container/btree_benchmark.cc | 707 |
1 files changed, 707 insertions, 0 deletions
diff --git a/absl/container/btree_benchmark.cc b/absl/container/btree_benchmark.cc new file mode 100644 index 00000000..4af92f9f --- /dev/null +++ b/absl/container/btree_benchmark.cc @@ -0,0 +1,707 @@ +// 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 <stdint.h> + +#include <algorithm> +#include <functional> +#include <map> +#include <numeric> +#include <random> +#include <set> +#include <string> +#include <type_traits> +#include <unordered_map> +#include <unordered_set> +#include <vector> + +#include "absl/base/internal/raw_logging.h" +#include "absl/container/btree_map.h" +#include "absl/container/btree_set.h" +#include "absl/container/btree_test.h" +#include "absl/container/flat_hash_map.h" +#include "absl/container/flat_hash_set.h" +#include "absl/container/internal/hashtable_debug.h" +#include "absl/flags/flag.h" +#include "absl/hash/hash.h" +#include "absl/memory/memory.h" +#include "absl/strings/str_format.h" +#include "absl/time/time.h" +#include "benchmark/benchmark.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace container_internal { +namespace { + +constexpr size_t kBenchmarkValues = 1 << 20; + +// How many times we add and remove sub-batches in one batch of *AddRem +// benchmarks. +constexpr size_t kAddRemBatchSize = 1 << 2; + +// Generates n values in the range [0, 4 * n]. +template <typename V> +std::vector<V> GenerateValues(int n) { + constexpr int kSeed = 23; + return GenerateValuesWithSeed<V>(n, 4 * n, kSeed); +} + +// Benchmark insertion of values into a container. +template <typename T> +void BM_InsertImpl(benchmark::State& state, bool sorted) { + using V = typename remove_pair_const<typename T::value_type>::type; + typename KeyOfValue<typename T::key_type, V>::type key_of_value; + + std::vector<V> values = GenerateValues<V>(kBenchmarkValues); + if (sorted) { + std::sort(values.begin(), values.end()); + } + T container(values.begin(), values.end()); + + // Remove and re-insert 10% of the keys per batch. + const int batch_size = (kBenchmarkValues + 9) / 10; + while (state.KeepRunningBatch(batch_size)) { + state.PauseTiming(); + const auto i = static_cast<int>(state.iterations()); + + for (int j = i; j < i + batch_size; j++) { + int x = j % kBenchmarkValues; + container.erase(key_of_value(values[x])); + } + + state.ResumeTiming(); + + for (int j = i; j < i + batch_size; j++) { + int x = j % kBenchmarkValues; + container.insert(values[x]); + } + } +} + +template <typename T> +void BM_Insert(benchmark::State& state) { + BM_InsertImpl<T>(state, false); +} + +template <typename T> +void BM_InsertSorted(benchmark::State& state) { + BM_InsertImpl<T>(state, true); +} + +// container::insert sometimes returns a pair<iterator, bool> and sometimes +// returns an iterator (for multi- containers). +template <typename Iter> +Iter GetIterFromInsert(const std::pair<Iter, bool>& pair) { + return pair.first; +} +template <typename Iter> +Iter GetIterFromInsert(const Iter iter) { + return iter; +} + +// Benchmark insertion of values into a container at the end. +template <typename T> +void BM_InsertEnd(benchmark::State& state) { + using V = typename remove_pair_const<typename T::value_type>::type; + typename KeyOfValue<typename T::key_type, V>::type key_of_value; + + T container; + const int kSize = 10000; + for (int i = 0; i < kSize; ++i) { + container.insert(Generator<V>(kSize)(i)); + } + V v = Generator<V>(kSize)(kSize - 1); + typename T::key_type k = key_of_value(v); + + auto it = container.find(k); + while (state.KeepRunning()) { + // Repeatedly removing then adding v. + container.erase(it); + it = GetIterFromInsert(container.insert(v)); + } +} + +template <typename T> +void BM_LookupImpl(benchmark::State& state, bool sorted) { + using V = typename remove_pair_const<typename T::value_type>::type; + typename KeyOfValue<typename T::key_type, V>::type key_of_value; + + std::vector<V> values = GenerateValues<V>(kBenchmarkValues); + if (sorted) { + std::sort(values.begin(), values.end()); + } + T container(values.begin(), values.end()); + + while (state.KeepRunning()) { + int idx = state.iterations() % kBenchmarkValues; + benchmark::DoNotOptimize(container.find(key_of_value(values[idx]))); + } +} + +// Benchmark lookup of values in a container. +template <typename T> +void BM_Lookup(benchmark::State& state) { + BM_LookupImpl<T>(state, false); +} + +// Benchmark lookup of values in a full container, meaning that values +// are inserted in-order to take advantage of biased insertion, which +// yields a full tree. +template <typename T> +void BM_FullLookup(benchmark::State& state) { + BM_LookupImpl<T>(state, true); +} + +// Benchmark deletion of values from a container. +template <typename T> +void BM_Delete(benchmark::State& state) { + using V = typename remove_pair_const<typename T::value_type>::type; + typename KeyOfValue<typename T::key_type, V>::type key_of_value; + std::vector<V> values = GenerateValues<V>(kBenchmarkValues); + T container(values.begin(), values.end()); + + // Remove and re-insert 10% of the keys per batch. + const int batch_size = (kBenchmarkValues + 9) / 10; + while (state.KeepRunningBatch(batch_size)) { + const int i = state.iterations(); + + for (int j = i; j < i + batch_size; j++) { + int x = j % kBenchmarkValues; + container.erase(key_of_value(values[x])); + } + + state.PauseTiming(); + for (int j = i; j < i + batch_size; j++) { + int x = j % kBenchmarkValues; + container.insert(values[x]); + } + state.ResumeTiming(); + } +} + +// Benchmark deletion of multiple values from a container. +template <typename T> +void BM_DeleteRange(benchmark::State& state) { + using V = typename remove_pair_const<typename T::value_type>::type; + typename KeyOfValue<typename T::key_type, V>::type key_of_value; + std::vector<V> values = GenerateValues<V>(kBenchmarkValues); + T container(values.begin(), values.end()); + + // Remove and re-insert 10% of the keys per batch. + const int batch_size = (kBenchmarkValues + 9) / 10; + while (state.KeepRunningBatch(batch_size)) { + const int i = state.iterations(); + + const int start_index = i % kBenchmarkValues; + + state.PauseTiming(); + { + std::vector<V> removed; + removed.reserve(batch_size); + auto itr = container.find(key_of_value(values[start_index])); + auto start = itr; + for (int j = 0; j < batch_size; j++) { + if (itr == container.end()) { + state.ResumeTiming(); + container.erase(start, itr); + state.PauseTiming(); + itr = container.begin(); + start = itr; + } + removed.push_back(*itr++); + } + + state.ResumeTiming(); + container.erase(start, itr); + state.PauseTiming(); + + container.insert(removed.begin(), removed.end()); + } + state.ResumeTiming(); + } +} + +// Benchmark steady-state insert (into first half of range) and remove (from +// second half of range), treating the container approximately like a queue with +// log-time access for all elements. This benchmark does not test the case where +// insertion and removal happen in the same region of the tree. This benchmark +// counts two value constructors. +template <typename T> +void BM_QueueAddRem(benchmark::State& state) { + using V = typename remove_pair_const<typename T::value_type>::type; + typename KeyOfValue<typename T::key_type, V>::type key_of_value; + + ABSL_RAW_CHECK(kBenchmarkValues % 2 == 0, "for performance"); + + T container; + + const size_t half = kBenchmarkValues / 2; + std::vector<int> remove_keys(half); + std::vector<int> add_keys(half); + + // We want to do the exact same work repeatedly, and the benchmark can end + // after a different number of iterations depending on the speed of the + // individual run so we use a large batch size here and ensure that we do + // deterministic work every batch. + while (state.KeepRunningBatch(half * kAddRemBatchSize)) { + state.PauseTiming(); + + container.clear(); + + for (size_t i = 0; i < half; ++i) { + remove_keys[i] = i; + add_keys[i] = i; + } + constexpr int kSeed = 5; + std::mt19937_64 rand(kSeed); + std::shuffle(remove_keys.begin(), remove_keys.end(), rand); + std::shuffle(add_keys.begin(), add_keys.end(), rand); + + // Note needs lazy generation of values. + Generator<V> g(kBenchmarkValues * kAddRemBatchSize); + + for (size_t i = 0; i < half; ++i) { + container.insert(g(add_keys[i])); + container.insert(g(half + remove_keys[i])); + } + + // There are three parts each of size "half": + // 1 is being deleted from [offset - half, offset) + // 2 is standing [offset, offset + half) + // 3 is being inserted into [offset + half, offset + 2 * half) + size_t offset = 0; + + for (size_t i = 0; i < kAddRemBatchSize; ++i) { + std::shuffle(remove_keys.begin(), remove_keys.end(), rand); + std::shuffle(add_keys.begin(), add_keys.end(), rand); + offset += half; + + state.ResumeTiming(); + for (size_t idx = 0; idx < half; ++idx) { + container.erase(key_of_value(g(offset - half + remove_keys[idx]))); + container.insert(g(offset + half + add_keys[idx])); + } + state.PauseTiming(); + } + state.ResumeTiming(); + } +} + +// Mixed insertion and deletion in the same range using pre-constructed values. +template <typename T> +void BM_MixedAddRem(benchmark::State& state) { + using V = typename remove_pair_const<typename T::value_type>::type; + typename KeyOfValue<typename T::key_type, V>::type key_of_value; + + ABSL_RAW_CHECK(kBenchmarkValues % 2 == 0, "for performance"); + + T container; + + // Create two random shuffles + std::vector<int> remove_keys(kBenchmarkValues); + std::vector<int> add_keys(kBenchmarkValues); + + // We want to do the exact same work repeatedly, and the benchmark can end + // after a different number of iterations depending on the speed of the + // individual run so we use a large batch size here and ensure that we do + // deterministic work every batch. + while (state.KeepRunningBatch(kBenchmarkValues * kAddRemBatchSize)) { + state.PauseTiming(); + + container.clear(); + + constexpr int kSeed = 7; + std::mt19937_64 rand(kSeed); + + std::vector<V> values = GenerateValues<V>(kBenchmarkValues * 2); + + // Insert the first half of the values (already in random order) + container.insert(values.begin(), values.begin() + kBenchmarkValues); + + // Insert the first half of the values (already in random order) + for (size_t i = 0; i < kBenchmarkValues; ++i) { + // remove_keys and add_keys will be swapped before each round, + // therefore fill add_keys here w/ the keys being inserted, so + // they'll be the first to be removed. + remove_keys[i] = i + kBenchmarkValues; + add_keys[i] = i; + } + + for (size_t i = 0; i < kAddRemBatchSize; ++i) { + remove_keys.swap(add_keys); + std::shuffle(remove_keys.begin(), remove_keys.end(), rand); + std::shuffle(add_keys.begin(), add_keys.end(), rand); + + state.ResumeTiming(); + for (size_t idx = 0; idx < kBenchmarkValues; ++idx) { + container.erase(key_of_value(values[remove_keys[idx]])); + container.insert(values[add_keys[idx]]); + } + state.PauseTiming(); + } + state.ResumeTiming(); + } +} + +// Insertion at end, removal from the beginning. This benchmark +// counts two value constructors. +// TODO(ezb): we could add a GenerateNext version of generator that could reduce +// noise for string-like types. +template <typename T> +void BM_Fifo(benchmark::State& state) { + using V = typename remove_pair_const<typename T::value_type>::type; + + T container; + // Need lazy generation of values as state.max_iterations is large. + Generator<V> g(kBenchmarkValues + state.max_iterations); + + for (int i = 0; i < kBenchmarkValues; i++) { + container.insert(g(i)); + } + + while (state.KeepRunning()) { + container.erase(container.begin()); + container.insert(container.end(), g(state.iterations() + kBenchmarkValues)); + } +} + +// Iteration (forward) through the tree +template <typename T> +void BM_FwdIter(benchmark::State& state) { + using V = typename remove_pair_const<typename T::value_type>::type; + using R = typename T::value_type const*; + + std::vector<V> values = GenerateValues<V>(kBenchmarkValues); + T container(values.begin(), values.end()); + + auto iter = container.end(); + + R r = nullptr; + + while (state.KeepRunning()) { + if (iter == container.end()) iter = container.begin(); + r = &(*iter); + ++iter; + } + + benchmark::DoNotOptimize(r); +} + +// Benchmark random range-construction of a container. +template <typename T> +void BM_RangeConstructionImpl(benchmark::State& state, bool sorted) { + using V = typename remove_pair_const<typename T::value_type>::type; + + std::vector<V> values = GenerateValues<V>(kBenchmarkValues); + if (sorted) { + std::sort(values.begin(), values.end()); + } + { + T container(values.begin(), values.end()); + } + + while (state.KeepRunning()) { + T container(values.begin(), values.end()); + benchmark::DoNotOptimize(container); + } +} + +template <typename T> +void BM_InsertRangeRandom(benchmark::State& state) { + BM_RangeConstructionImpl<T>(state, false); +} + +template <typename T> +void BM_InsertRangeSorted(benchmark::State& state) { + BM_RangeConstructionImpl<T>(state, true); +} + +#define STL_ORDERED_TYPES(value) \ + using stl_set_##value = std::set<value>; \ + using stl_map_##value = std::map<value, intptr_t>; \ + using stl_multiset_##value = std::multiset<value>; \ + using stl_multimap_##value = std::multimap<value, intptr_t> + +using StdString = std::string; +STL_ORDERED_TYPES(int32_t); +STL_ORDERED_TYPES(int64_t); +STL_ORDERED_TYPES(StdString); +STL_ORDERED_TYPES(Time); + +#define STL_UNORDERED_TYPES(value) \ + using stl_unordered_set_##value = std::unordered_set<value>; \ + using stl_unordered_map_##value = std::unordered_map<value, intptr_t>; \ + using flat_hash_set_##value = flat_hash_set<value>; \ + using flat_hash_map_##value = flat_hash_map<value, intptr_t>; \ + using stl_unordered_multiset_##value = std::unordered_multiset<value>; \ + using stl_unordered_multimap_##value = \ + std::unordered_multimap<value, intptr_t> + +#define STL_UNORDERED_TYPES_CUSTOM_HASH(value, hash) \ + using stl_unordered_set_##value = std::unordered_set<value, hash>; \ + using stl_unordered_map_##value = std::unordered_map<value, intptr_t, hash>; \ + using flat_hash_set_##value = flat_hash_set<value, hash>; \ + using flat_hash_map_##value = flat_hash_map<value, intptr_t, hash>; \ + using stl_unordered_multiset_##value = std::unordered_multiset<value, hash>; \ + using stl_unordered_multimap_##value = \ + std::unordered_multimap<value, intptr_t, hash> + +STL_UNORDERED_TYPES(int32_t); +STL_UNORDERED_TYPES(int64_t); +STL_UNORDERED_TYPES(StdString); +STL_UNORDERED_TYPES_CUSTOM_HASH(Time, absl::Hash<absl::Time>); + +#define BTREE_TYPES(value) \ + using btree_256_set_##value = \ + btree_set<value, std::less<value>, std::allocator<value>>; \ + using btree_256_map_##value = \ + btree_map<value, intptr_t, std::less<value>, \ + std::allocator<std::pair<const value, intptr_t>>>; \ + using btree_256_multiset_##value = \ + btree_multiset<value, std::less<value>, std::allocator<value>>; \ + using btree_256_multimap_##value = \ + btree_multimap<value, intptr_t, std::less<value>, \ + std::allocator<std::pair<const value, intptr_t>>> + +BTREE_TYPES(int32_t); +BTREE_TYPES(int64_t); +BTREE_TYPES(StdString); +BTREE_TYPES(Time); + +#define MY_BENCHMARK4(type, func) \ + void BM_##type##_##func(benchmark::State& state) { BM_##func<type>(state); } \ + BENCHMARK(BM_##type##_##func) + +#define MY_BENCHMARK3(type) \ + MY_BENCHMARK4(type, Insert); \ + MY_BENCHMARK4(type, InsertSorted); \ + MY_BENCHMARK4(type, InsertEnd); \ + MY_BENCHMARK4(type, Lookup); \ + MY_BENCHMARK4(type, FullLookup); \ + MY_BENCHMARK4(type, Delete); \ + MY_BENCHMARK4(type, DeleteRange); \ + MY_BENCHMARK4(type, QueueAddRem); \ + MY_BENCHMARK4(type, MixedAddRem); \ + MY_BENCHMARK4(type, Fifo); \ + MY_BENCHMARK4(type, FwdIter); \ + MY_BENCHMARK4(type, InsertRangeRandom); \ + MY_BENCHMARK4(type, InsertRangeSorted) + +#define MY_BENCHMARK2_SUPPORTS_MULTI_ONLY(type) \ + MY_BENCHMARK3(stl_##type); \ + MY_BENCHMARK3(stl_unordered_##type); \ + MY_BENCHMARK3(btree_256_##type) + +#define MY_BENCHMARK2(type) \ + MY_BENCHMARK2_SUPPORTS_MULTI_ONLY(type); \ + MY_BENCHMARK3(flat_hash_##type) + +// Define MULTI_TESTING to see benchmarks for multi-containers also. +// +// You can use --copt=-DMULTI_TESTING. +#ifdef MULTI_TESTING +#define MY_BENCHMARK(type) \ + MY_BENCHMARK2(set_##type); \ + MY_BENCHMARK2(map_##type); \ + MY_BENCHMARK2_SUPPORTS_MULTI_ONLY(multiset_##type); \ + MY_BENCHMARK2_SUPPORTS_MULTI_ONLY(multimap_##type) +#else +#define MY_BENCHMARK(type) \ + MY_BENCHMARK2(set_##type); \ + MY_BENCHMARK2(map_##type) +#endif + +MY_BENCHMARK(int32_t); +MY_BENCHMARK(int64_t); +MY_BENCHMARK(StdString); +MY_BENCHMARK(Time); + +// Define a type whose size and cost of moving are independently customizable. +// When sizeof(value_type) increases, we expect btree to no longer have as much +// cache-locality advantage over STL. When cost of moving increases, we expect +// btree to actually do more work than STL because it has to move values around +// and STL doesn't have to. +template <int Size, int Copies> +struct BigType { + BigType() : BigType(0) {} + explicit BigType(int x) { std::iota(values.begin(), values.end(), x); } + + void Copy(const BigType& x) { + for (int i = 0; i < Size && i < Copies; ++i) values[i] = x.values[i]; + // If Copies > Size, do extra copies. + for (int i = Size, idx = 0; i < Copies; ++i) { + int64_t tmp = x.values[idx]; + benchmark::DoNotOptimize(tmp); + idx = idx + 1 == Size ? 0 : idx + 1; + } + } + + BigType(const BigType& x) { Copy(x); } + BigType& operator=(const BigType& x) { + Copy(x); + return *this; + } + + // Compare only the first Copies elements if Copies is less than Size. + bool operator<(const BigType& other) const { + return std::lexicographical_compare( + values.begin(), values.begin() + std::min(Size, Copies), + other.values.begin(), other.values.begin() + std::min(Size, Copies)); + } + bool operator==(const BigType& other) const { + return std::equal(values.begin(), values.begin() + std::min(Size, Copies), + other.values.begin()); + } + + // Support absl::Hash. + template <typename State> + friend State AbslHashValue(State h, const BigType& b) { + for (int i = 0; i < Size && i < Copies; ++i) + h = State::combine(std::move(h), b.values[i]); + return h; + } + + std::array<int64_t, Size> values; +}; + +#define BIG_TYPE_BENCHMARKS(SIZE, COPIES) \ + using stl_set_size##SIZE##copies##COPIES = std::set<BigType<SIZE, COPIES>>; \ + using stl_map_size##SIZE##copies##COPIES = \ + std::map<BigType<SIZE, COPIES>, intptr_t>; \ + using stl_multiset_size##SIZE##copies##COPIES = \ + std::multiset<BigType<SIZE, COPIES>>; \ + using stl_multimap_size##SIZE##copies##COPIES = \ + std::multimap<BigType<SIZE, COPIES>, intptr_t>; \ + using stl_unordered_set_size##SIZE##copies##COPIES = \ + std::unordered_set<BigType<SIZE, COPIES>, \ + absl::Hash<BigType<SIZE, COPIES>>>; \ + using stl_unordered_map_size##SIZE##copies##COPIES = \ + std::unordered_map<BigType<SIZE, COPIES>, intptr_t, \ + absl::Hash<BigType<SIZE, COPIES>>>; \ + using flat_hash_set_size##SIZE##copies##COPIES = \ + flat_hash_set<BigType<SIZE, COPIES>>; \ + using flat_hash_map_size##SIZE##copies##COPIES = \ + flat_hash_map<BigType<SIZE, COPIES>, intptr_t>; \ + using stl_unordered_multiset_size##SIZE##copies##COPIES = \ + std::unordered_multiset<BigType<SIZE, COPIES>, \ + absl::Hash<BigType<SIZE, COPIES>>>; \ + using stl_unordered_multimap_size##SIZE##copies##COPIES = \ + std::unordered_multimap<BigType<SIZE, COPIES>, intptr_t, \ + absl::Hash<BigType<SIZE, COPIES>>>; \ + using btree_256_set_size##SIZE##copies##COPIES = \ + btree_set<BigType<SIZE, COPIES>>; \ + using btree_256_map_size##SIZE##copies##COPIES = \ + btree_map<BigType<SIZE, COPIES>, intptr_t>; \ + using btree_256_multiset_size##SIZE##copies##COPIES = \ + btree_multiset<BigType<SIZE, COPIES>>; \ + using btree_256_multimap_size##SIZE##copies##COPIES = \ + btree_multimap<BigType<SIZE, COPIES>, intptr_t>; \ + MY_BENCHMARK(size##SIZE##copies##COPIES) + +// Define BIG_TYPE_TESTING to see benchmarks for more big types. +// +// You can use --copt=-DBIG_TYPE_TESTING. +#ifndef NODESIZE_TESTING +#ifdef BIG_TYPE_TESTING +BIG_TYPE_BENCHMARKS(1, 4); +BIG_TYPE_BENCHMARKS(4, 1); +BIG_TYPE_BENCHMARKS(4, 4); +BIG_TYPE_BENCHMARKS(1, 8); +BIG_TYPE_BENCHMARKS(8, 1); +BIG_TYPE_BENCHMARKS(8, 8); +BIG_TYPE_BENCHMARKS(1, 16); +BIG_TYPE_BENCHMARKS(16, 1); +BIG_TYPE_BENCHMARKS(16, 16); +BIG_TYPE_BENCHMARKS(1, 32); +BIG_TYPE_BENCHMARKS(32, 1); +BIG_TYPE_BENCHMARKS(32, 32); +#else +BIG_TYPE_BENCHMARKS(32, 32); +#endif +#endif + +// Benchmark using unique_ptrs to large value types. In order to be able to use +// the same benchmark code as the other types, use a type that holds a +// unique_ptr and has a copy constructor. +template <int Size> +struct BigTypePtr { + BigTypePtr() : BigTypePtr(0) {} + explicit BigTypePtr(int x) { + ptr = absl::make_unique<BigType<Size, Size>>(x); + } + BigTypePtr(const BigTypePtr& x) { + ptr = absl::make_unique<BigType<Size, Size>>(*x.ptr); + } + BigTypePtr(BigTypePtr&& x) noexcept = default; + BigTypePtr& operator=(const BigTypePtr& x) { + ptr = absl::make_unique<BigType<Size, Size>>(*x.ptr); + } + BigTypePtr& operator=(BigTypePtr&& x) noexcept = default; + + bool operator<(const BigTypePtr& other) const { return *ptr < *other.ptr; } + bool operator==(const BigTypePtr& other) const { return *ptr == *other.ptr; } + + std::unique_ptr<BigType<Size, Size>> ptr; +}; + +template <int Size> +double ContainerInfo(const btree_set<BigTypePtr<Size>>& b) { + const double bytes_used = + b.bytes_used() + b.size() * sizeof(BigType<Size, Size>); + const double bytes_per_value = bytes_used / b.size(); + BtreeContainerInfoLog(b, bytes_used, bytes_per_value); + return bytes_per_value; +} +template <int Size> +double ContainerInfo(const btree_map<int, BigTypePtr<Size>>& b) { + const double bytes_used = + b.bytes_used() + b.size() * sizeof(BigType<Size, Size>); + const double bytes_per_value = bytes_used / b.size(); + BtreeContainerInfoLog(b, bytes_used, bytes_per_value); + return bytes_per_value; +} + +#define BIG_TYPE_PTR_BENCHMARKS(SIZE) \ + using stl_set_size##SIZE##copies##SIZE##ptr = std::set<BigType<SIZE, SIZE>>; \ + using stl_map_size##SIZE##copies##SIZE##ptr = \ + std::map<int, BigType<SIZE, SIZE>>; \ + using stl_unordered_set_size##SIZE##copies##SIZE##ptr = \ + std::unordered_set<BigType<SIZE, SIZE>, \ + absl::Hash<BigType<SIZE, SIZE>>>; \ + using stl_unordered_map_size##SIZE##copies##SIZE##ptr = \ + std::unordered_map<int, BigType<SIZE, SIZE>>; \ + using flat_hash_set_size##SIZE##copies##SIZE##ptr = \ + flat_hash_set<BigType<SIZE, SIZE>>; \ + using flat_hash_map_size##SIZE##copies##SIZE##ptr = \ + flat_hash_map<int, BigTypePtr<SIZE>>; \ + using btree_256_set_size##SIZE##copies##SIZE##ptr = \ + btree_set<BigTypePtr<SIZE>>; \ + using btree_256_map_size##SIZE##copies##SIZE##ptr = \ + btree_map<int, BigTypePtr<SIZE>>; \ + MY_BENCHMARK3(stl_set_size##SIZE##copies##SIZE##ptr); \ + MY_BENCHMARK3(stl_unordered_set_size##SIZE##copies##SIZE##ptr); \ + MY_BENCHMARK3(flat_hash_set_size##SIZE##copies##SIZE##ptr); \ + MY_BENCHMARK3(btree_256_set_size##SIZE##copies##SIZE##ptr); \ + MY_BENCHMARK3(stl_map_size##SIZE##copies##SIZE##ptr); \ + MY_BENCHMARK3(stl_unordered_map_size##SIZE##copies##SIZE##ptr); \ + MY_BENCHMARK3(flat_hash_map_size##SIZE##copies##SIZE##ptr); \ + MY_BENCHMARK3(btree_256_map_size##SIZE##copies##SIZE##ptr) + +BIG_TYPE_PTR_BENCHMARKS(32); + +} // namespace +} // namespace container_internal +ABSL_NAMESPACE_END +} // namespace absl |