xref: /aosp_15_r20/external/abseil-cpp/absl/container/btree_benchmark.cc (revision 9356374a3709195abf420251b3e825997ff56c0f)
1 // Copyright 2018 The Abseil Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include <stdint.h>
16 
17 #include <algorithm>
18 #include <functional>
19 #include <map>
20 #include <numeric>
21 #include <random>
22 #include <set>
23 #include <string>
24 #include <type_traits>
25 #include <unordered_map>
26 #include <unordered_set>
27 #include <vector>
28 
29 #include "benchmark/benchmark.h"
30 #include "absl/algorithm/container.h"
31 #include "absl/base/internal/raw_logging.h"
32 #include "absl/container/btree_map.h"
33 #include "absl/container/btree_set.h"
34 #include "absl/container/btree_test.h"
35 #include "absl/container/flat_hash_map.h"
36 #include "absl/container/flat_hash_set.h"
37 #include "absl/container/internal/hashtable_debug.h"
38 #include "absl/hash/hash.h"
39 #include "absl/log/log.h"
40 #include "absl/memory/memory.h"
41 #include "absl/random/random.h"
42 #include "absl/strings/cord.h"
43 #include "absl/strings/str_format.h"
44 #include "absl/time/time.h"
45 
46 namespace absl {
47 ABSL_NAMESPACE_BEGIN
48 namespace container_internal {
49 namespace {
50 
51 constexpr size_t kBenchmarkValues = 1 << 20;
52 
53 // How many times we add and remove sub-batches in one batch of *AddRem
54 // benchmarks.
55 constexpr size_t kAddRemBatchSize = 1 << 2;
56 
57 // Generates n values in the range [0, 4 * n].
58 template <typename V>
GenerateValues(int n)59 std::vector<V> GenerateValues(int n) {
60   constexpr int kSeed = 23;
61   return GenerateValuesWithSeed<V>(n, 4 * n, kSeed);
62 }
63 
64 // Benchmark insertion of values into a container.
65 template <typename T>
BM_InsertImpl(benchmark::State & state,bool sorted)66 void BM_InsertImpl(benchmark::State& state, bool sorted) {
67   using V = typename remove_pair_const<typename T::value_type>::type;
68   typename KeyOfValue<typename T::key_type, V>::type key_of_value;
69 
70   std::vector<V> values = GenerateValues<V>(kBenchmarkValues);
71   if (sorted) {
72     std::sort(values.begin(), values.end());
73   }
74   T container(values.begin(), values.end());
75 
76   // Remove and re-insert 10% of the keys per batch.
77   const int batch_size = (kBenchmarkValues + 9) / 10;
78   while (state.KeepRunningBatch(batch_size)) {
79     state.PauseTiming();
80     const auto i = static_cast<int>(state.iterations());
81 
82     for (int j = i; j < i + batch_size; j++) {
83       int x = j % kBenchmarkValues;
84       container.erase(key_of_value(values[x]));
85     }
86 
87     state.ResumeTiming();
88 
89     for (int j = i; j < i + batch_size; j++) {
90       int x = j % kBenchmarkValues;
91       container.insert(values[x]);
92     }
93   }
94 }
95 
96 template <typename T>
BM_Insert(benchmark::State & state)97 void BM_Insert(benchmark::State& state) {
98   BM_InsertImpl<T>(state, false);
99 }
100 
101 template <typename T>
BM_InsertSorted(benchmark::State & state)102 void BM_InsertSorted(benchmark::State& state) {
103   BM_InsertImpl<T>(state, true);
104 }
105 
106 // Benchmark inserting the first few elements in a container. In b-tree, this is
107 // when the root node grows.
108 template <typename T>
BM_InsertSmall(benchmark::State & state)109 void BM_InsertSmall(benchmark::State& state) {
110   using V = typename remove_pair_const<typename T::value_type>::type;
111 
112   const int kSize = 8;
113   std::vector<V> values = GenerateValues<V>(kSize);
114   T container;
115 
116   while (state.KeepRunningBatch(kSize)) {
117     for (int i = 0; i < kSize; ++i) {
118       benchmark::DoNotOptimize(container.insert(values[i]));
119     }
120     state.PauseTiming();
121     // Do not measure the time it takes to clear the container.
122     container.clear();
123     state.ResumeTiming();
124   }
125 }
126 
127 template <typename T>
BM_LookupImpl(benchmark::State & state,bool sorted)128 void BM_LookupImpl(benchmark::State& state, bool sorted) {
129   using V = typename remove_pair_const<typename T::value_type>::type;
130   typename KeyOfValue<typename T::key_type, V>::type key_of_value;
131 
132   std::vector<V> values = GenerateValues<V>(kBenchmarkValues);
133   if (sorted) {
134     std::sort(values.begin(), values.end());
135   }
136   T container(values.begin(), values.end());
137 
138   while (state.KeepRunning()) {
139     int idx = state.iterations() % kBenchmarkValues;
140     benchmark::DoNotOptimize(container.find(key_of_value(values[idx])));
141   }
142 }
143 
144 // Benchmark lookup of values in a container.
145 template <typename T>
BM_Lookup(benchmark::State & state)146 void BM_Lookup(benchmark::State& state) {
147   BM_LookupImpl<T>(state, false);
148 }
149 
150 // Benchmark lookup of values in a full container, meaning that values
151 // are inserted in-order to take advantage of biased insertion, which
152 // yields a full tree.
153 template <typename T>
BM_FullLookup(benchmark::State & state)154 void BM_FullLookup(benchmark::State& state) {
155   BM_LookupImpl<T>(state, true);
156 }
157 
158 // Benchmark erasing values from a container.
159 template <typename T>
BM_Erase(benchmark::State & state)160 void BM_Erase(benchmark::State& state) {
161   using V = typename remove_pair_const<typename T::value_type>::type;
162   typename KeyOfValue<typename T::key_type, V>::type key_of_value;
163   std::vector<V> values = GenerateValues<V>(kBenchmarkValues);
164   T container(values.begin(), values.end());
165 
166   // Remove and re-insert 10% of the keys per batch.
167   const int batch_size = (kBenchmarkValues + 9) / 10;
168   while (state.KeepRunningBatch(batch_size)) {
169     const int i = state.iterations();
170 
171     for (int j = i; j < i + batch_size; j++) {
172       int x = j % kBenchmarkValues;
173       container.erase(key_of_value(values[x]));
174     }
175 
176     state.PauseTiming();
177     for (int j = i; j < i + batch_size; j++) {
178       int x = j % kBenchmarkValues;
179       container.insert(values[x]);
180     }
181     state.ResumeTiming();
182   }
183 }
184 
185 // Benchmark erasing multiple values from a container.
186 template <typename T>
BM_EraseRange(benchmark::State & state)187 void BM_EraseRange(benchmark::State& state) {
188   using V = typename remove_pair_const<typename T::value_type>::type;
189   typename KeyOfValue<typename T::key_type, V>::type key_of_value;
190   std::vector<V> values = GenerateValues<V>(kBenchmarkValues);
191   T container(values.begin(), values.end());
192 
193   // Remove and re-insert 10% of the keys per batch.
194   const int batch_size = (kBenchmarkValues + 9) / 10;
195   while (state.KeepRunningBatch(batch_size)) {
196     const int i = state.iterations();
197 
198     const int start_index = i % kBenchmarkValues;
199 
200     state.PauseTiming();
201     {
202       std::vector<V> removed;
203       removed.reserve(batch_size);
204       auto itr = container.find(key_of_value(values[start_index]));
205       auto start = itr;
206       for (int j = 0; j < batch_size; j++) {
207         if (itr == container.end()) {
208           state.ResumeTiming();
209           container.erase(start, itr);
210           state.PauseTiming();
211           itr = container.begin();
212           start = itr;
213         }
214         removed.push_back(*itr++);
215       }
216 
217       state.ResumeTiming();
218       container.erase(start, itr);
219       state.PauseTiming();
220 
221       container.insert(removed.begin(), removed.end());
222     }
223     state.ResumeTiming();
224   }
225 }
226 
227 // Predicate that erases every other element. We can't use a lambda because
228 // C++11 doesn't support generic lambdas.
229 // TODO(b/207389011): consider adding benchmarks that remove different fractions
230 // of keys (e.g. 10%, 90%).
231 struct EraseIfPred {
232   uint64_t i = 0;
233   template <typename T>
operator ()absl::container_internal::__anon98250b490111::EraseIfPred234   bool operator()(const T&) {
235     return ++i % 2;
236   }
237 };
238 
239 // Benchmark erasing multiple values from a container with a predicate.
240 template <typename T>
BM_EraseIf(benchmark::State & state)241 void BM_EraseIf(benchmark::State& state) {
242   using V = typename remove_pair_const<typename T::value_type>::type;
243   std::vector<V> values = GenerateValues<V>(kBenchmarkValues);
244 
245   // Removes half of the keys per batch.
246   const int batch_size = (kBenchmarkValues + 1) / 2;
247   EraseIfPred pred;
248   while (state.KeepRunningBatch(batch_size)) {
249     state.PauseTiming();
250     {
251       T container(values.begin(), values.end());
252       state.ResumeTiming();
253       erase_if(container, pred);
254       benchmark::DoNotOptimize(container);
255       state.PauseTiming();
256     }
257     state.ResumeTiming();
258   }
259 }
260 
261 // Benchmark steady-state insert (into first half of range) and remove (from
262 // second half of range), treating the container approximately like a queue with
263 // log-time access for all elements. This benchmark does not test the case where
264 // insertion and removal happen in the same region of the tree.  This benchmark
265 // counts two value constructors.
266 template <typename T>
BM_QueueAddRem(benchmark::State & state)267 void BM_QueueAddRem(benchmark::State& state) {
268   using V = typename remove_pair_const<typename T::value_type>::type;
269   typename KeyOfValue<typename T::key_type, V>::type key_of_value;
270 
271   ABSL_RAW_CHECK(kBenchmarkValues % 2 == 0, "for performance");
272 
273   T container;
274 
275   const size_t half = kBenchmarkValues / 2;
276   std::vector<int> remove_keys(half);
277   std::vector<int> add_keys(half);
278 
279   // We want to do the exact same work repeatedly, and the benchmark can end
280   // after a different number of iterations depending on the speed of the
281   // individual run so we use a large batch size here and ensure that we do
282   // deterministic work every batch.
283   while (state.KeepRunningBatch(half * kAddRemBatchSize)) {
284     state.PauseTiming();
285 
286     container.clear();
287 
288     for (size_t i = 0; i < half; ++i) {
289       remove_keys[i] = i;
290       add_keys[i] = i;
291     }
292     constexpr int kSeed = 5;
293     std::mt19937_64 rand(kSeed);
294     std::shuffle(remove_keys.begin(), remove_keys.end(), rand);
295     std::shuffle(add_keys.begin(), add_keys.end(), rand);
296 
297     // Note needs lazy generation of values.
298     Generator<V> g(kBenchmarkValues * kAddRemBatchSize);
299 
300     for (size_t i = 0; i < half; ++i) {
301       container.insert(g(add_keys[i]));
302       container.insert(g(half + remove_keys[i]));
303     }
304 
305     // There are three parts each of size "half":
306     // 1 is being deleted from  [offset - half, offset)
307     // 2 is standing            [offset, offset + half)
308     // 3 is being inserted into [offset + half, offset + 2 * half)
309     size_t offset = 0;
310 
311     for (size_t i = 0; i < kAddRemBatchSize; ++i) {
312       std::shuffle(remove_keys.begin(), remove_keys.end(), rand);
313       std::shuffle(add_keys.begin(), add_keys.end(), rand);
314       offset += half;
315 
316       state.ResumeTiming();
317       for (size_t idx = 0; idx < half; ++idx) {
318         container.erase(key_of_value(g(offset - half + remove_keys[idx])));
319         container.insert(g(offset + half + add_keys[idx]));
320       }
321       state.PauseTiming();
322     }
323     state.ResumeTiming();
324   }
325 }
326 
327 // Mixed insertion and deletion in the same range using pre-constructed values.
328 template <typename T>
BM_MixedAddRem(benchmark::State & state)329 void BM_MixedAddRem(benchmark::State& state) {
330   using V = typename remove_pair_const<typename T::value_type>::type;
331   typename KeyOfValue<typename T::key_type, V>::type key_of_value;
332 
333   ABSL_RAW_CHECK(kBenchmarkValues % 2 == 0, "for performance");
334 
335   T container;
336 
337   // Create two random shuffles
338   std::vector<int> remove_keys(kBenchmarkValues);
339   std::vector<int> add_keys(kBenchmarkValues);
340 
341   // We want to do the exact same work repeatedly, and the benchmark can end
342   // after a different number of iterations depending on the speed of the
343   // individual run so we use a large batch size here and ensure that we do
344   // deterministic work every batch.
345   while (state.KeepRunningBatch(kBenchmarkValues * kAddRemBatchSize)) {
346     state.PauseTiming();
347 
348     container.clear();
349 
350     constexpr int kSeed = 7;
351     std::mt19937_64 rand(kSeed);
352 
353     std::vector<V> values = GenerateValues<V>(kBenchmarkValues * 2);
354 
355     // Insert the first half of the values (already in random order)
356     container.insert(values.begin(), values.begin() + kBenchmarkValues);
357 
358     // Insert the first half of the values (already in random order)
359     for (size_t i = 0; i < kBenchmarkValues; ++i) {
360       // remove_keys and add_keys will be swapped before each round,
361       // therefore fill add_keys here w/ the keys being inserted, so
362       // they'll be the first to be removed.
363       remove_keys[i] = i + kBenchmarkValues;
364       add_keys[i] = i;
365     }
366 
367     for (size_t i = 0; i < kAddRemBatchSize; ++i) {
368       remove_keys.swap(add_keys);
369       std::shuffle(remove_keys.begin(), remove_keys.end(), rand);
370       std::shuffle(add_keys.begin(), add_keys.end(), rand);
371 
372       state.ResumeTiming();
373       for (size_t idx = 0; idx < kBenchmarkValues; ++idx) {
374         container.erase(key_of_value(values[remove_keys[idx]]));
375         container.insert(values[add_keys[idx]]);
376       }
377       state.PauseTiming();
378     }
379     state.ResumeTiming();
380   }
381 }
382 
383 // Insertion at end, removal from the beginning.  This benchmark
384 // counts two value constructors.
385 // TODO(ezb): we could add a GenerateNext version of generator that could reduce
386 // noise for string-like types.
387 template <typename T>
BM_Fifo(benchmark::State & state)388 void BM_Fifo(benchmark::State& state) {
389   using V = typename remove_pair_const<typename T::value_type>::type;
390 
391   T container;
392   // Need lazy generation of values as state.max_iterations is large.
393   Generator<V> g(kBenchmarkValues + state.max_iterations);
394 
395   for (int i = 0; i < kBenchmarkValues; i++) {
396     container.insert(g(i));
397   }
398 
399   while (state.KeepRunning()) {
400     container.erase(container.begin());
401     container.insert(container.end(), g(state.iterations() + kBenchmarkValues));
402   }
403 }
404 
405 // Iteration (forward) through the tree
406 template <typename T>
BM_FwdIter(benchmark::State & state)407 void BM_FwdIter(benchmark::State& state) {
408   using V = typename remove_pair_const<typename T::value_type>::type;
409   using R = typename T::value_type const*;
410 
411   std::vector<V> values = GenerateValues<V>(kBenchmarkValues);
412   T container(values.begin(), values.end());
413 
414   auto iter = container.end();
415 
416   R r = nullptr;
417 
418   while (state.KeepRunning()) {
419     if (iter == container.end()) iter = container.begin();
420     r = &(*iter);
421     ++iter;
422   }
423 
424   benchmark::DoNotOptimize(r);
425 }
426 
427 // Benchmark random range-construction of a container.
428 template <typename T>
BM_RangeConstructionImpl(benchmark::State & state,bool sorted)429 void BM_RangeConstructionImpl(benchmark::State& state, bool sorted) {
430   using V = typename remove_pair_const<typename T::value_type>::type;
431 
432   std::vector<V> values = GenerateValues<V>(kBenchmarkValues);
433   if (sorted) {
434     std::sort(values.begin(), values.end());
435   }
436   {
437     T container(values.begin(), values.end());
438   }
439 
440   while (state.KeepRunning()) {
441     T container(values.begin(), values.end());
442     benchmark::DoNotOptimize(container);
443   }
444 }
445 
446 template <typename T>
BM_InsertRangeRandom(benchmark::State & state)447 void BM_InsertRangeRandom(benchmark::State& state) {
448   BM_RangeConstructionImpl<T>(state, false);
449 }
450 
451 template <typename T>
BM_InsertRangeSorted(benchmark::State & state)452 void BM_InsertRangeSorted(benchmark::State& state) {
453   BM_RangeConstructionImpl<T>(state, true);
454 }
455 
456 #define STL_ORDERED_TYPES(value)                     \
457   using stl_set_##value = std::set<value>;           \
458   using stl_map_##value = std::map<value, intptr_t>; \
459   using stl_multiset_##value = std::multiset<value>; \
460   using stl_multimap_##value = std::multimap<value, intptr_t>
461 
462 using StdString = std::string;
463 STL_ORDERED_TYPES(int32_t);
464 STL_ORDERED_TYPES(int64_t);
465 STL_ORDERED_TYPES(StdString);
466 STL_ORDERED_TYPES(Cord);
467 STL_ORDERED_TYPES(Time);
468 
469 #define STL_UNORDERED_TYPES(value)                                       \
470   using stl_unordered_set_##value = std::unordered_set<value>;           \
471   using stl_unordered_map_##value = std::unordered_map<value, intptr_t>; \
472   using flat_hash_set_##value = flat_hash_set<value>;                    \
473   using flat_hash_map_##value = flat_hash_map<value, intptr_t>;          \
474   using stl_unordered_multiset_##value = std::unordered_multiset<value>; \
475   using stl_unordered_multimap_##value =                                 \
476       std::unordered_multimap<value, intptr_t>
477 
478 #define STL_UNORDERED_TYPES_CUSTOM_HASH(value, hash)                           \
479   using stl_unordered_set_##value = std::unordered_set<value, hash>;           \
480   using stl_unordered_map_##value = std::unordered_map<value, intptr_t, hash>; \
481   using flat_hash_set_##value = flat_hash_set<value, hash>;                    \
482   using flat_hash_map_##value = flat_hash_map<value, intptr_t, hash>;          \
483   using stl_unordered_multiset_##value = std::unordered_multiset<value, hash>; \
484   using stl_unordered_multimap_##value =                                       \
485       std::unordered_multimap<value, intptr_t, hash>
486 
487 STL_UNORDERED_TYPES_CUSTOM_HASH(Cord, absl::Hash<absl::Cord>);
488 
489 STL_UNORDERED_TYPES(int32_t);
490 STL_UNORDERED_TYPES(int64_t);
491 STL_UNORDERED_TYPES(StdString);
492 STL_UNORDERED_TYPES_CUSTOM_HASH(Time, absl::Hash<absl::Time>);
493 
494 #define BTREE_TYPES(value)                                            \
495   using btree_256_set_##value =                                       \
496       btree_set<value, std::less<value>, std::allocator<value>>;      \
497   using btree_256_map_##value =                                       \
498       btree_map<value, intptr_t, std::less<value>,                    \
499                 std::allocator<std::pair<const value, intptr_t>>>;    \
500   using btree_256_multiset_##value =                                  \
501       btree_multiset<value, std::less<value>, std::allocator<value>>; \
502   using btree_256_multimap_##value =                                  \
503       btree_multimap<value, intptr_t, std::less<value>,               \
504                      std::allocator<std::pair<const value, intptr_t>>>
505 
506 BTREE_TYPES(int32_t);
507 BTREE_TYPES(int64_t);
508 BTREE_TYPES(StdString);
509 BTREE_TYPES(Cord);
510 BTREE_TYPES(Time);
511 
512 #define MY_BENCHMARK4(type, func)                                              \
513   void BM_##type##_##func(benchmark::State& state) { BM_##func<type>(state); } \
514   BENCHMARK(BM_##type##_##func)
515 
516 #define MY_BENCHMARK3_STL(type)           \
517   MY_BENCHMARK4(type, Insert);            \
518   MY_BENCHMARK4(type, InsertSorted);      \
519   MY_BENCHMARK4(type, InsertSmall);       \
520   MY_BENCHMARK4(type, Lookup);            \
521   MY_BENCHMARK4(type, FullLookup);        \
522   MY_BENCHMARK4(type, Erase);             \
523   MY_BENCHMARK4(type, EraseRange);        \
524   MY_BENCHMARK4(type, QueueAddRem);       \
525   MY_BENCHMARK4(type, MixedAddRem);       \
526   MY_BENCHMARK4(type, Fifo);              \
527   MY_BENCHMARK4(type, FwdIter);           \
528   MY_BENCHMARK4(type, InsertRangeRandom); \
529   MY_BENCHMARK4(type, InsertRangeSorted)
530 
531 #define MY_BENCHMARK3(type)     \
532   MY_BENCHMARK4(type, EraseIf); \
533   MY_BENCHMARK3_STL(type)
534 
535 #define MY_BENCHMARK2_SUPPORTS_MULTI_ONLY(type) \
536   MY_BENCHMARK3_STL(stl_##type);                \
537   MY_BENCHMARK3_STL(stl_unordered_##type);      \
538   MY_BENCHMARK3(btree_256_##type)
539 
540 #define MY_BENCHMARK2(type)                \
541   MY_BENCHMARK2_SUPPORTS_MULTI_ONLY(type); \
542   MY_BENCHMARK3(flat_hash_##type)
543 
544 // Define MULTI_TESTING to see benchmarks for multi-containers also.
545 //
546 // You can use --copt=-DMULTI_TESTING.
547 #ifdef MULTI_TESTING
548 #define MY_BENCHMARK(type)                            \
549   MY_BENCHMARK2(set_##type);                          \
550   MY_BENCHMARK2(map_##type);                          \
551   MY_BENCHMARK2_SUPPORTS_MULTI_ONLY(multiset_##type); \
552   MY_BENCHMARK2_SUPPORTS_MULTI_ONLY(multimap_##type)
553 #else
554 #define MY_BENCHMARK(type)   \
555   MY_BENCHMARK2(set_##type); \
556   MY_BENCHMARK2(map_##type)
557 #endif
558 
559 MY_BENCHMARK(int32_t);
560 MY_BENCHMARK(int64_t);
561 MY_BENCHMARK(StdString);
562 MY_BENCHMARK(Cord);
563 MY_BENCHMARK(Time);
564 
565 // Define a type whose size and cost of moving are independently customizable.
566 // When sizeof(value_type) increases, we expect btree to no longer have as much
567 // cache-locality advantage over STL. When cost of moving increases, we expect
568 // btree to actually do more work than STL because it has to move values around
569 // and STL doesn't have to.
570 template <int Size, int Copies>
571 struct BigType {
BigTypeabsl::container_internal::__anon98250b490111::BigType572   BigType() : BigType(0) {}
BigTypeabsl::container_internal::__anon98250b490111::BigType573   explicit BigType(int x) { std::iota(values.begin(), values.end(), x); }
574 
Copyabsl::container_internal::__anon98250b490111::BigType575   void Copy(const BigType& other) {
576     for (int i = 0; i < Size && i < Copies; ++i) values[i] = other.values[i];
577     // If Copies > Size, do extra copies.
578     for (int i = Size, idx = 0; i < Copies; ++i) {
579       int64_t tmp = other.values[idx];
580       benchmark::DoNotOptimize(tmp);
581       idx = idx + 1 == Size ? 0 : idx + 1;
582     }
583   }
584 
BigTypeabsl::container_internal::__anon98250b490111::BigType585   BigType(const BigType& other) { Copy(other); }
operator =absl::container_internal::__anon98250b490111::BigType586   BigType& operator=(const BigType& other) {
587     Copy(other);
588     return *this;
589   }
590 
591   // Compare only the first Copies elements if Copies is less than Size.
operator <absl::container_internal::__anon98250b490111::BigType592   bool operator<(const BigType& other) const {
593     return std::lexicographical_compare(
594         values.begin(), values.begin() + std::min(Size, Copies),
595         other.values.begin(), other.values.begin() + std::min(Size, Copies));
596   }
operator ==absl::container_internal::__anon98250b490111::BigType597   bool operator==(const BigType& other) const {
598     return std::equal(values.begin(), values.begin() + std::min(Size, Copies),
599                       other.values.begin());
600   }
601 
602   // Support absl::Hash.
603   template <typename State>
AbslHashValue(State h,const BigType & b)604   friend State AbslHashValue(State h, const BigType& b) {
605     for (int i = 0; i < Size && i < Copies; ++i)
606       h = State::combine(std::move(h), b.values[i]);
607     return h;
608   }
609 
610   std::array<int64_t, Size> values;
611 };
612 
613 #define BIG_TYPE_BENCHMARKS(SIZE, COPIES)                                     \
614   using stl_set_size##SIZE##copies##COPIES = std::set<BigType<SIZE, COPIES>>; \
615   using stl_map_size##SIZE##copies##COPIES =                                  \
616       std::map<BigType<SIZE, COPIES>, intptr_t>;                              \
617   using stl_multiset_size##SIZE##copies##COPIES =                             \
618       std::multiset<BigType<SIZE, COPIES>>;                                   \
619   using stl_multimap_size##SIZE##copies##COPIES =                             \
620       std::multimap<BigType<SIZE, COPIES>, intptr_t>;                         \
621   using stl_unordered_set_size##SIZE##copies##COPIES =                        \
622       std::unordered_set<BigType<SIZE, COPIES>,                               \
623                          absl::Hash<BigType<SIZE, COPIES>>>;                  \
624   using stl_unordered_map_size##SIZE##copies##COPIES =                        \
625       std::unordered_map<BigType<SIZE, COPIES>, intptr_t,                     \
626                          absl::Hash<BigType<SIZE, COPIES>>>;                  \
627   using flat_hash_set_size##SIZE##copies##COPIES =                            \
628       flat_hash_set<BigType<SIZE, COPIES>>;                                   \
629   using flat_hash_map_size##SIZE##copies##COPIES =                            \
630       flat_hash_map<BigType<SIZE, COPIES>, intptr_t>;                         \
631   using stl_unordered_multiset_size##SIZE##copies##COPIES =                   \
632       std::unordered_multiset<BigType<SIZE, COPIES>,                          \
633                               absl::Hash<BigType<SIZE, COPIES>>>;             \
634   using stl_unordered_multimap_size##SIZE##copies##COPIES =                   \
635       std::unordered_multimap<BigType<SIZE, COPIES>, intptr_t,                \
636                               absl::Hash<BigType<SIZE, COPIES>>>;             \
637   using btree_256_set_size##SIZE##copies##COPIES =                            \
638       btree_set<BigType<SIZE, COPIES>>;                                       \
639   using btree_256_map_size##SIZE##copies##COPIES =                            \
640       btree_map<BigType<SIZE, COPIES>, intptr_t>;                             \
641   using btree_256_multiset_size##SIZE##copies##COPIES =                       \
642       btree_multiset<BigType<SIZE, COPIES>>;                                  \
643   using btree_256_multimap_size##SIZE##copies##COPIES =                       \
644       btree_multimap<BigType<SIZE, COPIES>, intptr_t>;                        \
645   MY_BENCHMARK(size##SIZE##copies##COPIES)
646 
647 // Define BIG_TYPE_TESTING to see benchmarks for more big types.
648 //
649 // You can use --copt=-DBIG_TYPE_TESTING.
650 #ifndef NODESIZE_TESTING
651 #ifdef BIG_TYPE_TESTING
652 BIG_TYPE_BENCHMARKS(1, 4);
653 BIG_TYPE_BENCHMARKS(4, 1);
654 BIG_TYPE_BENCHMARKS(4, 4);
655 BIG_TYPE_BENCHMARKS(1, 8);
656 BIG_TYPE_BENCHMARKS(8, 1);
657 BIG_TYPE_BENCHMARKS(8, 8);
658 BIG_TYPE_BENCHMARKS(1, 16);
659 BIG_TYPE_BENCHMARKS(16, 1);
660 BIG_TYPE_BENCHMARKS(16, 16);
661 BIG_TYPE_BENCHMARKS(1, 32);
662 BIG_TYPE_BENCHMARKS(32, 1);
663 BIG_TYPE_BENCHMARKS(32, 32);
664 #else
665 BIG_TYPE_BENCHMARKS(32, 32);
666 #endif
667 #endif
668 
669 // Benchmark using unique_ptrs to large value types. In order to be able to use
670 // the same benchmark code as the other types, use a type that holds a
671 // unique_ptr and has a copy constructor.
672 template <int Size>
673 struct BigTypePtr {
BigTypePtrabsl::container_internal::__anon98250b490111::BigTypePtr674   BigTypePtr() : BigTypePtr(0) {}
BigTypePtrabsl::container_internal::__anon98250b490111::BigTypePtr675   explicit BigTypePtr(int x) {
676     ptr = absl::make_unique<BigType<Size, Size>>(x);
677   }
BigTypePtrabsl::container_internal::__anon98250b490111::BigTypePtr678   BigTypePtr(const BigTypePtr& other) {
679     ptr = absl::make_unique<BigType<Size, Size>>(*other.ptr);
680   }
681   BigTypePtr(BigTypePtr&& other) noexcept = default;
operator =absl::container_internal::__anon98250b490111::BigTypePtr682   BigTypePtr& operator=(const BigTypePtr& other) {
683     ptr = absl::make_unique<BigType<Size, Size>>(*other.ptr);
684   }
685   BigTypePtr& operator=(BigTypePtr&& other) noexcept = default;
686 
operator <absl::container_internal::__anon98250b490111::BigTypePtr687   bool operator<(const BigTypePtr& other) const { return *ptr < *other.ptr; }
operator ==absl::container_internal::__anon98250b490111::BigTypePtr688   bool operator==(const BigTypePtr& other) const { return *ptr == *other.ptr; }
689 
690   std::unique_ptr<BigType<Size, Size>> ptr;
691 };
692 
693 template <int Size>
ContainerInfo(const btree_set<BigTypePtr<Size>> & b)694 double ContainerInfo(const btree_set<BigTypePtr<Size>>& b) {
695   const double bytes_used =
696       b.bytes_used() + b.size() * sizeof(BigType<Size, Size>);
697   const double bytes_per_value = bytes_used / b.size();
698   BtreeContainerInfoLog(b, bytes_used, bytes_per_value);
699   return bytes_per_value;
700 }
701 template <int Size>
ContainerInfo(const btree_map<int,BigTypePtr<Size>> & b)702 double ContainerInfo(const btree_map<int, BigTypePtr<Size>>& b) {
703   const double bytes_used =
704       b.bytes_used() + b.size() * sizeof(BigType<Size, Size>);
705   const double bytes_per_value = bytes_used / b.size();
706   BtreeContainerInfoLog(b, bytes_used, bytes_per_value);
707   return bytes_per_value;
708 }
709 
710 #define BIG_TYPE_PTR_BENCHMARKS(SIZE)                                          \
711   using stl_set_size##SIZE##copies##SIZE##ptr = std::set<BigType<SIZE, SIZE>>; \
712   using stl_map_size##SIZE##copies##SIZE##ptr =                                \
713       std::map<int, BigType<SIZE, SIZE>>;                                      \
714   using stl_unordered_set_size##SIZE##copies##SIZE##ptr =                      \
715       std::unordered_set<BigType<SIZE, SIZE>,                                  \
716                          absl::Hash<BigType<SIZE, SIZE>>>;                     \
717   using stl_unordered_map_size##SIZE##copies##SIZE##ptr =                      \
718       std::unordered_map<int, BigType<SIZE, SIZE>>;                            \
719   using flat_hash_set_size##SIZE##copies##SIZE##ptr =                          \
720       flat_hash_set<BigType<SIZE, SIZE>>;                                      \
721   using flat_hash_map_size##SIZE##copies##SIZE##ptr =                          \
722       flat_hash_map<int, BigTypePtr<SIZE>>;                                    \
723   using btree_256_set_size##SIZE##copies##SIZE##ptr =                          \
724       btree_set<BigTypePtr<SIZE>>;                                             \
725   using btree_256_map_size##SIZE##copies##SIZE##ptr =                          \
726       btree_map<int, BigTypePtr<SIZE>>;                                        \
727   MY_BENCHMARK3_STL(stl_set_size##SIZE##copies##SIZE##ptr);                    \
728   MY_BENCHMARK3_STL(stl_unordered_set_size##SIZE##copies##SIZE##ptr);          \
729   MY_BENCHMARK3(flat_hash_set_size##SIZE##copies##SIZE##ptr);                  \
730   MY_BENCHMARK3(btree_256_set_size##SIZE##copies##SIZE##ptr);                  \
731   MY_BENCHMARK3_STL(stl_map_size##SIZE##copies##SIZE##ptr);                    \
732   MY_BENCHMARK3_STL(stl_unordered_map_size##SIZE##copies##SIZE##ptr);          \
733   MY_BENCHMARK3(flat_hash_map_size##SIZE##copies##SIZE##ptr);                  \
734   MY_BENCHMARK3(btree_256_map_size##SIZE##copies##SIZE##ptr)
735 
736 BIG_TYPE_PTR_BENCHMARKS(32);
737 
BM_BtreeSet_IteratorSubtraction(benchmark::State & state)738 void BM_BtreeSet_IteratorSubtraction(benchmark::State& state) {
739   absl::InsecureBitGen bitgen;
740   std::vector<int> vec;
741   // Randomize the set's insertion order so the nodes aren't all full.
742   vec.reserve(state.range(0));
743   for (int i = 0; i < state.range(0); ++i) vec.push_back(i);
744   absl::c_shuffle(vec, bitgen);
745 
746   absl::btree_set<int> set;
747   for (int i : vec) set.insert(i);
748 
749   size_t distance = absl::Uniform(bitgen, 0u, set.size());
750   while (state.KeepRunningBatch(distance)) {
751     size_t end = absl::Uniform(bitgen, distance, set.size());
752     size_t begin = end - distance;
753     benchmark::DoNotOptimize(set.find(static_cast<int>(end)) -
754                              set.find(static_cast<int>(begin)));
755     distance = absl::Uniform(bitgen, 0u, set.size());
756   }
757 }
758 
759 BENCHMARK(BM_BtreeSet_IteratorSubtraction)->Range(1 << 10, 1 << 20);
760 
761 }  // namespace
762 }  // namespace container_internal
763 ABSL_NAMESPACE_END
764 }  // namespace absl
765