Louis Dionne b2d2494731
[libc++] Make benchmarks forward-compatible with the test suite (#114502)
This patch fixes warnings and errors that come up when running the
benchmarks as part of the test suite. It also adds the necessary Lit
annotations to make it pass in various configurations and increases the
portability of the benchmarks.
2024-11-05 09:08:00 -05:00

950 lines
31 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
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14
#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();
}