mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-26 09:46:07 +00:00

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.
950 lines
31 KiB
C++
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();
|
|
}
|