1038 lines
		
	
	
		
			32 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			1038 lines
		
	
	
		
			32 KiB
		
	
	
	
		
			C++
		
	
	
	
| //===----------------------------------------------------------------------===//
 | |
| //
 | |
| // 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();
 | |
| }
 |