| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735 | // 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/cord.h"#include "absl/strings/str_format.h"#include "absl/time/time.h"#include "benchmark/benchmark.h"namespace absl {ABSL_NAMESPACE_BEGINnamespace 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));  }}// 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 treetemplate <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, InsertEnd);         \  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)#endifMY_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_TESTINGBIG_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);#elseBIG_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_internalABSL_NAMESPACE_END}  // namespace absl
 |