123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701 |
- // 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 "benchmark/benchmark.h"
- #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/cord.h"
- #include "absl/strings/str_format.h"
- #include "absl/time/time.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);
- }
- // Benchmark inserting the first few elements in a container. In b-tree, this is
- // when the root node grows.
- template <typename T>
- void BM_InsertSmall(benchmark::State& state) {
- using V = typename remove_pair_const<typename T::value_type>::type;
- const int kSize = 8;
- std::vector<V> values = GenerateValues<V>(kSize);
- T container;
- while (state.KeepRunningBatch(kSize)) {
- for (int i = 0; i < kSize; ++i) {
- benchmark::DoNotOptimize(container.insert(values[i]));
- }
- state.PauseTiming();
- // Do not measure the time it takes to clear the container.
- container.clear();
- state.ResumeTiming();
- }
- }
- 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(Cord);
- 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_CUSTOM_HASH(Cord, absl::Hash<absl::Cord>);
- 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(Cord);
- 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, InsertSmall); \
- 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(Cord);
- 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& other) {
- for (int i = 0; i < Size && i < Copies; ++i) values[i] = other.values[i];
- // If Copies > Size, do extra copies.
- for (int i = Size, idx = 0; i < Copies; ++i) {
- int64_t tmp = other.values[idx];
- benchmark::DoNotOptimize(tmp);
- idx = idx + 1 == Size ? 0 : idx + 1;
- }
- }
- BigType(const BigType& other) { Copy(other); }
- BigType& operator=(const BigType& other) {
- Copy(other);
- 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& other) {
- ptr = absl::make_unique<BigType<Size, Size>>(*other.ptr);
- }
- BigTypePtr(BigTypePtr&& other) noexcept = default;
- BigTypePtr& operator=(const BigTypePtr& other) {
- ptr = absl::make_unique<BigType<Size, Size>>(*other.ptr);
- }
- BigTypePtr& operator=(BigTypePtr&& other) 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
|