From 8e1b692d1d59ba5f5013d0f6a7b4aa3bacbd346c Mon Sep 17 00:00:00 2001 From: Louis Dionne Date: Fri, 17 Jan 2025 10:42:36 -0500 Subject: [PATCH] [libc++] Implement generic associative container benchmarks This patch implements generic associative container benchmarks for containers with unique keys. In doing so, it replaces the existing std::map benchmarks which were based on the cartesian product infrastructure and were too slow to execute. These new benchmarks aim to strike a balance between exhaustive coverage of all operations in the most interesting case, while executing fairly rapidly (~40s on my machine). --- libcxx/test/benchmarks/GenerateInput.h | 5 + .../associative_container_benchmarks.h | 536 ++++++++++ .../benchmarks/containers/flat_map.bench.cpp | 25 + .../test/benchmarks/containers/map.bench.cpp | 940 +----------------- .../test/benchmarks/containers/set.bench.cpp | 23 + 5 files changed, 597 insertions(+), 932 deletions(-) create mode 100644 libcxx/test/benchmarks/containers/associative_container_benchmarks.h create mode 100644 libcxx/test/benchmarks/containers/flat_map.bench.cpp create mode 100644 libcxx/test/benchmarks/containers/set.bench.cpp diff --git a/libcxx/test/benchmarks/GenerateInput.h b/libcxx/test/benchmarks/GenerateInput.h index 081631a32b21d..c87fd69162e9d 100644 --- a/libcxx/test/benchmarks/GenerateInput.h +++ b/libcxx/test/benchmarks/GenerateInput.h @@ -190,6 +190,7 @@ struct Generate { static T arbitrary() { return 42; } static T cheap() { return 42; } static T expensive() { return 42; } + static T random() { return getRandomInteger(std::numeric_limits::min(), std::numeric_limits::max()); } }; template <> @@ -197,6 +198,10 @@ struct Generate { static std::string arbitrary() { return "hello world"; } static std::string cheap() { return "small"; } static std::string expensive() { return std::string(256, 'x'); } + static std::string random() { + auto length = getRandomInteger(1, 1024); + return getRandomString(length); + } }; #endif // BENCHMARK_GENERATE_INPUT_H diff --git a/libcxx/test/benchmarks/containers/associative_container_benchmarks.h b/libcxx/test/benchmarks/containers/associative_container_benchmarks.h new file mode 100644 index 0000000000000..3bddc0c73b54e --- /dev/null +++ b/libcxx/test/benchmarks/containers/associative_container_benchmarks.h @@ -0,0 +1,536 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef TEST_BENCHMARKS_CONTAINERS_ASSOCIATIVE_CONTAINER_BENCHMARKS_H +#define TEST_BENCHMARKS_CONTAINERS_ASSOCIATIVE_CONTAINER_BENCHMARKS_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "benchmark/benchmark.h" +#include "../GenerateInput.h" +#include "test_macros.h" + +namespace support { + +template +struct adapt_operations; + +template +struct adapt_operations> { + using ValueType = typename std::set::value_type; + using KeyType = typename std::set::key_type; + static ValueType value_from_key(KeyType const& k) { return k; } + static KeyType key_from_value(ValueType const& value) { return value; } +}; + +template +struct adapt_operations> { + using ValueType = typename std::map::value_type; + using KeyType = typename std::map::key_type; + static ValueType value_from_key(KeyType const& k) { return {k, Generate::arbitrary()}; } + static KeyType key_from_value(ValueType const& value) { return value.first; } +}; + +#if TEST_STD_VER >= 26 +template +struct adapt_operations> { + using ValueType = typename std::map::value_type; + using KeyType = typename std::map::key_type; + static ValueType value_from_key(KeyType const& k) { return {k, Generate::arbitrary()}; } + static KeyType key_from_value(ValueType const& value) { return value.first; } +}; +#endif + +template +void associative_container_benchmarks(std::string container) { + using Key = typename Container::key_type; + using Value = typename Container::value_type; + + auto generate_unique_keys = [=](std::size_t n) { + std::set keys; + while (keys.size() < n) { + Key k = Generate::random(); + keys.insert(k); + } + return std::vector(keys.begin(), keys.end()); + }; + + auto add_dummy_mapped_type = [](std::vector const& keys) { + std::vector kv; + for (Key const& k : keys) + kv.push_back(adapt_operations::value_from_key(k)); + return kv; + }; + + auto get_key = [](Value const& v) { return adapt_operations::key_from_value(v); }; + + // These benchmarks are structured to perform the operation being benchmarked + // a small number of times at each iteration, in order to offset the cost of + // PauseTiming() and ResumeTiming(). + static constexpr std::size_t BatchSize = 10; + + struct ScratchSpace { + char storage[sizeof(Container)]; + }; + + ///////////////////////// + // Constructors + ///////////////////////// + benchmark::RegisterBenchmark(container + "::ctor(const&)", [=](auto& st) { + const std::size_t size = st.range(0); + std::vector in = add_dummy_mapped_type(generate_unique_keys(size)); + Container src(in.begin(), in.end()); + ScratchSpace c[BatchSize]; + + while (st.KeepRunningBatch(BatchSize)) { + for (std::size_t i = 0; i != BatchSize; ++i) { + new (c + i) Container(src); + benchmark::DoNotOptimize(c + i); + benchmark::ClobberMemory(); + } + + st.PauseTiming(); + for (std::size_t i = 0; i != BatchSize; ++i) { + reinterpret_cast(c + i)->~Container(); + } + st.ResumeTiming(); + } + })->Arg(1024); + + benchmark::RegisterBenchmark(container + "::ctor(iterator, iterator) (unsorted sequence)", [=](auto& st) { + const std::size_t size = st.range(0); + std::mt19937 randomness; + std::vector keys = generate_unique_keys(size); + std::shuffle(keys.begin(), keys.end(), randomness); + std::vector in = add_dummy_mapped_type(keys); + ScratchSpace c[BatchSize]; + + while (st.KeepRunningBatch(BatchSize)) { + for (std::size_t i = 0; i != BatchSize; ++i) { + new (c + i) Container(in.begin(), in.end()); + benchmark::DoNotOptimize(c + i); + benchmark::ClobberMemory(); + } + + st.PauseTiming(); + for (std::size_t i = 0; i != BatchSize; ++i) { + reinterpret_cast(c + i)->~Container(); + } + st.ResumeTiming(); + } + })->Arg(1024); + + benchmark::RegisterBenchmark(container + "::ctor(iterator, iterator) (sorted sequence)", [=](auto& st) { + const std::size_t size = st.range(0); + std::vector keys = generate_unique_keys(size); + std::sort(keys.begin(), keys.end()); + std::vector in = add_dummy_mapped_type(keys); + ScratchSpace c[BatchSize]; + + while (st.KeepRunningBatch(BatchSize)) { + for (std::size_t i = 0; i != BatchSize; ++i) { + new (c + i) Container(in.begin(), in.end()); + benchmark::DoNotOptimize(c + i); + benchmark::ClobberMemory(); + } + + st.PauseTiming(); + for (std::size_t i = 0; i != BatchSize; ++i) { + reinterpret_cast(c + i)->~Container(); + } + st.ResumeTiming(); + } + })->Arg(1024); + + ///////////////////////// + // Assignment + ///////////////////////// + benchmark::RegisterBenchmark(container + "::operator=(const&)", [=](auto& st) { + const std::size_t size = st.range(0); + std::vector in = add_dummy_mapped_type(generate_unique_keys(size)); + Container src(in.begin(), in.end()); + Container c[BatchSize]; + + while (st.KeepRunningBatch(BatchSize)) { + for (std::size_t i = 0; i != BatchSize; ++i) { + c[i] = src; + benchmark::DoNotOptimize(c[i]); + benchmark::ClobberMemory(); + } + + st.PauseTiming(); + for (std::size_t i = 0; i != BatchSize; ++i) { + c[i].clear(); + } + st.ResumeTiming(); + } + })->Arg(1024); + + ///////////////////////// + // Insertion + ///////////////////////// + benchmark::RegisterBenchmark(container + "::insert(value) (already present)", [=](auto& st) { + const std::size_t size = st.range(0); + std::vector in = add_dummy_mapped_type(generate_unique_keys(size)); + Value to_insert = in[in.size() / 2]; // pick any existing value + std::vector c(BatchSize, Container(in.begin(), in.end())); + + while (st.KeepRunningBatch(BatchSize)) { + for (std::size_t i = 0; i != BatchSize; ++i) { + c[i].insert(to_insert); + benchmark::DoNotOptimize(c[i]); + benchmark::ClobberMemory(); + } + + // There is no cleanup to do, since associative containers don't insert + // if the key is already present. + } + })->Arg(1024); + + benchmark::RegisterBenchmark(container + "::insert(value) (new value)", [=](auto& st) { + const std::size_t size = st.range(0); + std::vector in = add_dummy_mapped_type(generate_unique_keys(size + 1)); + Value to_insert = in.back(); + in.pop_back(); + std::vector c(BatchSize, Container(in.begin(), in.end())); + + while (st.KeepRunningBatch(BatchSize)) { + for (std::size_t i = 0; i != BatchSize; ++i) { + c[i].insert(to_insert); + benchmark::DoNotOptimize(c[i]); + benchmark::ClobberMemory(); + } + + st.PauseTiming(); + for (std::size_t i = 0; i != BatchSize; ++i) { + c[i].erase(get_key(to_insert)); + } + st.ResumeTiming(); + } + })->Arg(1024); + + benchmark::RegisterBenchmark(container + "::insert(hint, value) (good hint)", [=](auto& st) { + const std::size_t size = st.range(0); + std::vector in = add_dummy_mapped_type(generate_unique_keys(size + 1)); + Value to_insert = in.back(); + in.pop_back(); + + std::vector c(BatchSize, Container(in.begin(), in.end())); + typename Container::iterator hints[BatchSize]; + for (std::size_t i = 0; i != BatchSize; ++i) { + hints[i] = c[i].lower_bound(get_key(to_insert)); + } + + while (st.KeepRunningBatch(BatchSize)) { + for (std::size_t i = 0; i != BatchSize; ++i) { + c[i].insert(hints[i], to_insert); + benchmark::DoNotOptimize(c[i]); + benchmark::ClobberMemory(); + } + + st.PauseTiming(); + for (std::size_t i = 0; i != BatchSize; ++i) { + c[i].erase(get_key(to_insert)); + hints[i] = c[i].lower_bound(get_key(to_insert)); // refresh hints in case of invalidation + } + st.ResumeTiming(); + } + })->Arg(1024); + + benchmark::RegisterBenchmark(container + "::insert(hint, value) (bad hint)", [=](auto& st) { + const std::size_t size = st.range(0); + std::vector in = add_dummy_mapped_type(generate_unique_keys(size + 1)); + Value to_insert = in.back(); + in.pop_back(); + std::vector c(BatchSize, Container(in.begin(), in.end())); + + while (st.KeepRunningBatch(BatchSize)) { + for (std::size_t i = 0; i != BatchSize; ++i) { + c[i].insert(c[i].begin(), to_insert); + benchmark::DoNotOptimize(c[i]); + benchmark::ClobberMemory(); + } + + st.PauseTiming(); + for (std::size_t i = 0; i != BatchSize; ++i) { + c[i].erase(get_key(to_insert)); + } + st.ResumeTiming(); + } + })->Arg(1024); + + benchmark::RegisterBenchmark(container + "::insert(iterator, iterator) (all new keys)", [=](auto& st) { + const std::size_t size = st.range(0); + std::vector in = add_dummy_mapped_type(generate_unique_keys(size + (size / 10))); + + // Populate a container with a small number of elements, that's what containers will start with. + std::vector small; + for (std::size_t i = 0; i != (size / 10); ++i) { + small.push_back(in.back()); + in.pop_back(); + } + Container c(small.begin(), small.end()); + + for (auto _ : st) { + c.insert(in.begin(), in.end()); + benchmark::DoNotOptimize(c); + benchmark::ClobberMemory(); + + st.PauseTiming(); + c = Container(small.begin(), small.end()); + st.ResumeTiming(); + } + })->Arg(1024); + + benchmark::RegisterBenchmark(container + "::insert(iterator, iterator) (half new keys)", [=](auto& st) { + const std::size_t size = st.range(0); + std::vector in = add_dummy_mapped_type(generate_unique_keys(size)); + + // Populate a container that already contains half the elements we'll try inserting, + // that's what our container will start with. + std::vector small; + for (std::size_t i = 0; i != size / 2; ++i) { + small.push_back(in.at(i * 2)); + } + Container c(small.begin(), small.end()); + + for (auto _ : st) { + c.insert(in.begin(), in.end()); + benchmark::DoNotOptimize(c); + benchmark::ClobberMemory(); + + st.PauseTiming(); + c = Container(small.begin(), small.end()); + st.ResumeTiming(); + } + })->Arg(1024); + + ///////////////////////// + // Erasure + ///////////////////////// + benchmark::RegisterBenchmark(container + "::erase(key) (existent)", [=](auto& st) { + const std::size_t size = st.range(0); + std::vector in = add_dummy_mapped_type(generate_unique_keys(size)); + Value element = in[in.size() / 2]; // pick any element + std::vector c(BatchSize, Container(in.begin(), in.end())); + + while (st.KeepRunningBatch(BatchSize)) { + for (std::size_t i = 0; i != BatchSize; ++i) { + c[i].erase(get_key(element)); + benchmark::DoNotOptimize(c[i]); + benchmark::ClobberMemory(); + } + + st.PauseTiming(); + for (std::size_t i = 0; i != BatchSize; ++i) { + c[i].insert(element); + } + st.ResumeTiming(); + } + })->Arg(1024); + + benchmark::RegisterBenchmark(container + "::erase(key) (non-existent)", [=](auto& st) { + const std::size_t size = st.range(0); + std::vector in = add_dummy_mapped_type(generate_unique_keys(size + 1)); + Value element = in.back(); + in.pop_back(); + Container c(in.begin(), in.end()); + + while (st.KeepRunningBatch(BatchSize)) { + for (std::size_t i = 0; i != BatchSize; ++i) { + c.erase(get_key(element)); + benchmark::DoNotOptimize(c); + benchmark::ClobberMemory(); + } + + // no cleanup required because we erased a non-existent element + } + })->Arg(1024); + + benchmark::RegisterBenchmark(container + "::erase(iterator)", [=](auto& st) { + const std::size_t size = st.range(0); + std::vector in = add_dummy_mapped_type(generate_unique_keys(size)); + Value element = in[in.size() / 2]; // pick any element + + std::vector c; + std::vector iterators; + for (std::size_t i = 0; i != BatchSize; ++i) { + c.push_back(Container(in.begin(), in.end())); + iterators.push_back(c[i].find(get_key(element))); + } + + while (st.KeepRunningBatch(BatchSize)) { + for (std::size_t i = 0; i != BatchSize; ++i) { + c[i].erase(iterators[i]); + benchmark::DoNotOptimize(c[i]); + benchmark::ClobberMemory(); + } + + st.PauseTiming(); + for (std::size_t i = 0; i != BatchSize; ++i) { + iterators[i] = c[i].insert(element).first; + } + st.ResumeTiming(); + } + })->Arg(1024); + + benchmark::RegisterBenchmark(container + "::erase(iterator, iterator) (erase half the container)", [=](auto& st) { + const std::size_t size = st.range(0); + std::vector in = add_dummy_mapped_type(generate_unique_keys(size)); + Container c(in.begin(), in.end()); + + auto first = std::next(c.begin(), c.size() / 4); + auto last = std::next(c.begin(), 3 * (c.size() / 4)); + for (auto _ : st) { + c.erase(first, last); + benchmark::DoNotOptimize(c); + benchmark::ClobberMemory(); + + st.PauseTiming(); + c = Container(in.begin(), in.end()); + first = std::next(c.begin(), c.size() / 4); + last = std::next(c.begin(), 3 * (c.size() / 4)); + st.ResumeTiming(); + } + })->Arg(1024); + + benchmark::RegisterBenchmark(container + "::clear()", [=](auto& st) { + const std::size_t size = st.range(0); + std::vector in = add_dummy_mapped_type(generate_unique_keys(size)); + Container c(in.begin(), in.end()); + + for (auto _ : st) { + c.clear(); + benchmark::DoNotOptimize(c); + benchmark::ClobberMemory(); + + st.PauseTiming(); + c = Container(in.begin(), in.end()); + st.ResumeTiming(); + } + })->Arg(1024); + + ///////////////////////// + // Query + ///////////////////////// + auto bench_with_existent_key = [=](auto func) { + return [=](auto& st) { + const std::size_t size = st.range(0); + std::vector in = add_dummy_mapped_type(generate_unique_keys(size)); + Value element = in[in.size() / 2]; // pick any element + Container c(in.begin(), in.end()); + + while (st.KeepRunningBatch(BatchSize)) { + for (std::size_t i = 0; i != BatchSize; ++i) { + auto result = func(c, element); + benchmark::DoNotOptimize(c); + benchmark::DoNotOptimize(result); + benchmark::ClobberMemory(); + } + } + }; + }; + + auto bench_with_nonexistent_key = [=](auto func) { + return [=](auto& st) { + const std::size_t size = st.range(0); + std::vector in = add_dummy_mapped_type(generate_unique_keys(size + 1)); + Value element = in.back(); + in.pop_back(); + Container c(in.begin(), in.end()); + + while (st.KeepRunningBatch(BatchSize)) { + for (std::size_t i = 0; i != BatchSize; ++i) { + auto result = func(c, element); + benchmark::DoNotOptimize(c); + benchmark::DoNotOptimize(result); + benchmark::ClobberMemory(); + } + } + }; + }; + + benchmark::RegisterBenchmark( + container + "::find(key) (existent)", + bench_with_existent_key([=](Container const& c, Value const& element) { return c.find(get_key(element)); })) + ->Arg(1024); + benchmark::RegisterBenchmark( + container + "::find(key) (non-existent)", + bench_with_nonexistent_key([=](Container const& c, Value const& element) { return c.find(get_key(element)); })) + ->Arg(1024); + + benchmark::RegisterBenchmark( + container + "::count(key) (existent)", + bench_with_existent_key([=](Container const& c, Value const& element) { return c.count(get_key(element)); })) + ->Arg(1024); + benchmark::RegisterBenchmark( + container + "::count(key) (non-existent)", + bench_with_nonexistent_key([=](Container const& c, Value const& element) { return c.count(get_key(element)); })) + ->Arg(1024); + + benchmark::RegisterBenchmark( + container + "::contains(key) (existent)", + bench_with_existent_key([=](Container const& c, Value const& element) { return c.contains(get_key(element)); })) + ->Arg(1024); + benchmark::RegisterBenchmark( + container + "::contains(key) (non-existent)", + bench_with_nonexistent_key([=](Container const& c, Value const& element) { + return c.contains(get_key(element)); + })) + ->Arg(1024); + + benchmark::RegisterBenchmark( + container + "::lower_bound(key) (existent)", + bench_with_existent_key([=](Container const& c, Value const& element) { + return c.lower_bound(get_key(element)); + })) + ->Arg(1024); + benchmark::RegisterBenchmark( + container + "::lower_bound(key) (non-existent)", + bench_with_nonexistent_key([=](Container const& c, Value const& element) { + return c.lower_bound(get_key(element)); + })) + ->Arg(1024); + + benchmark::RegisterBenchmark( + container + "::upper_bound(key) (existent)", + bench_with_existent_key([=](Container const& c, Value const& element) { + return c.upper_bound(get_key(element)); + })) + ->Arg(1024); + benchmark::RegisterBenchmark( + container + "::upper_bound(key) (non-existent)", + bench_with_nonexistent_key([=](Container const& c, Value const& element) { + return c.upper_bound(get_key(element)); + })) + ->Arg(1024); + + benchmark::RegisterBenchmark( + container + "::equal_range(key) (existent)", + bench_with_existent_key([=](Container const& c, Value const& element) { + return c.equal_range(get_key(element)); + })) + ->Arg(1024); + benchmark::RegisterBenchmark( + container + "::equal_range(key) (non-existent)", + bench_with_nonexistent_key([=](Container const& c, Value const& element) { + return c.equal_range(get_key(element)); + })) + ->Arg(1024); +} + +} // namespace support + +#endif // TEST_BENCHMARKS_CONTAINERS_ASSOCIATIVE_CONTAINER_BENCHMARKS_H diff --git a/libcxx/test/benchmarks/containers/flat_map.bench.cpp b/libcxx/test/benchmarks/containers/flat_map.bench.cpp new file mode 100644 index 0000000000000..39971c9319e6c --- /dev/null +++ b/libcxx/test/benchmarks/containers/flat_map.bench.cpp @@ -0,0 +1,25 @@ +//===----------------------------------------------------------------------===// +// +// 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, c++17, c++20, c++23 + +#include +#include + +#include "associative_container_benchmarks.h" +#include "benchmark/benchmark.h" + +int main(int argc, char** argv) { + support::associative_container_benchmarks>("std::flat_map"); + support::associative_container_benchmarks>("std::flat_map"); + + benchmark::Initialize(&argc, argv); + benchmark::RunSpecifiedBenchmarks(); + benchmark::Shutdown(); + return 0; +} diff --git a/libcxx/test/benchmarks/containers/map.bench.cpp b/libcxx/test/benchmarks/containers/map.bench.cpp index e37c7d8d55163..5c5ba7cc9d3db 100644 --- a/libcxx/test/benchmarks/containers/map.bench.cpp +++ b/libcxx/test/benchmarks/containers/map.bench.cpp @@ -6,944 +6,20 @@ // //===----------------------------------------------------------------------===// -// UNSUPPORTED: c++03, c++11, c++14 +// UNSUPPORTED: c++03, c++11, c++14, c++17 -#include -#include #include -#include -#include +#include -#include "../CartesianBenchmarks.h" +#include "associative_container_benchmarks.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 { - 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 { - static constexpr const char* Names[] = {"Begin", "Third", "Correct", "End"}; -}; - -enum class Order { Sorted, Random }; -struct AllOrders : EnumValuesAsTuple { - static constexpr const char* Names[] = {"Sorted", "Random"}; -}; - -struct TestSets { - std::vector Keys; - std::vector > Maps; - std::vector::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()); - } - } - - 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(Map.begin(), Map.end())); -#else - std::map 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 M(Map); - benchmark::DoNotOptimize(M); -#else - std::map 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 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 -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 -struct InsertHint : Base { - using Base::Base; - - template < ::Hint hint> - typename std::enable_if::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::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(State); - } - - std::string name() const { return "BM_InsertHint" + baseName() + Mode::name() + Hint::name(); } -}; - -template -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 -struct InsertAssignHint : Base { - using Base::Base; - - template < ::Hint hint> - typename std::enable_if::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::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(State); - } - - std::string name() const { return "BM_InsertAssignHint" + baseName() + Mode::name() + Hint::name(); } -}; - -template -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 -struct EmplaceHint : Base { - using Base::Base; - - template < ::Hint hint> - typename std::enable_if::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::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(State); - } - - std::string name() const { return "BM_EmplaceHint" + baseName() + Mode::name() + Hint::name(); } -}; - -template -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 -struct TryEmplaceHint : Base { - using Base::Base; - - template < ::Hint hint> - typename std::enable_if::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::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(State); - } - - std::string name() const { return "BM_TryEmplaceHint" + baseName() + Mode::name() + Hint::name(); } -}; - -template -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 -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 -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 -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 -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 -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 -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::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 MapSize{10}; -#else - const std::vector MapSize{10, 100, 1000, 10000, 100000, 1000000}; -#endif - - // Member functions - makeCartesianProductBenchmark(); - makeCartesianProductBenchmark(MapSize); - makeCartesianProductBenchmark(MapSize); - makeCartesianProductBenchmark(MapSize); - - // Capacity - makeCartesianProductBenchmark(MapSize); - makeCartesianProductBenchmark(MapSize); - - // Modifiers - makeCartesianProductBenchmark(MapSize); - makeCartesianProductBenchmark(MapSize); - makeCartesianProductBenchmark(MapSize); - makeCartesianProductBenchmark(MapSize); - makeCartesianProductBenchmark(MapSize); - - makeCartesianProductBenchmark(MapSize); - makeCartesianProductBenchmark(MapSize); - makeCartesianProductBenchmark(MapSize); - makeCartesianProductBenchmark(MapSize); - makeCartesianProductBenchmark(MapSize); - makeCartesianProductBenchmark(MapSize); - makeCartesianProductBenchmark(MapSize); - - // Lookup - makeCartesianProductBenchmark(MapSize); - makeCartesianProductBenchmark(MapSize); - makeCartesianProductBenchmark(MapSize); - makeCartesianProductBenchmark(MapSize); - makeCartesianProductBenchmark(MapSize); + support::associative_container_benchmarks>("std::map"); + support::associative_container_benchmarks>("std::map"); + benchmark::Initialize(&argc, argv); benchmark::RunSpecifiedBenchmarks(); + benchmark::Shutdown(); + return 0; } diff --git a/libcxx/test/benchmarks/containers/set.bench.cpp b/libcxx/test/benchmarks/containers/set.bench.cpp new file mode 100644 index 0000000000000..6a8de0862f2ae --- /dev/null +++ b/libcxx/test/benchmarks/containers/set.bench.cpp @@ -0,0 +1,23 @@ +//===----------------------------------------------------------------------===// +// +// 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, c++17 + +#include + +#include "associative_container_benchmarks.h" +#include "benchmark/benchmark.h" + +int main(int argc, char** argv) { + support::associative_container_benchmarks>("std::set"); + + benchmark::Initialize(&argc, argv); + benchmark::RunSpecifiedBenchmarks(); + benchmark::Shutdown(); + return 0; +}