| //===----------------------------------------------------------------------===// |
| // |
| // 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(); |
| } |