| //===----------------------------------------------------------------------===// | 
 | // | 
 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
 | // See https://llvm.org/LICENSE.txt for license information. | 
 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
 | // | 
 | //===----------------------------------------------------------------------===// | 
 |  | 
 | #include <algorithm> | 
 | #include <cstdint> | 
 | #include <map> | 
 | #include <random> | 
 | #include <vector> | 
 |  | 
 | #include "CartesianBenchmarks.h" | 
 | #include "benchmark/benchmark.h" | 
 | #include "test_macros.h" | 
 |  | 
 | // When VALIDATE is defined the benchmark will run to validate the benchmarks. | 
 | // The time taken by several operations depend on whether or not an element | 
 | // exists. To avoid errors in the benchmark these operations have a validation | 
 | // mode to test the benchmark. Since they are not meant to be benchmarked the | 
 | // number of sizes tested is limited to 1. | 
 | //#define VALIDATE | 
 |  | 
 | namespace { | 
 |  | 
 | enum class Mode { Hit, Miss }; | 
 |  | 
 | struct AllModes : EnumValuesAsTuple<AllModes, Mode, 2> { | 
 |   static constexpr const char* Names[] = {"ExistingElement", "NewElement"}; | 
 | }; | 
 |  | 
 | // The positions of the hints to pick: | 
 | // - Begin picks the first item. The item cannot be put before this element. | 
 | // - Thrid picks the third item. This is just an element with a valid entry | 
 | //   before and after it. | 
 | // - Correct contains the correct hint. | 
 | // - End contains a hint to the end of the map. | 
 | enum class Hint { Begin, Third, Correct, End }; | 
 | struct AllHints : EnumValuesAsTuple<AllHints, Hint, 4> { | 
 |   static constexpr const char* Names[] = {"Begin", "Third", "Correct", "End"}; | 
 | }; | 
 |  | 
 | enum class Order { Sorted, Random }; | 
 | struct AllOrders : EnumValuesAsTuple<AllOrders, Order, 2> { | 
 |   static constexpr const char* Names[] = {"Sorted", "Random"}; | 
 | }; | 
 |  | 
 | struct TestSets { | 
 |   std::vector<uint64_t> Keys; | 
 |   std::vector<std::map<uint64_t, int64_t> > Maps; | 
 |   std::vector< | 
 |       std::vector<typename std::map<uint64_t, int64_t>::const_iterator> > | 
 |       Hints; | 
 | }; | 
 |  | 
 | enum class Shuffle { None, Keys, Hints }; | 
 |  | 
 | TestSets makeTestingSets(size_t MapSize, Mode mode, Shuffle shuffle, | 
 |                          size_t max_maps) { | 
 |   /* | 
 |    * The shuffle does not retain the random number generator to use the same | 
 |    * set of random numbers for every iteration. | 
 |    */ | 
 |   TestSets R; | 
 |  | 
 |   int MapCount = std::min(max_maps, 1000000 / MapSize); | 
 |  | 
 |   for (uint64_t I = 0; I < MapSize; ++I) { | 
 |     R.Keys.push_back(mode == Mode::Hit ? 2 * I + 2 : 2 * I + 1); | 
 |   } | 
 |   if (shuffle == Shuffle::Keys) | 
 |     std::shuffle(R.Keys.begin(), R.Keys.end(), std::mt19937()); | 
 |  | 
 |   for (int M = 0; M < MapCount; ++M) { | 
 |     auto& map = R.Maps.emplace_back(); | 
 |     auto& hints = R.Hints.emplace_back(); | 
 |     for (uint64_t I = 0; I < MapSize; ++I) { | 
 |       hints.push_back(map.insert(std::make_pair(2 * I + 2, 0)).first); | 
 |     } | 
 |     if (shuffle == Shuffle::Hints) | 
 |       std::shuffle(hints.begin(), hints.end(), std::mt19937()); | 
 |   } | 
 |  | 
 |   return R; | 
 | } | 
 |  | 
 | struct Base { | 
 |   size_t MapSize; | 
 |   Base(size_t T) : MapSize(T) {} | 
 |  | 
 |   std::string baseName() const { return "_MapSize=" + std::to_string(MapSize); } | 
 | }; | 
 |  | 
 | //*******************************************************************| | 
 | //                       Member functions                            | | 
 | //*******************************************************************| | 
 |  | 
 | struct ConstructorDefault { | 
 |   void run(benchmark::State& State) const { | 
 |     for (auto _ : State) { | 
 |       benchmark::DoNotOptimize(std::map<uint64_t, int64_t>()); | 
 |     } | 
 |   } | 
 |  | 
 |   std::string name() const { return "BM_ConstructorDefault"; } | 
 | }; | 
 |  | 
 | struct ConstructorIterator : Base { | 
 |   using Base::Base; | 
 |  | 
 |   void run(benchmark::State& State) const { | 
 |     auto Data = makeTestingSets(MapSize, Mode::Hit, Shuffle::None, 1); | 
 |     auto& Map = Data.Maps.front(); | 
 |     while (State.KeepRunningBatch(MapSize)) { | 
 | #ifndef VALIDATE | 
 |       benchmark::DoNotOptimize( | 
 |           std::map<uint64_t, int64_t>(Map.begin(), Map.end())); | 
 | #else | 
 |       std::map<uint64_t, int64_t> M{Map.begin(), Map.end()}; | 
 |       if (M != Map) | 
 |         State.SkipWithError("Map copy not identical"); | 
 | #endif | 
 |     } | 
 |   } | 
 |  | 
 |   std::string name() const { return "BM_ConstructorIterator" + baseName(); } | 
 | }; | 
 |  | 
 | struct ConstructorCopy : Base { | 
 |   using Base::Base; | 
 |  | 
 |   void run(benchmark::State& State) const { | 
 |     auto Data = makeTestingSets(MapSize, Mode::Hit, Shuffle::None, 1); | 
 |     auto& Map = Data.Maps.front(); | 
 |     while (State.KeepRunningBatch(MapSize)) { | 
 | #ifndef VALIDATE | 
 |       std::map<uint64_t, int64_t> M(Map); | 
 |       benchmark::DoNotOptimize(M); | 
 | #else | 
 |       std::map<uint64_t, int64_t> M(Map); | 
 |       if (M != Map) | 
 |         State.SkipWithError("Map copy not identical"); | 
 | #endif | 
 |     } | 
 |   } | 
 |  | 
 |   std::string name() const { return "BM_ConstructorCopy" + baseName(); } | 
 | }; | 
 |  | 
 | struct ConstructorMove : Base { | 
 |   using Base::Base; | 
 |  | 
 |   void run(benchmark::State& State) const { | 
 |     auto Data = makeTestingSets(MapSize, Mode::Hit, Shuffle::None, 1000); | 
 |     while (State.KeepRunningBatch(MapSize * Data.Maps.size())) { | 
 |       for (auto& Map : Data.Maps) { | 
 |         std::map<uint64_t, int64_t> M(std::move(Map)); | 
 |         benchmark::DoNotOptimize(M); | 
 |       } | 
 |       State.PauseTiming(); | 
 |       Data = makeTestingSets(MapSize, Mode::Hit, Shuffle::None, 1000); | 
 |       State.ResumeTiming(); | 
 |     } | 
 |   } | 
 |  | 
 |   std::string name() const { return "BM_ConstructorMove" + baseName(); } | 
 | }; | 
 |  | 
 | //*******************************************************************| | 
 | //                           Capacity                                | | 
 | //*******************************************************************| | 
 |  | 
 | struct Empty : Base { | 
 |   using Base::Base; | 
 |  | 
 |   void run(benchmark::State& State) const { | 
 |     auto Data = makeTestingSets(MapSize, Mode::Hit, Shuffle::None, 1); | 
 |     auto& Map = Data.Maps.front(); | 
 |     for (auto _ : State) { | 
 | #ifndef VALIDATE | 
 |       benchmark::DoNotOptimize(Map.empty()); | 
 | #else | 
 |       if (Map.empty()) | 
 |         State.SkipWithError("Map contains an invalid number of elements."); | 
 | #endif | 
 |     } | 
 |   } | 
 |  | 
 |   std::string name() const { return "BM_Empty" + baseName(); } | 
 | }; | 
 |  | 
 | struct Size : Base { | 
 |   using Base::Base; | 
 |  | 
 |   void run(benchmark::State& State) const { | 
 |     auto Data = makeTestingSets(MapSize, Mode::Hit, Shuffle::None, 1); | 
 |     auto& Map = Data.Maps.front(); | 
 |     for (auto _ : State) { | 
 | #ifndef VALIDATE | 
 |       benchmark::DoNotOptimize(Map.size()); | 
 | #else | 
 |       if (Map.size() != MapSize) | 
 |         State.SkipWithError("Map contains an invalid number of elements."); | 
 | #endif | 
 |     } | 
 |   } | 
 |  | 
 |   std::string name() const { return "BM_Size" + baseName(); } | 
 | }; | 
 |  | 
 | //*******************************************************************| | 
 | //                           Modifiers                               | | 
 | //*******************************************************************| | 
 |  | 
 | struct Clear : Base { | 
 |   using Base::Base; | 
 |  | 
 |   void run(benchmark::State& State) const { | 
 |     auto Data = makeTestingSets(MapSize, Mode::Hit, Shuffle::None, 1000); | 
 |     while (State.KeepRunningBatch(MapSize * Data.Maps.size())) { | 
 |       for (auto& Map : Data.Maps) { | 
 |         Map.clear(); | 
 |         benchmark::DoNotOptimize(Map); | 
 |       } | 
 |       State.PauseTiming(); | 
 |       Data = makeTestingSets(MapSize, Mode::Hit, Shuffle::None, 1000); | 
 |       State.ResumeTiming(); | 
 |     } | 
 |   } | 
 |  | 
 |   std::string name() const { return "BM_Clear" + baseName(); } | 
 | }; | 
 |  | 
 | template <class Mode, class Order> | 
 | struct Insert : Base { | 
 |   using Base::Base; | 
 |  | 
 |   void run(benchmark::State& State) const { | 
 |     auto Data = makeTestingSets( | 
 |         MapSize, Mode(), | 
 |         Order::value == ::Order::Random ? Shuffle::Keys : Shuffle::None, 1000); | 
 |     while (State.KeepRunningBatch(MapSize * Data.Maps.size())) { | 
 |       for (auto& Map : Data.Maps) { | 
 |         for (auto K : Data.Keys) { | 
 | #ifndef VALIDATE | 
 |           benchmark::DoNotOptimize(Map.insert(std::make_pair(K, 1))); | 
 | #else | 
 |           bool Inserted = Map.insert(std::make_pair(K, 1)).second; | 
 |           if (Mode() == ::Mode::Hit) { | 
 |             if (Inserted) | 
 |               State.SkipWithError("Inserted a duplicate element"); | 
 |           } else { | 
 |             if (!Inserted) | 
 |               State.SkipWithError("Failed to insert e new element"); | 
 |           } | 
 | #endif | 
 |         } | 
 |       } | 
 |  | 
 |       State.PauseTiming(); | 
 |       Data = makeTestingSets(MapSize, Mode(), | 
 |                              Order::value == ::Order::Random ? Shuffle::Keys | 
 |                                                              : Shuffle::None, | 
 |                              1000); | 
 |       State.ResumeTiming(); | 
 |     } | 
 |   } | 
 |  | 
 |   std::string name() const { | 
 |     return "BM_Insert" + baseName() + Mode::name() + Order::name(); | 
 |   } | 
 | }; | 
 |  | 
 | template <class Mode, class Hint> | 
 | struct InsertHint : Base { | 
 |   using Base::Base; | 
 |  | 
 |   template < ::Hint hint> | 
 |   typename std::enable_if<hint == ::Hint::Correct>::type | 
 |   run(benchmark::State& State) const { | 
 |     auto Data = makeTestingSets(MapSize, Mode(), Shuffle::None, 1000); | 
 |     while (State.KeepRunningBatch(MapSize * Data.Maps.size())) { | 
 |       for (size_t I = 0; I < Data.Maps.size(); ++I) { | 
 |         auto& Map = Data.Maps[I]; | 
 |         auto H = Data.Hints[I].begin(); | 
 |         for (auto K : Data.Keys) { | 
 | #ifndef VALIDATE | 
 |           benchmark::DoNotOptimize(Map.insert(*H, std::make_pair(K, 1))); | 
 | #else | 
 |           auto Inserted = Map.insert(*H, std::make_pair(K, 1)); | 
 |           if (Mode() == ::Mode::Hit) { | 
 |             if (Inserted != *H) | 
 |               State.SkipWithError("Inserted a duplicate element"); | 
 |           } else { | 
 |             if (++Inserted != *H) | 
 |               State.SkipWithError("Failed to insert a new element"); | 
 |           } | 
 | #endif | 
 |           ++H; | 
 |         } | 
 |       } | 
 |  | 
 |       State.PauseTiming(); | 
 |       Data = makeTestingSets(MapSize, Mode(), Shuffle::None, 1000); | 
 |       State.ResumeTiming(); | 
 |     } | 
 |   } | 
 |  | 
 |   template < ::Hint hint> | 
 |   typename std::enable_if<hint != ::Hint::Correct>::type | 
 |   run(benchmark::State& State) const { | 
 |     auto Data = makeTestingSets(MapSize, Mode(), Shuffle::None, 1000); | 
 |     while (State.KeepRunningBatch(MapSize * Data.Maps.size())) { | 
 |       for (size_t I = 0; I < Data.Maps.size(); ++I) { | 
 |         auto& Map = Data.Maps[I]; | 
 |         auto Third = *(Data.Hints[I].begin() + 2); | 
 |         for (auto K : Data.Keys) { | 
 |           auto Itor = hint == ::Hint::Begin | 
 |                           ? Map.begin() | 
 |                           : hint == ::Hint::Third ? Third : Map.end(); | 
 | #ifndef VALIDATE | 
 |           benchmark::DoNotOptimize(Map.insert(Itor, std::make_pair(K, 1))); | 
 | #else | 
 |           size_t Size = Map.size(); | 
 |           Map.insert(Itor, std::make_pair(K, 1)); | 
 |           if (Mode() == ::Mode::Hit) { | 
 |             if (Size != Map.size()) | 
 |               State.SkipWithError("Inserted a duplicate element"); | 
 |           } else { | 
 |             if (Size + 1 != Map.size()) | 
 |               State.SkipWithError("Failed to insert a new element"); | 
 |           } | 
 | #endif | 
 |         } | 
 |       } | 
 |  | 
 |       State.PauseTiming(); | 
 |       Data = makeTestingSets(MapSize, Mode(), Shuffle::None, 1000); | 
 |       State.ResumeTiming(); | 
 |     } | 
 |   } | 
 |  | 
 |   void run(benchmark::State& State) const { | 
 |     static constexpr auto h = Hint(); | 
 |     run<h>(State); | 
 |   } | 
 |  | 
 |   std::string name() const { | 
 |     return "BM_InsertHint" + baseName() + Mode::name() + Hint::name(); | 
 |   } | 
 | }; | 
 |  | 
 | template <class Mode, class Order> | 
 | struct InsertAssign : Base { | 
 |   using Base::Base; | 
 |  | 
 |   void run(benchmark::State& State) const { | 
 |     auto Data = makeTestingSets( | 
 |         MapSize, Mode(), | 
 |         Order::value == ::Order::Random ? Shuffle::Keys : Shuffle::None, 1000); | 
 |     while (State.KeepRunningBatch(MapSize * Data.Maps.size())) { | 
 |       for (auto& Map : Data.Maps) { | 
 |         for (auto K : Data.Keys) { | 
 | #ifndef VALIDATE | 
 |           benchmark::DoNotOptimize(Map.insert_or_assign(K, 1)); | 
 | #else | 
 |           bool Inserted = Map.insert_or_assign(K, 1).second; | 
 |           if (Mode() == ::Mode::Hit) { | 
 |             if (Inserted) | 
 |               State.SkipWithError("Inserted a duplicate element"); | 
 |           } else { | 
 |             if (!Inserted) | 
 |               State.SkipWithError("Failed to insert e new element"); | 
 |           } | 
 | #endif | 
 |         } | 
 |       } | 
 |  | 
 |       State.PauseTiming(); | 
 |       Data = makeTestingSets(MapSize, Mode(), | 
 |                              Order::value == ::Order::Random ? Shuffle::Keys | 
 |                                                              : Shuffle::None, | 
 |                              1000); | 
 |       State.ResumeTiming(); | 
 |     } | 
 |   } | 
 |  | 
 |   std::string name() const { | 
 |     return "BM_InsertAssign" + baseName() + Mode::name() + Order::name(); | 
 |   } | 
 | }; | 
 |  | 
 | template <class Mode, class Hint> | 
 | struct InsertAssignHint : Base { | 
 |   using Base::Base; | 
 |  | 
 |   template < ::Hint hint> | 
 |   typename std::enable_if<hint == ::Hint::Correct>::type | 
 |   run(benchmark::State& State) const { | 
 |     auto Data = makeTestingSets(MapSize, Mode(), Shuffle::None, 1000); | 
 |     while (State.KeepRunningBatch(MapSize * Data.Maps.size())) { | 
 |       for (size_t I = 0; I < Data.Maps.size(); ++I) { | 
 |         auto& Map = Data.Maps[I]; | 
 |         auto H = Data.Hints[I].begin(); | 
 |         for (auto K : Data.Keys) { | 
 | #ifndef VALIDATE | 
 |           benchmark::DoNotOptimize(Map.insert_or_assign(*H, K, 1)); | 
 | #else | 
 |           auto Inserted = Map.insert_or_assign(*H, K, 1); | 
 |           if (Mode() == ::Mode::Hit) { | 
 |             if (Inserted != *H) | 
 |               State.SkipWithError("Inserted a duplicate element"); | 
 |           } else { | 
 |             if (++Inserted != *H) | 
 |               State.SkipWithError("Failed to insert a new element"); | 
 |           } | 
 | #endif | 
 |           ++H; | 
 |         } | 
 |       } | 
 |  | 
 |       State.PauseTiming(); | 
 |       Data = makeTestingSets(MapSize, Mode(), Shuffle::None, 1000); | 
 |       State.ResumeTiming(); | 
 |     } | 
 |   } | 
 |  | 
 |   template < ::Hint hint> | 
 |   typename std::enable_if<hint != ::Hint::Correct>::type | 
 |   run(benchmark::State& State) const { | 
 |     auto Data = makeTestingSets(MapSize, Mode(), Shuffle::None, 1000); | 
 |     while (State.KeepRunningBatch(MapSize * Data.Maps.size())) { | 
 |       for (size_t I = 0; I < Data.Maps.size(); ++I) { | 
 |         auto& Map = Data.Maps[I]; | 
 |         auto Third = *(Data.Hints[I].begin() + 2); | 
 |         for (auto K : Data.Keys) { | 
 |           auto Itor = hint == ::Hint::Begin | 
 |                           ? Map.begin() | 
 |                           : hint == ::Hint::Third ? Third : Map.end(); | 
 | #ifndef VALIDATE | 
 |           benchmark::DoNotOptimize(Map.insert_or_assign(Itor, K, 1)); | 
 | #else | 
 |           size_t Size = Map.size(); | 
 |           Map.insert_or_assign(Itor, K, 1); | 
 |           if (Mode() == ::Mode::Hit) { | 
 |             if (Size != Map.size()) | 
 |               State.SkipWithError("Inserted a duplicate element"); | 
 |           } else { | 
 |             if (Size + 1 != Map.size()) | 
 |               State.SkipWithError("Failed to insert a new element"); | 
 |           } | 
 | #endif | 
 |         } | 
 |       } | 
 |  | 
 |       State.PauseTiming(); | 
 |       Data = makeTestingSets(MapSize, Mode(), Shuffle::None, 1000); | 
 |       State.ResumeTiming(); | 
 |     } | 
 |   } | 
 |  | 
 |   void run(benchmark::State& State) const { | 
 |     static constexpr auto h = Hint(); | 
 |     run<h>(State); | 
 |   } | 
 |  | 
 |   std::string name() const { | 
 |     return "BM_InsertAssignHint" + baseName() + Mode::name() + Hint::name(); | 
 |   } | 
 | }; | 
 |  | 
 | template <class Mode, class Order> | 
 | struct Emplace : Base { | 
 |   using Base::Base; | 
 |  | 
 |   void run(benchmark::State& State) const { | 
 |  | 
 |     auto Data = makeTestingSets( | 
 |         MapSize, Mode(), | 
 |         Order::value == ::Order::Random ? Shuffle::Keys : Shuffle::None, 1000); | 
 |     while (State.KeepRunningBatch(MapSize * Data.Maps.size())) { | 
 |       for (auto& Map : Data.Maps) { | 
 |         for (auto K : Data.Keys) { | 
 | #ifndef VALIDATE | 
 |           benchmark::DoNotOptimize(Map.emplace(K, 1)); | 
 | #else | 
 |           bool Inserted = Map.emplace(K, 1).second; | 
 |           if (Mode() == ::Mode::Hit) { | 
 |             if (Inserted) | 
 |               State.SkipWithError("Emplaced a duplicate element"); | 
 |           } else { | 
 |             if (!Inserted) | 
 |               State.SkipWithError("Failed to emplace a new element"); | 
 |           } | 
 | #endif | 
 |         } | 
 |       } | 
 |  | 
 |       State.PauseTiming(); | 
 |       Data = makeTestingSets(MapSize, Mode(), | 
 |                              Order::value == ::Order::Random ? Shuffle::Keys | 
 |                                                              : Shuffle::None, | 
 |                              1000); | 
 |       State.ResumeTiming(); | 
 |     } | 
 |   } | 
 |  | 
 |   std::string name() const { | 
 |     return "BM_Emplace" + baseName() + Mode::name() + Order::name(); | 
 |   } | 
 | }; | 
 |  | 
 | template <class Mode, class Hint> | 
 | struct EmplaceHint : Base { | 
 |   using Base::Base; | 
 |  | 
 |   template < ::Hint hint> | 
 |   typename std::enable_if<hint == ::Hint::Correct>::type | 
 |   run(benchmark::State& State) const { | 
 |     auto Data = makeTestingSets(MapSize, Mode(), Shuffle::None, 1000); | 
 |     while (State.KeepRunningBatch(MapSize * Data.Maps.size())) { | 
 |       for (size_t I = 0; I < Data.Maps.size(); ++I) { | 
 |         auto& Map = Data.Maps[I]; | 
 |         auto H = Data.Hints[I].begin(); | 
 |         for (auto K : Data.Keys) { | 
 | #ifndef VALIDATE | 
 |           benchmark::DoNotOptimize(Map.emplace_hint(*H, K, 1)); | 
 | #else | 
 |           auto Inserted = Map.emplace_hint(*H, K, 1); | 
 |           if (Mode() == ::Mode::Hit) { | 
 |             if (Inserted != *H) | 
 |               State.SkipWithError("Emplaced a duplicate element"); | 
 |           } else { | 
 |             if (++Inserted != *H) | 
 |               State.SkipWithError("Failed to emplace a new element"); | 
 |           } | 
 | #endif | 
 |           ++H; | 
 |         } | 
 |       } | 
 |  | 
 |       State.PauseTiming(); | 
 |       Data = makeTestingSets(MapSize, Mode(), Shuffle::None, 1000); | 
 |       State.ResumeTiming(); | 
 |     } | 
 |   } | 
 |  | 
 |   template < ::Hint hint> | 
 |   typename std::enable_if<hint != ::Hint::Correct>::type | 
 |   run(benchmark::State& State) const { | 
 |     auto Data = makeTestingSets(MapSize, Mode(), Shuffle::None, 1000); | 
 |     while (State.KeepRunningBatch(MapSize * Data.Maps.size())) { | 
 |       for (size_t I = 0; I < Data.Maps.size(); ++I) { | 
 |         auto& Map = Data.Maps[I]; | 
 |         auto Third = *(Data.Hints[I].begin() + 2); | 
 |         for (auto K : Data.Keys) { | 
 |           auto Itor = hint == ::Hint::Begin | 
 |                           ? Map.begin() | 
 |                           : hint == ::Hint::Third ? Third : Map.end(); | 
 | #ifndef VALIDATE | 
 |           benchmark::DoNotOptimize(Map.emplace_hint(Itor, K, 1)); | 
 | #else | 
 |           size_t Size = Map.size(); | 
 |           Map.emplace_hint(Itor, K, 1); | 
 |           if (Mode() == ::Mode::Hit) { | 
 |             if (Size != Map.size()) | 
 |               State.SkipWithError("Emplaced a duplicate element"); | 
 |           } else { | 
 |             if (Size + 1 != Map.size()) | 
 |               State.SkipWithError("Failed to emplace a new element"); | 
 |           } | 
 | #endif | 
 |         } | 
 |       } | 
 |  | 
 |       State.PauseTiming(); | 
 |       Data = makeTestingSets(MapSize, Mode(), Shuffle::None, 1000); | 
 |       State.ResumeTiming(); | 
 |     } | 
 |   } | 
 |  | 
 |   void run(benchmark::State& State) const { | 
 |     static constexpr auto h = Hint(); | 
 |     run<h>(State); | 
 |   } | 
 |  | 
 |   std::string name() const { | 
 |     return "BM_EmplaceHint" + baseName() + Mode::name() + Hint::name(); | 
 |   } | 
 | }; | 
 |  | 
 | template <class Mode, class Order> | 
 | struct TryEmplace : Base { | 
 |   using Base::Base; | 
 |  | 
 |   void run(benchmark::State& State) const { | 
 |  | 
 |     auto Data = makeTestingSets( | 
 |         MapSize, Mode(), | 
 |         Order::value == ::Order::Random ? Shuffle::Keys : Shuffle::None, 1000); | 
 |     while (State.KeepRunningBatch(MapSize * Data.Maps.size())) { | 
 |       for (auto& Map : Data.Maps) { | 
 |         for (auto K : Data.Keys) { | 
 | #ifndef VALIDATE | 
 |           benchmark::DoNotOptimize(Map.try_emplace(K, 1)); | 
 | #else | 
 |           bool Inserted = Map.try_emplace(K, 1).second; | 
 |           if (Mode() == ::Mode::Hit) { | 
 |             if (Inserted) | 
 |               State.SkipWithError("Emplaced a duplicate element"); | 
 |           } else { | 
 |             if (!Inserted) | 
 |               State.SkipWithError("Failed to emplace a new element"); | 
 |           } | 
 | #endif | 
 |         } | 
 |       } | 
 |  | 
 |       State.PauseTiming(); | 
 |       Data = makeTestingSets(MapSize, Mode(), | 
 |                              Order::value == ::Order::Random ? Shuffle::Keys | 
 |                                                              : Shuffle::None, | 
 |                              1000); | 
 |       State.ResumeTiming(); | 
 |     } | 
 |   } | 
 |  | 
 |   std::string name() const { | 
 |     return "BM_TryEmplace" + baseName() + Mode::name() + Order::name(); | 
 |   } | 
 | }; | 
 |  | 
 | template <class Mode, class Hint> | 
 | struct TryEmplaceHint : Base { | 
 |   using Base::Base; | 
 |  | 
 |   template < ::Hint hint> | 
 |   typename std::enable_if<hint == ::Hint::Correct>::type | 
 |   run(benchmark::State& State) const { | 
 |     auto Data = makeTestingSets(MapSize, Mode(), Shuffle::None, 1000); | 
 |     while (State.KeepRunningBatch(MapSize * Data.Maps.size())) { | 
 |       for (size_t I = 0; I < Data.Maps.size(); ++I) { | 
 |         auto& Map = Data.Maps[I]; | 
 |         auto H = Data.Hints[I].begin(); | 
 |         for (auto K : Data.Keys) { | 
 | #ifndef VALIDATE | 
 |           benchmark::DoNotOptimize(Map.try_emplace(*H, K, 1)); | 
 | #else | 
 |           auto Inserted = Map.try_emplace(*H, K, 1); | 
 |           if (Mode() == ::Mode::Hit) { | 
 |             if (Inserted != *H) | 
 |               State.SkipWithError("Emplaced a duplicate element"); | 
 |           } else { | 
 |             if (++Inserted != *H) | 
 |               State.SkipWithError("Failed to emplace a new element"); | 
 |           } | 
 | #endif | 
 |           ++H; | 
 |         } | 
 |       } | 
 |  | 
 |       State.PauseTiming(); | 
 |       Data = makeTestingSets(MapSize, Mode(), Shuffle::None, 1000); | 
 |       State.ResumeTiming(); | 
 |     } | 
 |   } | 
 |  | 
 |   template < ::Hint hint> | 
 |   typename std::enable_if<hint != ::Hint::Correct>::type | 
 |   run(benchmark::State& State) const { | 
 |     auto Data = makeTestingSets(MapSize, Mode(), Shuffle::None, 1000); | 
 |     while (State.KeepRunningBatch(MapSize * Data.Maps.size())) { | 
 |       for (size_t I = 0; I < Data.Maps.size(); ++I) { | 
 |         auto& Map = Data.Maps[I]; | 
 |         auto Third = *(Data.Hints[I].begin() + 2); | 
 |         for (auto K : Data.Keys) { | 
 |           auto Itor = hint == ::Hint::Begin | 
 |                           ? Map.begin() | 
 |                           : hint == ::Hint::Third ? Third : Map.end(); | 
 | #ifndef VALIDATE | 
 |           benchmark::DoNotOptimize(Map.try_emplace(Itor, K, 1)); | 
 | #else | 
 |           size_t Size = Map.size(); | 
 |           Map.try_emplace(Itor, K, 1); | 
 |           if (Mode() == ::Mode::Hit) { | 
 |             if (Size != Map.size()) | 
 |               State.SkipWithError("Emplaced a duplicate element"); | 
 |           } else { | 
 |             if (Size + 1 != Map.size()) | 
 |               State.SkipWithError("Failed to emplace a new element"); | 
 |           } | 
 | #endif | 
 |         } | 
 |       } | 
 |  | 
 |       State.PauseTiming(); | 
 |       Data = makeTestingSets(MapSize, Mode(), Shuffle::None, 1000); | 
 |       State.ResumeTiming(); | 
 |     } | 
 |   } | 
 |  | 
 |   void run(benchmark::State& State) const { | 
 |     static constexpr auto h = Hint(); | 
 |     run<h>(State); | 
 |   } | 
 |  | 
 |   std::string name() const { | 
 |     return "BM_TryEmplaceHint" + baseName() + Mode::name() + Hint::name(); | 
 |   } | 
 | }; | 
 |  | 
 | template <class Mode, class Order> | 
 | struct Erase : Base { | 
 |   using Base::Base; | 
 |  | 
 |   void run(benchmark::State& State) const { | 
 |     auto Data = makeTestingSets( | 
 |         MapSize, Mode(), | 
 |         Order::value == ::Order::Random ? Shuffle::Keys : Shuffle::None, 1000); | 
 |     while (State.KeepRunningBatch(MapSize * Data.Maps.size())) { | 
 |       for (auto& Map : Data.Maps) { | 
 |         for (auto K : Data.Keys) { | 
 | #ifndef VALIDATE | 
 |           benchmark::DoNotOptimize(Map.erase(K)); | 
 | #else | 
 |           size_t I = Map.erase(K); | 
 |           if (Mode() == ::Mode::Hit) { | 
 |             if (I == 0) | 
 |               State.SkipWithError("Did not find the existing element"); | 
 |           } else { | 
 |             if (I == 1) | 
 |               State.SkipWithError("Did find the non-existing element"); | 
 |           } | 
 | #endif | 
 |         } | 
 |       } | 
 |  | 
 |       State.PauseTiming(); | 
 |       Data = makeTestingSets(MapSize, Mode(), | 
 |                              Order::value == ::Order::Random ? Shuffle::Keys | 
 |                                                              : Shuffle::None, | 
 |                              1000); | 
 |       State.ResumeTiming(); | 
 |     } | 
 |   } | 
 |  | 
 |   std::string name() const { | 
 |     return "BM_Erase" + baseName() + Mode::name() + Order::name(); | 
 |   } | 
 | }; | 
 |  | 
 | template <class Order> | 
 | struct EraseIterator : Base { | 
 |   using Base::Base; | 
 |  | 
 |   void run(benchmark::State& State) const { | 
 |     auto Data = makeTestingSets( | 
 |         MapSize, Mode::Hit, | 
 |         Order::value == ::Order::Random ? Shuffle::Hints : Shuffle::None, 1000); | 
 |     while (State.KeepRunningBatch(MapSize * Data.Maps.size())) { | 
 |       for (size_t I = 0; I < Data.Maps.size(); ++I) { | 
 |         auto& Map = Data.Maps[I]; | 
 |         for (auto H : Data.Hints[I]) { | 
 |           benchmark::DoNotOptimize(Map.erase(H)); | 
 |         } | 
 | #ifdef VALIDATE | 
 |         if (!Map.empty()) | 
 |           State.SkipWithError("Did not erase the entire map"); | 
 | #endif | 
 |       } | 
 |  | 
 |       State.PauseTiming(); | 
 |       Data = makeTestingSets(MapSize, Mode::Hit, | 
 |                              Order::value == ::Order::Random ? Shuffle::Hints | 
 |                                                              : Shuffle::None, | 
 |                              1000); | 
 |       State.ResumeTiming(); | 
 |     } | 
 |   } | 
 |  | 
 |   std::string name() const { | 
 |     return "BM_EraseIterator" + baseName() + Order::name(); | 
 |   } | 
 | }; | 
 |  | 
 | struct EraseRange : Base { | 
 |   using Base::Base; | 
 |  | 
 |   void run(benchmark::State& State) const { | 
 |     auto Data = makeTestingSets(MapSize, Mode::Hit, Shuffle::None, 1000); | 
 |     while (State.KeepRunningBatch(MapSize * Data.Maps.size())) { | 
 |       for (auto& Map : Data.Maps) { | 
 | #ifndef VALIDATE | 
 |         benchmark::DoNotOptimize(Map.erase(Map.begin(), Map.end())); | 
 | #else | 
 |         Map.erase(Map.begin(), Map.end()); | 
 |         if (!Map.empty()) | 
 |           State.SkipWithError("Did not erase the entire map"); | 
 | #endif | 
 |       } | 
 |  | 
 |       State.PauseTiming(); | 
 |       Data = makeTestingSets(MapSize, Mode::Hit, Shuffle::None, 1000); | 
 |       State.ResumeTiming(); | 
 |     } | 
 |   } | 
 |  | 
 |   std::string name() const { return "BM_EraseRange" + baseName(); } | 
 | }; | 
 |  | 
 | //*******************************************************************| | 
 | //                            Lookup                                 | | 
 | //*******************************************************************| | 
 |  | 
 | template <class Mode, class Order> | 
 | struct Count : Base { | 
 |   using Base::Base; | 
 |  | 
 |   void run(benchmark::State& State) const { | 
 |     auto Data = makeTestingSets( | 
 |         MapSize, Mode(), | 
 |         Order::value == ::Order::Random ? Shuffle::Keys : Shuffle::None, 1); | 
 |     auto& Map = Data.Maps.front(); | 
 |     while (State.KeepRunningBatch(MapSize)) { | 
 |       for (auto K : Data.Keys) { | 
 | #ifndef VALIDATE | 
 |         benchmark::DoNotOptimize(Map.count(K)); | 
 | #else | 
 |         size_t I = Map.count(K); | 
 |         if (Mode() == ::Mode::Hit) { | 
 |           if (I == 0) | 
 |             State.SkipWithError("Did not find the existing element"); | 
 |         } else { | 
 |           if (I == 1) | 
 |             State.SkipWithError("Did find the non-existing element"); | 
 |         } | 
 | #endif | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   std::string name() const { | 
 |     return "BM_Count" + baseName() + Mode::name() + Order::name(); | 
 |   } | 
 | }; | 
 |  | 
 | template <class Mode, class Order> | 
 | struct Find : Base { | 
 |   using Base::Base; | 
 |  | 
 |   void run(benchmark::State& State) const { | 
 |     auto Data = makeTestingSets( | 
 |         MapSize, Mode(), | 
 |         Order::value == ::Order::Random ? Shuffle::Keys : Shuffle::None, 1); | 
 |     auto& Map = Data.Maps.front(); | 
 |     while (State.KeepRunningBatch(MapSize)) { | 
 |       for (auto K : Data.Keys) { | 
 | #ifndef VALIDATE | 
 |         benchmark::DoNotOptimize(Map.find(K)); | 
 | #else | 
 |         auto Itor = Map.find(K); | 
 |         if (Mode() == ::Mode::Hit) { | 
 |           if (Itor == Map.end()) | 
 |             State.SkipWithError("Did not find the existing element"); | 
 |         } else { | 
 |           if (Itor != Map.end()) | 
 |             State.SkipWithError("Did find the non-existing element"); | 
 |         } | 
 | #endif | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   std::string name() const { | 
 |     return "BM_Find" + baseName() + Mode::name() + Order::name(); | 
 |   } | 
 | }; | 
 |  | 
 | template <class Mode, class Order> | 
 | struct EqualRange : Base { | 
 |   using Base::Base; | 
 |  | 
 |   void run(benchmark::State& State) const { | 
 |     auto Data = makeTestingSets( | 
 |         MapSize, Mode(), | 
 |         Order::value == ::Order::Random ? Shuffle::Keys : Shuffle::None, 1); | 
 |     auto& Map = Data.Maps.front(); | 
 |     while (State.KeepRunningBatch(MapSize)) { | 
 |       for (auto K : Data.Keys) { | 
 | #ifndef VALIDATE | 
 |         benchmark::DoNotOptimize(Map.equal_range(K)); | 
 | #else | 
 |         auto Range = Map.equal_range(K); | 
 |         if (Mode() == ::Mode::Hit) { | 
 |           // Adjust validation for the last element. | 
 |           auto Key = K; | 
 |           if (Range.second == Map.end() && K == 2 * MapSize) { | 
 |             --Range.second; | 
 |             Key -= 2; | 
 |           } | 
 |           if (Range.first == Map.end() || Range.first->first != K || | 
 |               Range.second == Map.end() || Range.second->first - 2 != Key) | 
 |             State.SkipWithError("Did not find the existing element"); | 
 |         } else { | 
 |           if (Range.first == Map.end() || Range.first->first - 1 != K || | 
 |               Range.second == Map.end() || Range.second->first - 1 != K) | 
 |             State.SkipWithError("Did find the non-existing element"); | 
 |         } | 
 | #endif | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   std::string name() const { | 
 |     return "BM_EqualRange" + baseName() + Mode::name() + Order::name(); | 
 |   } | 
 | }; | 
 |  | 
 | template <class Mode, class Order> | 
 | struct LowerBound : Base { | 
 |   using Base::Base; | 
 |  | 
 |   void run(benchmark::State& State) const { | 
 |     auto Data = makeTestingSets( | 
 |         MapSize, Mode(), | 
 |         Order::value == ::Order::Random ? Shuffle::Keys : Shuffle::None, 1); | 
 |     auto& Map = Data.Maps.front(); | 
 |     while (State.KeepRunningBatch(MapSize)) { | 
 |       for (auto K : Data.Keys) { | 
 | #ifndef VALIDATE | 
 |         benchmark::DoNotOptimize(Map.lower_bound(K)); | 
 | #else | 
 |         auto Itor = Map.lower_bound(K); | 
 |         if (Mode() == ::Mode::Hit) { | 
 |           if (Itor == Map.end() || Itor->first != K) | 
 |             State.SkipWithError("Did not find the existing element"); | 
 |         } else { | 
 |           if (Itor == Map.end() || Itor->first - 1 != K) | 
 |             State.SkipWithError("Did find the non-existing element"); | 
 |         } | 
 | #endif | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   std::string name() const { | 
 |     return "BM_LowerBound" + baseName() + Mode::name() + Order::name(); | 
 |   } | 
 | }; | 
 |  | 
 | template <class Mode, class Order> | 
 | struct UpperBound : Base { | 
 |   using Base::Base; | 
 |  | 
 |   void run(benchmark::State& State) const { | 
 |     auto Data = makeTestingSets( | 
 |         MapSize, Mode(), | 
 |         Order::value == ::Order::Random ? Shuffle::Keys : Shuffle::None, 1); | 
 |     auto& Map = Data.Maps.front(); | 
 |     while (State.KeepRunningBatch(MapSize)) { | 
 |       for (auto K : Data.Keys) { | 
 | #ifndef VALIDATE | 
 |         benchmark::DoNotOptimize(Map.upper_bound(K)); | 
 | #else | 
 |         std::map<uint64_t, int64_t>::iterator Itor = Map.upper_bound(K); | 
 |         if (Mode() == ::Mode::Hit) { | 
 |           // Adjust validation for the last element. | 
 |           auto Key = K; | 
 |           if (Itor == Map.end() && K == 2 * MapSize) { | 
 |             --Itor; | 
 |             Key -= 2; | 
 |           } | 
 |           if (Itor == Map.end() || Itor->first - 2 != Key) | 
 |             State.SkipWithError("Did not find the existing element"); | 
 |         } else { | 
 |           if (Itor == Map.end() || Itor->first - 1 != K) | 
 |             State.SkipWithError("Did find the non-existing element"); | 
 |         } | 
 | #endif | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   std::string name() const { | 
 |     return "BM_UpperBound" + baseName() + Mode::name() + Order::name(); | 
 |   } | 
 | }; | 
 |  | 
 | } // namespace | 
 |  | 
 | int main(int argc, char** argv) { | 
 |   benchmark::Initialize(&argc, argv); | 
 |   if (benchmark::ReportUnrecognizedArguments(argc, argv)) | 
 |     return 1; | 
 |  | 
 | #ifdef VALIDATE | 
 |   const std::vector<size_t> MapSize{10}; | 
 | #else | 
 |   const std::vector<size_t> MapSize{10, 100, 1000, 10000, 100000, 1000000}; | 
 | #endif | 
 |  | 
 |   // Member functions | 
 |   makeCartesianProductBenchmark<ConstructorDefault>(); | 
 |   makeCartesianProductBenchmark<ConstructorIterator>(MapSize); | 
 |   makeCartesianProductBenchmark<ConstructorCopy>(MapSize); | 
 |   makeCartesianProductBenchmark<ConstructorMove>(MapSize); | 
 |  | 
 |   // Capacity | 
 |   makeCartesianProductBenchmark<Empty>(MapSize); | 
 |   makeCartesianProductBenchmark<Size>(MapSize); | 
 |  | 
 |   // Modifiers | 
 |   makeCartesianProductBenchmark<Clear>(MapSize); | 
 |   makeCartesianProductBenchmark<Insert, AllModes, AllOrders>(MapSize); | 
 |   makeCartesianProductBenchmark<InsertHint, AllModes, AllHints>(MapSize); | 
 |   makeCartesianProductBenchmark<InsertAssign, AllModes, AllOrders>(MapSize); | 
 |   makeCartesianProductBenchmark<InsertAssignHint, AllModes, AllHints>(MapSize); | 
 |  | 
 |   makeCartesianProductBenchmark<Emplace, AllModes, AllOrders>(MapSize); | 
 |   makeCartesianProductBenchmark<EmplaceHint, AllModes, AllHints>(MapSize); | 
 |   makeCartesianProductBenchmark<TryEmplace, AllModes, AllOrders>(MapSize); | 
 |   makeCartesianProductBenchmark<TryEmplaceHint, AllModes, AllHints>(MapSize); | 
 |   makeCartesianProductBenchmark<Erase, AllModes, AllOrders>(MapSize); | 
 |   makeCartesianProductBenchmark<EraseIterator, AllOrders>(MapSize); | 
 |   makeCartesianProductBenchmark<EraseRange>(MapSize); | 
 |  | 
 |   // Lookup | 
 |   makeCartesianProductBenchmark<Count, AllModes, AllOrders>(MapSize); | 
 |   makeCartesianProductBenchmark<Find, AllModes, AllOrders>(MapSize); | 
 |   makeCartesianProductBenchmark<EqualRange, AllModes, AllOrders>(MapSize); | 
 |   makeCartesianProductBenchmark<LowerBound, AllModes, AllOrders>(MapSize); | 
 |   makeCartesianProductBenchmark<UpperBound, AllModes, AllOrders>(MapSize); | 
 |  | 
 |   benchmark::RunSpecifiedBenchmarks(); | 
 | } |