blob: dd1884f65032e5bff6e6b5ab2c1169d9391e99a1 [file] [log] [blame]
Mark de Wevera8444d02020-09-15 11:08:13 -04001//===----------------------------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include <algorithm>
10#include <cstdint>
11#include <map>
12#include <random>
13#include <vector>
14
15#include "CartesianBenchmarks.h"
16#include "benchmark/benchmark.h"
17#include "test_macros.h"
18
19// When VALIDATE is defined the benchmark will run to validate the benchmarks.
20// The time taken by several operations depend on whether or not an element
21// exists. To avoid errors in the benchmark these operations have a validation
22// mode to test the benchmark. Since they are not meant to be benchmarked the
23// number of sizes tested is limited to 1.
24//#define VALIDATE
25
26namespace {
27
28enum class Mode { Hit, Miss };
29
30struct AllModes : EnumValuesAsTuple<AllModes, Mode, 2> {
31 static constexpr const char* Names[] = {"ExistingElement", "NewElement"};
32};
33
34// The positions of the hints to pick:
35// - Begin picks the first item. The item cannot be put before this element.
36// - Thrid picks the third item. This is just an element with a valid entry
37// before and after it.
38// - Correct contains the correct hint.
39// - End contains a hint to the end of the map.
40enum class Hint { Begin, Third, Correct, End };
41struct AllHints : EnumValuesAsTuple<AllHints, Hint, 4> {
42 static constexpr const char* Names[] = {"Begin", "Third", "Correct", "End"};
43};
44
45enum class Order { Sorted, Random };
46struct AllOrders : EnumValuesAsTuple<AllOrders, Order, 2> {
47 static constexpr const char* Names[] = {"Sorted", "Random"};
48};
49
50struct TestSets {
51 std::vector<uint64_t> Keys;
52 std::vector<std::map<uint64_t, int64_t> > Maps;
53 std::vector<
54 std::vector<typename std::map<uint64_t, int64_t>::const_iterator> >
55 Hints;
56};
57
58enum class Shuffle { None, Keys, Hints };
59
60TestSets makeTestingSets(size_t MapSize, Mode mode, Shuffle shuffle,
61 size_t max_maps) {
62 /*
63 * The shuffle does not retain the random number generator to use the same
64 * set of random numbers for every iteration.
65 */
66 TestSets R;
67
68 int MapCount = std::min(max_maps, 1000000 / MapSize);
69
70 for (uint64_t I = 0; I < MapSize; ++I) {
71 R.Keys.push_back(mode == Mode::Hit ? 2 * I + 2 : 2 * I + 1);
72 }
73 if (shuffle == Shuffle::Keys)
74 std::shuffle(R.Keys.begin(), R.Keys.end(), std::mt19937());
75
76 for (int M = 0; M < MapCount; ++M) {
77 auto& map = R.Maps.emplace_back();
78 auto& hints = R.Hints.emplace_back();
79 for (uint64_t I = 0; I < MapSize; ++I) {
80 hints.push_back(map.insert(std::make_pair(2 * I + 2, 0)).first);
81 }
82 if (shuffle == Shuffle::Hints)
83 std::shuffle(hints.begin(), hints.end(), std::mt19937());
84 }
85
86 return R;
87}
88
89struct Base {
90 size_t MapSize;
91 Base(size_t T) : MapSize(T) {}
92
93 std::string baseName() const { return "_MapSize=" + std::to_string(MapSize); }
94};
95
96//*******************************************************************|
97// Member functions |
98//*******************************************************************|
99
100struct ConstructorDefault {
101 void run(benchmark::State& State) const {
102 for (auto _ : State) {
103 benchmark::DoNotOptimize(std::map<uint64_t, int64_t>());
104 }
105 }
106
107 std::string name() const { return "BM_ConstructorDefault"; }
108};
109
110struct ConstructorIterator : Base {
111 using Base::Base;
112
113 void run(benchmark::State& State) const {
114 auto Data = makeTestingSets(MapSize, Mode::Hit, Shuffle::None, 1);
115 auto& Map = Data.Maps.front();
116 while (State.KeepRunningBatch(MapSize)) {
117#ifndef VALIDATE
118 benchmark::DoNotOptimize(
119 std::map<uint64_t, int64_t>(Map.begin(), Map.end()));
120#else
121 std::map<uint64_t, int64_t> M{Map.begin(), Map.end()};
122 if (M != Map)
123 State.SkipWithError("Map copy not identical");
124#endif
125 }
126 }
127
128 std::string name() const { return "BM_ConstructorIterator" + baseName(); }
129};
130
131struct ConstructorCopy : Base {
132 using Base::Base;
133
134 void run(benchmark::State& State) const {
135 auto Data = makeTestingSets(MapSize, Mode::Hit, Shuffle::None, 1);
136 auto& Map = Data.Maps.front();
137 while (State.KeepRunningBatch(MapSize)) {
138#ifndef VALIDATE
139 std::map<uint64_t, int64_t> M(Map);
140 benchmark::DoNotOptimize(M);
141#else
142 std::map<uint64_t, int64_t> M(Map);
143 if (M != Map)
144 State.SkipWithError("Map copy not identical");
145#endif
146 }
147 }
148
149 std::string name() const { return "BM_ConstructorCopy" + baseName(); }
150};
151
152struct ConstructorMove : Base {
153 using Base::Base;
154
155 void run(benchmark::State& State) const {
156 auto Data = makeTestingSets(MapSize, Mode::Hit, Shuffle::None, 1000);
157 while (State.KeepRunningBatch(MapSize * Data.Maps.size())) {
158 for (auto& Map : Data.Maps) {
159 std::map<uint64_t, int64_t> M(std::move(Map));
160 benchmark::DoNotOptimize(M);
161 }
162 State.PauseTiming();
163 Data = makeTestingSets(MapSize, Mode::Hit, Shuffle::None, 1000);
164 State.ResumeTiming();
165 }
166 }
167
168 std::string name() const { return "BM_ConstructorMove" + baseName(); }
169};
170
171//*******************************************************************|
172// Capacity |
173//*******************************************************************|
174
175struct Empty : Base {
176 using Base::Base;
177
178 void run(benchmark::State& State) const {
179 auto Data = makeTestingSets(MapSize, Mode::Hit, Shuffle::None, 1);
180 auto& Map = Data.Maps.front();
181 for (auto _ : State) {
182#ifndef VALIDATE
183 benchmark::DoNotOptimize(Map.empty());
184#else
185 if (Map.empty())
186 State.SkipWithError("Map contains an invalid number of elements.");
187#endif
188 }
189 }
190
191 std::string name() const { return "BM_Empty" + baseName(); }
192};
193
194struct Size : Base {
195 using Base::Base;
196
197 void run(benchmark::State& State) const {
198 auto Data = makeTestingSets(MapSize, Mode::Hit, Shuffle::None, 1);
199 auto& Map = Data.Maps.front();
200 for (auto _ : State) {
201#ifndef VALIDATE
202 benchmark::DoNotOptimize(Map.size());
203#else
204 if (Map.size() != MapSize)
205 State.SkipWithError("Map contains an invalid number of elements.");
206#endif
207 }
208 }
209
210 std::string name() const { return "BM_Size" + baseName(); }
211};
212
213//*******************************************************************|
214// Modifiers |
215//*******************************************************************|
216
217struct Clear : Base {
218 using Base::Base;
219
220 void run(benchmark::State& State) const {
221 auto Data = makeTestingSets(MapSize, Mode::Hit, Shuffle::None, 1000);
222 while (State.KeepRunningBatch(MapSize * Data.Maps.size())) {
223 for (auto& Map : Data.Maps) {
224 Map.clear();
225 benchmark::DoNotOptimize(Map);
226 }
227 State.PauseTiming();
228 Data = makeTestingSets(MapSize, Mode::Hit, Shuffle::None, 1000);
229 State.ResumeTiming();
230 }
231 }
232
233 std::string name() const { return "BM_Clear" + baseName(); }
234};
235
236template <class Mode, class Order>
237struct Insert : Base {
238 using Base::Base;
239
240 void run(benchmark::State& State) const {
241 auto Data = makeTestingSets(
242 MapSize, Mode(),
243 Order::value == ::Order::Random ? Shuffle::Keys : Shuffle::None, 1000);
244 while (State.KeepRunningBatch(MapSize * Data.Maps.size())) {
245 for (auto& Map : Data.Maps) {
246 for (auto K : Data.Keys) {
247#ifndef VALIDATE
248 benchmark::DoNotOptimize(Map.insert(std::make_pair(K, 1)));
249#else
250 bool Inserted = Map.insert(std::make_pair(K, 1)).second;
251 if (Mode() == ::Mode::Hit) {
252 if (Inserted)
253 State.SkipWithError("Inserted a duplicate element");
254 } else {
255 if (!Inserted)
256 State.SkipWithError("Failed to insert e new element");
257 }
258#endif
259 }
260 }
261
262 State.PauseTiming();
263 Data = makeTestingSets(MapSize, Mode(),
264 Order::value == ::Order::Random ? Shuffle::Keys
265 : Shuffle::None,
266 1000);
267 State.ResumeTiming();
268 }
269 }
270
271 std::string name() const {
272 return "BM_Insert" + baseName() + Mode::name() + Order::name();
273 }
274};
275
276template <class Mode, class Hint>
277struct InsertHint : Base {
278 using Base::Base;
279
280 template < ::Hint hint>
281 typename std::enable_if<hint == ::Hint::Correct>::type
282 run(benchmark::State& State) const {
283 auto Data = makeTestingSets(MapSize, Mode(), Shuffle::None, 1000);
284 while (State.KeepRunningBatch(MapSize * Data.Maps.size())) {
285 for (size_t I = 0; I < Data.Maps.size(); ++I) {
286 auto& Map = Data.Maps[I];
287 auto H = Data.Hints[I].begin();
288 for (auto K : Data.Keys) {
289#ifndef VALIDATE
290 benchmark::DoNotOptimize(Map.insert(*H, std::make_pair(K, 1)));
291#else
292 auto Inserted = Map.insert(*H, std::make_pair(K, 1));
293 if (Mode() == ::Mode::Hit) {
294 if (Inserted != *H)
295 State.SkipWithError("Inserted a duplicate element");
296 } else {
297 if (++Inserted != *H)
298 State.SkipWithError("Failed to insert a new element");
299 }
300#endif
301 ++H;
302 }
303 }
304
305 State.PauseTiming();
306 Data = makeTestingSets(MapSize, Mode(), Shuffle::None, 1000);
307 State.ResumeTiming();
308 }
309 }
310
311 template < ::Hint hint>
312 typename std::enable_if<hint != ::Hint::Correct>::type
313 run(benchmark::State& State) const {
314 auto Data = makeTestingSets(MapSize, Mode(), Shuffle::None, 1000);
315 while (State.KeepRunningBatch(MapSize * Data.Maps.size())) {
316 for (size_t I = 0; I < Data.Maps.size(); ++I) {
317 auto& Map = Data.Maps[I];
318 auto Third = *(Data.Hints[I].begin() + 2);
319 for (auto K : Data.Keys) {
320 auto Itor = hint == ::Hint::Begin
321 ? Map.begin()
322 : hint == ::Hint::Third ? Third : Map.end();
323#ifndef VALIDATE
324 benchmark::DoNotOptimize(Map.insert(Itor, std::make_pair(K, 1)));
325#else
326 size_t Size = Map.size();
327 Map.insert(Itor, std::make_pair(K, 1));
328 if (Mode() == ::Mode::Hit) {
329 if (Size != Map.size())
330 State.SkipWithError("Inserted a duplicate element");
331 } else {
332 if (Size + 1 != Map.size())
333 State.SkipWithError("Failed to insert a new element");
334 }
335#endif
336 }
337 }
338
339 State.PauseTiming();
340 Data = makeTestingSets(MapSize, Mode(), Shuffle::None, 1000);
341 State.ResumeTiming();
342 }
343 }
344
345 void run(benchmark::State& State) const {
346 static constexpr auto h = Hint();
347 run<h>(State);
348 }
349
350 std::string name() const {
351 return "BM_InsertHint" + baseName() + Mode::name() + Hint::name();
352 }
353};
354
355template <class Mode, class Order>
356struct InsertAssign : Base {
357 using Base::Base;
358
359 void run(benchmark::State& State) const {
360 auto Data = makeTestingSets(
361 MapSize, Mode(),
362 Order::value == ::Order::Random ? Shuffle::Keys : Shuffle::None, 1000);
363 while (State.KeepRunningBatch(MapSize * Data.Maps.size())) {
364 for (auto& Map : Data.Maps) {
365 for (auto K : Data.Keys) {
366#ifndef VALIDATE
367 benchmark::DoNotOptimize(Map.insert_or_assign(K, 1));
368#else
369 bool Inserted = Map.insert_or_assign(K, 1).second;
370 if (Mode() == ::Mode::Hit) {
371 if (Inserted)
372 State.SkipWithError("Inserted a duplicate element");
373 } else {
374 if (!Inserted)
375 State.SkipWithError("Failed to insert e new element");
376 }
377#endif
378 }
379 }
380
381 State.PauseTiming();
382 Data = makeTestingSets(MapSize, Mode(),
383 Order::value == ::Order::Random ? Shuffle::Keys
384 : Shuffle::None,
385 1000);
386 State.ResumeTiming();
387 }
388 }
389
390 std::string name() const {
391 return "BM_InsertAssign" + baseName() + Mode::name() + Order::name();
392 }
393};
394
395template <class Mode, class Hint>
396struct InsertAssignHint : Base {
397 using Base::Base;
398
399 template < ::Hint hint>
400 typename std::enable_if<hint == ::Hint::Correct>::type
401 run(benchmark::State& State) const {
402 auto Data = makeTestingSets(MapSize, Mode(), Shuffle::None, 1000);
403 while (State.KeepRunningBatch(MapSize * Data.Maps.size())) {
404 for (size_t I = 0; I < Data.Maps.size(); ++I) {
405 auto& Map = Data.Maps[I];
406 auto H = Data.Hints[I].begin();
407 for (auto K : Data.Keys) {
408#ifndef VALIDATE
409 benchmark::DoNotOptimize(Map.insert_or_assign(*H, K, 1));
410#else
411 auto Inserted = Map.insert_or_assign(*H, K, 1);
412 if (Mode() == ::Mode::Hit) {
413 if (Inserted != *H)
414 State.SkipWithError("Inserted a duplicate element");
415 } else {
416 if (++Inserted != *H)
417 State.SkipWithError("Failed to insert a new element");
418 }
419#endif
420 ++H;
421 }
422 }
423
424 State.PauseTiming();
425 Data = makeTestingSets(MapSize, Mode(), Shuffle::None, 1000);
426 State.ResumeTiming();
427 }
428 }
429
430 template < ::Hint hint>
431 typename std::enable_if<hint != ::Hint::Correct>::type
432 run(benchmark::State& State) const {
433 auto Data = makeTestingSets(MapSize, Mode(), Shuffle::None, 1000);
434 while (State.KeepRunningBatch(MapSize * Data.Maps.size())) {
435 for (size_t I = 0; I < Data.Maps.size(); ++I) {
436 auto& Map = Data.Maps[I];
437 auto Third = *(Data.Hints[I].begin() + 2);
438 for (auto K : Data.Keys) {
439 auto Itor = hint == ::Hint::Begin
440 ? Map.begin()
441 : hint == ::Hint::Third ? Third : Map.end();
442#ifndef VALIDATE
443 benchmark::DoNotOptimize(Map.insert_or_assign(Itor, K, 1));
444#else
445 size_t Size = Map.size();
446 Map.insert_or_assign(Itor, K, 1);
447 if (Mode() == ::Mode::Hit) {
448 if (Size != Map.size())
449 State.SkipWithError("Inserted a duplicate element");
450 } else {
451 if (Size + 1 != Map.size())
452 State.SkipWithError("Failed to insert a new element");
453 }
454#endif
455 }
456 }
457
458 State.PauseTiming();
459 Data = makeTestingSets(MapSize, Mode(), Shuffle::None, 1000);
460 State.ResumeTiming();
461 }
462 }
463
464 void run(benchmark::State& State) const {
465 static constexpr auto h = Hint();
466 run<h>(State);
467 }
468
469 std::string name() const {
470 return "BM_InsertAssignHint" + baseName() + Mode::name() + Hint::name();
471 }
472};
473
474template <class Mode, class Order>
475struct Emplace : Base {
476 using Base::Base;
477
478 void run(benchmark::State& State) const {
479
480 auto Data = makeTestingSets(
481 MapSize, Mode(),
482 Order::value == ::Order::Random ? Shuffle::Keys : Shuffle::None, 1000);
483 while (State.KeepRunningBatch(MapSize * Data.Maps.size())) {
484 for (auto& Map : Data.Maps) {
485 for (auto K : Data.Keys) {
486#ifndef VALIDATE
487 benchmark::DoNotOptimize(Map.emplace(K, 1));
488#else
489 bool Inserted = Map.emplace(K, 1).second;
490 if (Mode() == ::Mode::Hit) {
491 if (Inserted)
492 State.SkipWithError("Emplaced a duplicate element");
493 } else {
494 if (!Inserted)
495 State.SkipWithError("Failed to emplace a new element");
496 }
497#endif
498 }
499 }
500
501 State.PauseTiming();
502 Data = makeTestingSets(MapSize, Mode(),
503 Order::value == ::Order::Random ? Shuffle::Keys
504 : Shuffle::None,
505 1000);
506 State.ResumeTiming();
507 }
508 }
509
510 std::string name() const {
511 return "BM_Emplace" + baseName() + Mode::name() + Order::name();
512 }
513};
514
515template <class Mode, class Hint>
516struct EmplaceHint : Base {
517 using Base::Base;
518
519 template < ::Hint hint>
520 typename std::enable_if<hint == ::Hint::Correct>::type
521 run(benchmark::State& State) const {
522 auto Data = makeTestingSets(MapSize, Mode(), Shuffle::None, 1000);
523 while (State.KeepRunningBatch(MapSize * Data.Maps.size())) {
524 for (size_t I = 0; I < Data.Maps.size(); ++I) {
525 auto& Map = Data.Maps[I];
526 auto H = Data.Hints[I].begin();
527 for (auto K : Data.Keys) {
528#ifndef VALIDATE
529 benchmark::DoNotOptimize(Map.emplace_hint(*H, K, 1));
530#else
531 auto Inserted = Map.emplace_hint(*H, K, 1);
532 if (Mode() == ::Mode::Hit) {
533 if (Inserted != *H)
534 State.SkipWithError("Emplaced a duplicate element");
535 } else {
536 if (++Inserted != *H)
537 State.SkipWithError("Failed to emplace a new element");
538 }
539#endif
540 ++H;
541 }
542 }
543
544 State.PauseTiming();
545 Data = makeTestingSets(MapSize, Mode(), Shuffle::None, 1000);
546 State.ResumeTiming();
547 }
548 }
549
550 template < ::Hint hint>
551 typename std::enable_if<hint != ::Hint::Correct>::type
552 run(benchmark::State& State) const {
553 auto Data = makeTestingSets(MapSize, Mode(), Shuffle::None, 1000);
554 while (State.KeepRunningBatch(MapSize * Data.Maps.size())) {
555 for (size_t I = 0; I < Data.Maps.size(); ++I) {
556 auto& Map = Data.Maps[I];
557 auto Third = *(Data.Hints[I].begin() + 2);
558 for (auto K : Data.Keys) {
559 auto Itor = hint == ::Hint::Begin
560 ? Map.begin()
561 : hint == ::Hint::Third ? Third : Map.end();
562#ifndef VALIDATE
563 benchmark::DoNotOptimize(Map.emplace_hint(Itor, K, 1));
564#else
565 size_t Size = Map.size();
566 Map.emplace_hint(Itor, K, 1);
567 if (Mode() == ::Mode::Hit) {
568 if (Size != Map.size())
569 State.SkipWithError("Emplaced a duplicate element");
570 } else {
571 if (Size + 1 != Map.size())
572 State.SkipWithError("Failed to emplace a new element");
573 }
574#endif
575 }
576 }
577
578 State.PauseTiming();
579 Data = makeTestingSets(MapSize, Mode(), Shuffle::None, 1000);
580 State.ResumeTiming();
581 }
582 }
583
584 void run(benchmark::State& State) const {
585 static constexpr auto h = Hint();
586 run<h>(State);
587 }
588
589 std::string name() const {
590 return "BM_EmplaceHint" + baseName() + Mode::name() + Hint::name();
591 }
592};
593
594template <class Mode, class Order>
595struct TryEmplace : Base {
596 using Base::Base;
597
598 void run(benchmark::State& State) const {
599
600 auto Data = makeTestingSets(
601 MapSize, Mode(),
602 Order::value == ::Order::Random ? Shuffle::Keys : Shuffle::None, 1000);
603 while (State.KeepRunningBatch(MapSize * Data.Maps.size())) {
604 for (auto& Map : Data.Maps) {
605 for (auto K : Data.Keys) {
606#ifndef VALIDATE
607 benchmark::DoNotOptimize(Map.try_emplace(K, 1));
608#else
609 bool Inserted = Map.try_emplace(K, 1).second;
610 if (Mode() == ::Mode::Hit) {
611 if (Inserted)
612 State.SkipWithError("Emplaced a duplicate element");
613 } else {
614 if (!Inserted)
615 State.SkipWithError("Failed to emplace a new element");
616 }
617#endif
618 }
619 }
620
621 State.PauseTiming();
622 Data = makeTestingSets(MapSize, Mode(),
623 Order::value == ::Order::Random ? Shuffle::Keys
624 : Shuffle::None,
625 1000);
626 State.ResumeTiming();
627 }
628 }
629
630 std::string name() const {
631 return "BM_TryEmplace" + baseName() + Mode::name() + Order::name();
632 }
633};
634
635template <class Mode, class Hint>
636struct TryEmplaceHint : Base {
637 using Base::Base;
638
639 template < ::Hint hint>
640 typename std::enable_if<hint == ::Hint::Correct>::type
641 run(benchmark::State& State) const {
642 auto Data = makeTestingSets(MapSize, Mode(), Shuffle::None, 1000);
643 while (State.KeepRunningBatch(MapSize * Data.Maps.size())) {
644 for (size_t I = 0; I < Data.Maps.size(); ++I) {
645 auto& Map = Data.Maps[I];
646 auto H = Data.Hints[I].begin();
647 for (auto K : Data.Keys) {
648#ifndef VALIDATE
649 benchmark::DoNotOptimize(Map.try_emplace(*H, K, 1));
650#else
651 auto Inserted = Map.try_emplace(*H, K, 1);
652 if (Mode() == ::Mode::Hit) {
653 if (Inserted != *H)
654 State.SkipWithError("Emplaced a duplicate element");
655 } else {
656 if (++Inserted != *H)
657 State.SkipWithError("Failed to emplace a new element");
658 }
659#endif
660 ++H;
661 }
662 }
663
664 State.PauseTiming();
665 Data = makeTestingSets(MapSize, Mode(), Shuffle::None, 1000);
666 State.ResumeTiming();
667 }
668 }
669
670 template < ::Hint hint>
671 typename std::enable_if<hint != ::Hint::Correct>::type
672 run(benchmark::State& State) const {
673 auto Data = makeTestingSets(MapSize, Mode(), Shuffle::None, 1000);
674 while (State.KeepRunningBatch(MapSize * Data.Maps.size())) {
675 for (size_t I = 0; I < Data.Maps.size(); ++I) {
676 auto& Map = Data.Maps[I];
677 auto Third = *(Data.Hints[I].begin() + 2);
678 for (auto K : Data.Keys) {
679 auto Itor = hint == ::Hint::Begin
680 ? Map.begin()
681 : hint == ::Hint::Third ? Third : Map.end();
682#ifndef VALIDATE
683 benchmark::DoNotOptimize(Map.try_emplace(Itor, K, 1));
684#else
685 size_t Size = Map.size();
686 Map.try_emplace(Itor, K, 1);
687 if (Mode() == ::Mode::Hit) {
688 if (Size != Map.size())
689 State.SkipWithError("Emplaced a duplicate element");
690 } else {
691 if (Size + 1 != Map.size())
692 State.SkipWithError("Failed to emplace a new element");
693 }
694#endif
695 }
696 }
697
698 State.PauseTiming();
699 Data = makeTestingSets(MapSize, Mode(), Shuffle::None, 1000);
700 State.ResumeTiming();
701 }
702 }
703
704 void run(benchmark::State& State) const {
705 static constexpr auto h = Hint();
706 run<h>(State);
707 }
708
709 std::string name() const {
710 return "BM_TryEmplaceHint" + baseName() + Mode::name() + Hint::name();
711 }
712};
713
714template <class Mode, class Order>
715struct Erase : Base {
716 using Base::Base;
717
718 void run(benchmark::State& State) const {
719 auto Data = makeTestingSets(
720 MapSize, Mode(),
721 Order::value == ::Order::Random ? Shuffle::Keys : Shuffle::None, 1000);
722 while (State.KeepRunningBatch(MapSize * Data.Maps.size())) {
723 for (auto& Map : Data.Maps) {
724 for (auto K : Data.Keys) {
725#ifndef VALIDATE
726 benchmark::DoNotOptimize(Map.erase(K));
727#else
728 size_t I = Map.erase(K);
729 if (Mode() == ::Mode::Hit) {
730 if (I == 0)
731 State.SkipWithError("Did not find the existing element");
732 } else {
733 if (I == 1)
734 State.SkipWithError("Did find the non-existing element");
735 }
736#endif
737 }
738 }
739
740 State.PauseTiming();
741 Data = makeTestingSets(MapSize, Mode(),
742 Order::value == ::Order::Random ? Shuffle::Keys
743 : Shuffle::None,
744 1000);
745 State.ResumeTiming();
746 }
747 }
748
749 std::string name() const {
750 return "BM_Erase" + baseName() + Mode::name() + Order::name();
751 }
752};
753
754template <class Order>
755struct EraseIterator : Base {
756 using Base::Base;
757
758 void run(benchmark::State& State) const {
759 auto Data = makeTestingSets(
760 MapSize, Mode::Hit,
761 Order::value == ::Order::Random ? Shuffle::Hints : Shuffle::None, 1000);
762 while (State.KeepRunningBatch(MapSize * Data.Maps.size())) {
763 for (size_t I = 0; I < Data.Maps.size(); ++I) {
764 auto& Map = Data.Maps[I];
765 for (auto H : Data.Hints[I]) {
766 benchmark::DoNotOptimize(Map.erase(H));
767 }
768#ifdef VALIDATE
769 if (!Map.empty())
770 State.SkipWithError("Did not erase the entire map");
771#endif
772 }
773
774 State.PauseTiming();
775 Data = makeTestingSets(MapSize, Mode::Hit,
776 Order::value == ::Order::Random ? Shuffle::Hints
777 : Shuffle::None,
778 1000);
779 State.ResumeTiming();
780 }
781 }
782
783 std::string name() const {
784 return "BM_EraseIterator" + baseName() + Order::name();
785 }
786};
787
788struct EraseRange : Base {
789 using Base::Base;
790
791 void run(benchmark::State& State) const {
792 auto Data = makeTestingSets(MapSize, Mode::Hit, Shuffle::None, 1000);
793 while (State.KeepRunningBatch(MapSize * Data.Maps.size())) {
794 for (auto& Map : Data.Maps) {
795#ifndef VALIDATE
796 benchmark::DoNotOptimize(Map.erase(Map.begin(), Map.end()));
797#else
798 Map.erase(Map.begin(), Map.end());
799 if (!Map.empty())
800 State.SkipWithError("Did not erase the entire map");
801#endif
802 }
803
804 State.PauseTiming();
805 Data = makeTestingSets(MapSize, Mode::Hit, Shuffle::None, 1000);
806 State.ResumeTiming();
807 }
808 }
809
810 std::string name() const { return "BM_EraseRange" + baseName(); }
811};
812
813//*******************************************************************|
814// Lookup |
815//*******************************************************************|
816
817template <class Mode, class Order>
818struct Count : Base {
819 using Base::Base;
820
821 void run(benchmark::State& State) const {
822 auto Data = makeTestingSets(
823 MapSize, Mode(),
824 Order::value == ::Order::Random ? Shuffle::Keys : Shuffle::None, 1);
825 auto& Map = Data.Maps.front();
826 while (State.KeepRunningBatch(MapSize)) {
827 for (auto K : Data.Keys) {
828#ifndef VALIDATE
829 benchmark::DoNotOptimize(Map.count(K));
830#else
831 size_t I = Map.count(K);
832 if (Mode() == ::Mode::Hit) {
833 if (I == 0)
834 State.SkipWithError("Did not find the existing element");
835 } else {
836 if (I == 1)
837 State.SkipWithError("Did find the non-existing element");
838 }
839#endif
840 }
841 }
842 }
843
844 std::string name() const {
845 return "BM_Count" + baseName() + Mode::name() + Order::name();
846 }
847};
848
849template <class Mode, class Order>
850struct Find : Base {
851 using Base::Base;
852
853 void run(benchmark::State& State) const {
854 auto Data = makeTestingSets(
855 MapSize, Mode(),
856 Order::value == ::Order::Random ? Shuffle::Keys : Shuffle::None, 1);
857 auto& Map = Data.Maps.front();
858 while (State.KeepRunningBatch(MapSize)) {
859 for (auto K : Data.Keys) {
860#ifndef VALIDATE
861 benchmark::DoNotOptimize(Map.find(K));
862#else
863 auto Itor = Map.find(K);
864 if (Mode() == ::Mode::Hit) {
865 if (Itor == Map.end())
866 State.SkipWithError("Did not find the existing element");
867 } else {
868 if (Itor != Map.end())
869 State.SkipWithError("Did find the non-existing element");
870 }
871#endif
872 }
873 }
874 }
875
876 std::string name() const {
877 return "BM_Find" + baseName() + Mode::name() + Order::name();
878 }
879};
880
881template <class Mode, class Order>
882struct EqualRange : Base {
883 using Base::Base;
884
885 void run(benchmark::State& State) const {
886 auto Data = makeTestingSets(
887 MapSize, Mode(),
888 Order::value == ::Order::Random ? Shuffle::Keys : Shuffle::None, 1);
889 auto& Map = Data.Maps.front();
890 while (State.KeepRunningBatch(MapSize)) {
891 for (auto K : Data.Keys) {
892#ifndef VALIDATE
893 benchmark::DoNotOptimize(Map.equal_range(K));
894#else
895 auto Range = Map.equal_range(K);
896 if (Mode() == ::Mode::Hit) {
897 // Adjust validation for the last element.
898 auto Key = K;
899 if (Range.second == Map.end() && K == 2 * MapSize) {
900 --Range.second;
901 Key -= 2;
902 }
903 if (Range.first == Map.end() || Range.first->first != K ||
904 Range.second == Map.end() || Range.second->first - 2 != Key)
905 State.SkipWithError("Did not find the existing element");
906 } else {
907 if (Range.first == Map.end() || Range.first->first - 1 != K ||
908 Range.second == Map.end() || Range.second->first - 1 != K)
909 State.SkipWithError("Did find the non-existing element");
910 }
911#endif
912 }
913 }
914 }
915
916 std::string name() const {
917 return "BM_EqualRange" + baseName() + Mode::name() + Order::name();
918 }
919};
920
921template <class Mode, class Order>
922struct LowerBound : Base {
923 using Base::Base;
924
925 void run(benchmark::State& State) const {
926 auto Data = makeTestingSets(
927 MapSize, Mode(),
928 Order::value == ::Order::Random ? Shuffle::Keys : Shuffle::None, 1);
929 auto& Map = Data.Maps.front();
930 while (State.KeepRunningBatch(MapSize)) {
931 for (auto K : Data.Keys) {
932#ifndef VALIDATE
933 benchmark::DoNotOptimize(Map.lower_bound(K));
934#else
935 auto Itor = Map.lower_bound(K);
936 if (Mode() == ::Mode::Hit) {
937 if (Itor == Map.end() || Itor->first != K)
938 State.SkipWithError("Did not find the existing element");
939 } else {
940 if (Itor == Map.end() || Itor->first - 1 != K)
941 State.SkipWithError("Did find the non-existing element");
942 }
943#endif
944 }
945 }
946 }
947
948 std::string name() const {
949 return "BM_LowerBound" + baseName() + Mode::name() + Order::name();
950 }
951};
952
953template <class Mode, class Order>
954struct UpperBound : Base {
955 using Base::Base;
956
957 void run(benchmark::State& State) const {
958 auto Data = makeTestingSets(
959 MapSize, Mode(),
960 Order::value == ::Order::Random ? Shuffle::Keys : Shuffle::None, 1);
961 auto& Map = Data.Maps.front();
962 while (State.KeepRunningBatch(MapSize)) {
963 for (auto K : Data.Keys) {
964#ifndef VALIDATE
965 benchmark::DoNotOptimize(Map.upper_bound(K));
966#else
967 std::map<uint64_t, int64_t>::iterator Itor = Map.upper_bound(K);
968 if (Mode() == ::Mode::Hit) {
969 // Adjust validation for the last element.
970 auto Key = K;
971 if (Itor == Map.end() && K == 2 * MapSize) {
972 --Itor;
973 Key -= 2;
974 }
975 if (Itor == Map.end() || Itor->first - 2 != Key)
976 State.SkipWithError("Did not find the existing element");
977 } else {
978 if (Itor == Map.end() || Itor->first - 1 != K)
979 State.SkipWithError("Did find the non-existing element");
980 }
981#endif
982 }
983 }
984 }
985
986 std::string name() const {
987 return "BM_UpperBound" + baseName() + Mode::name() + Order::name();
988 }
989};
990
991} // namespace
992
993int main(int argc, char** argv) {
994 benchmark::Initialize(&argc, argv);
995 if (benchmark::ReportUnrecognizedArguments(argc, argv))
996 return 1;
997
998#ifdef VALIDATE
999 const std::vector<size_t> MapSize{10};
1000#else
1001 const std::vector<size_t> MapSize{10, 100, 1000, 10000, 100000, 1000000};
1002#endif
1003
1004 // Member functions
1005 makeCartesianProductBenchmark<ConstructorDefault>();
1006 makeCartesianProductBenchmark<ConstructorIterator>(MapSize);
1007 makeCartesianProductBenchmark<ConstructorCopy>(MapSize);
1008 makeCartesianProductBenchmark<ConstructorMove>(MapSize);
1009
1010 // Capacity
1011 makeCartesianProductBenchmark<Empty>(MapSize);
1012 makeCartesianProductBenchmark<Size>(MapSize);
1013
1014 // Modifiers
1015 makeCartesianProductBenchmark<Clear>(MapSize);
1016 makeCartesianProductBenchmark<Insert, AllModes, AllOrders>(MapSize);
1017 makeCartesianProductBenchmark<InsertHint, AllModes, AllHints>(MapSize);
1018 makeCartesianProductBenchmark<InsertAssign, AllModes, AllOrders>(MapSize);
1019 makeCartesianProductBenchmark<InsertAssignHint, AllModes, AllHints>(MapSize);
1020
1021 makeCartesianProductBenchmark<Emplace, AllModes, AllOrders>(MapSize);
1022 makeCartesianProductBenchmark<EmplaceHint, AllModes, AllHints>(MapSize);
1023 makeCartesianProductBenchmark<TryEmplace, AllModes, AllOrders>(MapSize);
1024 makeCartesianProductBenchmark<TryEmplaceHint, AllModes, AllHints>(MapSize);
1025 makeCartesianProductBenchmark<Erase, AllModes, AllOrders>(MapSize);
1026 makeCartesianProductBenchmark<EraseIterator, AllOrders>(MapSize);
1027 makeCartesianProductBenchmark<EraseRange>(MapSize);
1028
1029 // Lookup
1030 makeCartesianProductBenchmark<Count, AllModes, AllOrders>(MapSize);
1031 makeCartesianProductBenchmark<Find, AllModes, AllOrders>(MapSize);
1032 makeCartesianProductBenchmark<EqualRange, AllModes, AllOrders>(MapSize);
1033 makeCartesianProductBenchmark<LowerBound, AllModes, AllOrders>(MapSize);
1034 makeCartesianProductBenchmark<UpperBound, AllModes, AllOrders>(MapSize);
1035
1036 benchmark::RunSpecifiedBenchmarks();
1037}