[libcxx-commits] [libcxx] 1d319df - [libc++] Implement generic associative container benchmarks (#123663)
via libcxx-commits
libcxx-commits at lists.llvm.org
Thu Feb 6 13:08:59 PST 2025
Author: Louis Dionne
Date: 2025-02-06T16:08:55-05:00
New Revision: 1d319dfe7d1ea39354ff61c5568bfecad3ce3d15
URL: https://github.com/llvm/llvm-project/commit/1d319dfe7d1ea39354ff61c5568bfecad3ce3d15
DIFF: https://github.com/llvm/llvm-project/commit/1d319dfe7d1ea39354ff61c5568bfecad3ce3d15.diff
LOG: [libc++] Implement generic associative container benchmarks (#123663)
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).
This bumps the requirement for the map benchmarks from C++17 to C++20
because the common header that provides associative container benchmarks
requires support for C++20 concepts.
Added:
libcxx/test/benchmarks/containers/associative/associative_container_benchmarks.h
libcxx/test/benchmarks/containers/associative/flat_map.bench.cpp
libcxx/test/benchmarks/containers/associative/flat_multimap.bench.cpp
libcxx/test/benchmarks/containers/associative/map.bench.cpp
libcxx/test/benchmarks/containers/associative/multimap.bench.cpp
libcxx/test/benchmarks/containers/associative/multiset.bench.cpp
libcxx/test/benchmarks/containers/associative/set.bench.cpp
libcxx/test/benchmarks/containers/associative/unordered_map.bench.cpp
libcxx/test/benchmarks/containers/associative/unordered_multimap.bench.cpp
libcxx/test/benchmarks/containers/associative/unordered_multiset.bench.cpp
libcxx/test/benchmarks/containers/associative/unordered_set.bench.cpp
Modified:
libcxx/test/benchmarks/GenerateInput.h
Removed:
libcxx/test/benchmarks/containers/map.bench.cpp
libcxx/test/benchmarks/containers/ordered_set.bench.cpp
libcxx/test/benchmarks/containers/unordered_set.bench.cpp
################################################################################
diff --git a/libcxx/test/benchmarks/GenerateInput.h b/libcxx/test/benchmarks/GenerateInput.h
index 081631a32b21d90..c87fd69162e9d43 100644
--- a/libcxx/test/benchmarks/GenerateInput.h
+++ b/libcxx/test/benchmarks/GenerateInput.h
@@ -190,6 +190,7 @@ struct Generate<T> {
static T arbitrary() { return 42; }
static T cheap() { return 42; }
static T expensive() { return 42; }
+ static T random() { return getRandomInteger<T>(std::numeric_limits<T>::min(), std::numeric_limits<T>::max()); }
};
template <>
@@ -197,6 +198,10 @@ struct Generate<std::string> {
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<std::size_t>(1, 1024);
+ return getRandomString(length);
+ }
};
#endif // BENCHMARK_GENERATE_INPUT_H
diff --git a/libcxx/test/benchmarks/containers/associative/associative_container_benchmarks.h b/libcxx/test/benchmarks/containers/associative/associative_container_benchmarks.h
new file mode 100644
index 000000000000000..fb4455c4aa9daf5
--- /dev/null
+++ b/libcxx/test/benchmarks/containers/associative/associative_container_benchmarks.h
@@ -0,0 +1,512 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <algorithm>
+#include <iterator>
+#include <random>
+#include <string>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+#include "../../GenerateInput.h"
+
+namespace support {
+
+template <class Container>
+struct adapt_operations {
+ // using ValueType = ...;
+ // using KeyType = ...;
+ // static ValueType value_from_key(KeyType const& k);
+ // static KeyType key_from_value(ValueType const& value);
+
+ // using InsertionResult = ...;
+ // static Container::iterator get_iterator(InsertionResult const&);
+};
+
+template <class Container>
+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<Key> keys;
+ while (keys.size() < n) {
+ Key k = Generate<Key>::random();
+ keys.insert(k);
+ }
+ return std::vector<Key>(keys.begin(), keys.end());
+ };
+
+ auto make_value_types = [](std::vector<Key> const& keys) {
+ std::vector<Value> kv;
+ for (Key const& k : keys)
+ kv.push_back(adapt_operations<Container>::value_from_key(k));
+ return kv;
+ };
+
+ auto get_key = [](Value const& v) { return adapt_operations<Container>::key_from_value(v); };
+
+ auto bench = [&](std::string operation, auto f) {
+ benchmark::RegisterBenchmark(container + "::" + operation, f)->Arg(32)->Arg(1024)->Arg(8192);
+ };
+
+ static constexpr bool is_multi_key_container =
+ !std::is_same_v<typename adapt_operations<Container>::InsertionResult,
+ std::pair<typename Container::iterator, bool>>;
+
+ static constexpr bool is_ordered_container = requires(Container c, Key k) { c.lower_bound(k); };
+
+ // 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 = 32;
+
+ struct alignas(Container) ScratchSpace {
+ char storage[sizeof(Container)];
+ };
+
+ /////////////////////////
+ // Constructors
+ /////////////////////////
+ bench("ctor(const&)", [=](auto& st) {
+ const std::size_t size = st.range(0);
+ std::vector<Value> in = make_value_types(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<Container*>(c + i)->~Container();
+ }
+ st.ResumeTiming();
+ }
+ });
+
+ bench("ctor(iterator, iterator) (unsorted sequence)", [=](auto& st) {
+ const std::size_t size = st.range(0);
+ std::mt19937 randomness;
+ std::vector<Key> keys = generate_unique_keys(size);
+ std::shuffle(keys.begin(), keys.end(), randomness);
+ std::vector<Value> in = make_value_types(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<Container*>(c + i)->~Container();
+ }
+ st.ResumeTiming();
+ }
+ });
+
+ bench("ctor(iterator, iterator) (sorted sequence)", [=](auto& st) {
+ const std::size_t size = st.range(0);
+ std::vector<Key> keys = generate_unique_keys(size);
+ std::sort(keys.begin(), keys.end());
+ std::vector<Value> in = make_value_types(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<Container*>(c + i)->~Container();
+ }
+ st.ResumeTiming();
+ }
+ });
+
+ /////////////////////////
+ // Assignment
+ /////////////////////////
+ bench("operator=(const&)", [=](auto& st) {
+ const std::size_t size = st.range(0);
+ std::vector<Value> in = make_value_types(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();
+ }
+ });
+
+ /////////////////////////
+ // Insertion
+ /////////////////////////
+ bench("insert(value) (already present)", [=](auto& st) {
+ const std::size_t size = st.range(0);
+ std::vector<Value> in = make_value_types(generate_unique_keys(size));
+ Value to_insert = in[in.size() / 2]; // pick any existing value
+ std::vector<Container> c(BatchSize, Container(in.begin(), in.end()));
+ typename Container::iterator inserted[BatchSize];
+
+ while (st.KeepRunningBatch(BatchSize)) {
+ for (std::size_t i = 0; i != BatchSize; ++i) {
+ inserted[i] = adapt_operations<Container>::get_iterator(c[i].insert(to_insert));
+ benchmark::DoNotOptimize(inserted[i]);
+ benchmark::DoNotOptimize(c[i]);
+ benchmark::ClobberMemory();
+ }
+
+ if constexpr (is_multi_key_container) {
+ st.PauseTiming();
+ for (std::size_t i = 0; i != BatchSize; ++i) {
+ c[i].erase(inserted[i]);
+ }
+ st.ResumeTiming();
+ }
+ }
+ });
+
+ bench("insert(value) (new value)", [=](auto& st) {
+ const std::size_t size = st.range(0);
+ std::vector<Value> in = make_value_types(generate_unique_keys(size + 1));
+ Value to_insert = in.back();
+ in.pop_back();
+ std::vector<Container> c(BatchSize, Container(in.begin(), in.end()));
+
+ while (st.KeepRunningBatch(BatchSize)) {
+ for (std::size_t i = 0; i != BatchSize; ++i) {
+ auto result = c[i].insert(to_insert);
+ benchmark::DoNotOptimize(result);
+ 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();
+ }
+ });
+
+ // The insert(hint, ...) methods are only relevant for ordered containers, and we lack
+ // a good way to compute a hint for unordered ones.
+ if constexpr (is_ordered_container) {
+ bench("insert(hint, value) (good hint)", [=](auto& st) {
+ const std::size_t size = st.range(0);
+ std::vector<Value> in = make_value_types(generate_unique_keys(size + 1));
+ Value to_insert = in.back();
+ in.pop_back();
+
+ std::vector<Container> 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) {
+ auto result = c[i].insert(hints[i], to_insert);
+ benchmark::DoNotOptimize(result);
+ 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();
+ }
+ });
+
+ bench("insert(hint, value) (bad hint)", [=](auto& st) {
+ const std::size_t size = st.range(0);
+ std::vector<Value> in = make_value_types(generate_unique_keys(size + 1));
+ Value to_insert = in.back();
+ in.pop_back();
+ std::vector<Container> c(BatchSize, Container(in.begin(), in.end()));
+
+ while (st.KeepRunningBatch(BatchSize)) {
+ for (std::size_t i = 0; i != BatchSize; ++i) {
+ auto result = c[i].insert(c[i].begin(), to_insert);
+ benchmark::DoNotOptimize(result);
+ 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();
+ }
+ });
+ }
+
+ bench("insert(iterator, iterator) (all new keys)", [=](auto& st) {
+ const std::size_t size = st.range(0);
+ std::vector<Value> in = make_value_types(generate_unique_keys(size + (size / 10)));
+
+ // Populate a container with a small number of elements, that's what containers will start with.
+ std::vector<Value> 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 ([[maybe_unused]] auto _ : st) {
+ c.insert(in.begin(), in.end());
+ benchmark::DoNotOptimize(c);
+ benchmark::ClobberMemory();
+
+ st.PauseTiming();
+ c = Container(small.begin(), small.end());
+ st.ResumeTiming();
+ }
+ });
+
+ bench("insert(iterator, iterator) (half new keys)", [=](auto& st) {
+ const std::size_t size = st.range(0);
+ std::vector<Value> in = make_value_types(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<Value> 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 ([[maybe_unused]] auto _ : st) {
+ c.insert(in.begin(), in.end());
+ benchmark::DoNotOptimize(c);
+ benchmark::ClobberMemory();
+
+ st.PauseTiming();
+ c = Container(small.begin(), small.end());
+ st.ResumeTiming();
+ }
+ });
+
+ /////////////////////////
+ // Erasure
+ /////////////////////////
+ bench("erase(key) (existent)", [=](auto& st) {
+ const std::size_t size = st.range(0);
+ std::vector<Value> in = make_value_types(generate_unique_keys(size));
+ Value element = in[in.size() / 2]; // pick any element
+ std::vector<Container> c(BatchSize, Container(in.begin(), in.end()));
+
+ while (st.KeepRunningBatch(BatchSize)) {
+ for (std::size_t i = 0; i != BatchSize; ++i) {
+ auto result = c[i].erase(get_key(element));
+ benchmark::DoNotOptimize(result);
+ benchmark::DoNotOptimize(c[i]);
+ benchmark::ClobberMemory();
+ }
+
+ st.PauseTiming();
+ for (std::size_t i = 0; i != BatchSize; ++i) {
+ c[i].insert(element);
+ }
+ st.ResumeTiming();
+ }
+ });
+
+ bench("erase(key) (non-existent)", [=](auto& st) {
+ const std::size_t size = st.range(0);
+ std::vector<Value> in = make_value_types(generate_unique_keys(size + BatchSize));
+ std::vector<Key> keys;
+ for (std::size_t i = 0; i != BatchSize; ++i) {
+ keys.push_back(get_key(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 = c.erase(keys[i]);
+ benchmark::DoNotOptimize(result);
+ benchmark::DoNotOptimize(c);
+ benchmark::ClobberMemory();
+ }
+
+ // no cleanup required because we erased a non-existent element
+ }
+ });
+
+ bench("erase(iterator)", [=](auto& st) {
+ const std::size_t size = st.range(0);
+ std::vector<Value> in = make_value_types(generate_unique_keys(size));
+ Value element = in[in.size() / 2]; // pick any element
+
+ std::vector<Container> c;
+ std::vector<typename Container::iterator> 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) {
+ auto result = c[i].erase(iterators[i]);
+ benchmark::DoNotOptimize(result);
+ benchmark::DoNotOptimize(c[i]);
+ benchmark::ClobberMemory();
+ }
+
+ st.PauseTiming();
+ for (std::size_t i = 0; i != BatchSize; ++i) {
+ iterators[i] = adapt_operations<Container>::get_iterator(c[i].insert(element));
+ }
+ st.ResumeTiming();
+ }
+ });
+
+ bench("erase(iterator, iterator) (erase half the container)", [=](auto& st) {
+ const std::size_t size = st.range(0);
+ std::vector<Value> in = make_value_types(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 ([[maybe_unused]] auto _ : st) {
+ auto result = c.erase(first, last);
+ benchmark::DoNotOptimize(result);
+ 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();
+ }
+ });
+
+ bench("clear()", [=](auto& st) {
+ const std::size_t size = st.range(0);
+ std::vector<Value> in = make_value_types(generate_unique_keys(size));
+ Container c(in.begin(), in.end());
+
+ for ([[maybe_unused]] auto _ : st) {
+ c.clear();
+ benchmark::DoNotOptimize(c);
+ benchmark::ClobberMemory();
+
+ st.PauseTiming();
+ c = Container(in.begin(), in.end());
+ st.ResumeTiming();
+ }
+ });
+
+ /////////////////////////
+ // Query
+ /////////////////////////
+ auto with_existent_key = [=](auto func) {
+ return [=](auto& st) {
+ const std::size_t size = st.range(0);
+ std::vector<Value> in = make_value_types(generate_unique_keys(size));
+ // Pick any `BatchSize` number of elements
+ std::vector<Key> keys;
+ for (std::size_t i = 0; i < in.size(); i += (in.size() / BatchSize)) {
+ keys.push_back(get_key(in.at(i)));
+ }
+ Container c(in.begin(), in.end());
+
+ while (st.KeepRunningBatch(BatchSize)) {
+ for (std::size_t i = 0; i != BatchSize; ++i) {
+ auto result = func(c, keys[i]);
+ benchmark::DoNotOptimize(c);
+ benchmark::DoNotOptimize(result);
+ benchmark::ClobberMemory();
+ }
+ }
+ };
+ };
+
+ auto with_nonexistent_key = [=](auto func) {
+ return [=](auto& st) {
+ const std::size_t size = st.range(0);
+ std::vector<Value> in = make_value_types(generate_unique_keys(size + BatchSize));
+ std::vector<Key> keys;
+ for (std::size_t i = 0; i != BatchSize; ++i) {
+ keys.push_back(get_key(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, keys[i]);
+ benchmark::DoNotOptimize(c);
+ benchmark::DoNotOptimize(result);
+ benchmark::ClobberMemory();
+ }
+ }
+ };
+ };
+
+ auto find = [](Container const& c, Key const& key) { return c.find(key); };
+ bench("find(key) (existent)", with_existent_key(find));
+ bench("find(key) (non-existent)", with_nonexistent_key(find));
+
+ auto count = [](Container const& c, Key const& key) { return c.count(key); };
+ bench("count(key) (existent)", with_existent_key(count));
+ bench("count(key) (non-existent)", with_nonexistent_key(count));
+
+ auto contains = [](Container const& c, Key const& key) { return c.contains(key); };
+ bench("contains(key) (existent)", with_existent_key(contains));
+ bench("contains(key) (non-existent)", with_nonexistent_key(contains));
+
+ if constexpr (is_ordered_container) {
+ auto lower_bound = [](Container const& c, Key const& key) { return c.lower_bound(key); };
+ bench("lower_bound(key) (existent)", with_existent_key(lower_bound));
+ bench("lower_bound(key) (non-existent)", with_nonexistent_key(lower_bound));
+
+ auto upper_bound = [](Container const& c, Key const& key) { return c.upper_bound(key); };
+ bench("upper_bound(key) (existent)", with_existent_key(upper_bound));
+ bench("upper_bound(key) (non-existent)", with_nonexistent_key(upper_bound));
+
+ auto equal_range = [](Container const& c, Key const& key) { return c.equal_range(key); };
+ bench("equal_range(key) (existent)", with_existent_key(equal_range));
+ bench("equal_range(key) (non-existent)", with_nonexistent_key(equal_range));
+ }
+}
+
+} // namespace support
+
+#endif // TEST_BENCHMARKS_CONTAINERS_ASSOCIATIVE_CONTAINER_BENCHMARKS_H
diff --git a/libcxx/test/benchmarks/containers/associative/flat_map.bench.cpp b/libcxx/test/benchmarks/containers/associative/flat_map.bench.cpp
new file mode 100644
index 000000000000000..82902d50f31e6cb
--- /dev/null
+++ b/libcxx/test/benchmarks/containers/associative/flat_map.bench.cpp
@@ -0,0 +1,36 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+#include <flat_map>
+#include <utility>
+
+#include "associative_container_benchmarks.h"
+#include "../../GenerateInput.h"
+#include "benchmark/benchmark.h"
+
+template <class K, class V>
+struct support::adapt_operations<std::flat_map<K, V>> {
+ using ValueType = typename std::flat_map<K, V>::value_type;
+ using KeyType = typename std::flat_map<K, V>::key_type;
+ static ValueType value_from_key(KeyType const& k) { return {k, Generate<V>::arbitrary()}; }
+ static KeyType key_from_value(ValueType const& value) { return value.first; }
+
+ using InsertionResult = std::pair<typename std::flat_map<K, V>::iterator, bool>;
+ static auto get_iterator(InsertionResult const& result) { return result.first; }
+};
+
+int main(int argc, char** argv) {
+ support::associative_container_benchmarks<std::flat_map<int, int>>("std::flat_map<int, int>");
+
+ benchmark::Initialize(&argc, argv);
+ benchmark::RunSpecifiedBenchmarks();
+ benchmark::Shutdown();
+ return 0;
+}
diff --git a/libcxx/test/benchmarks/containers/associative/flat_multimap.bench.cpp b/libcxx/test/benchmarks/containers/associative/flat_multimap.bench.cpp
new file mode 100644
index 000000000000000..f752f79b3b45476
--- /dev/null
+++ b/libcxx/test/benchmarks/containers/associative/flat_multimap.bench.cpp
@@ -0,0 +1,35 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+#include <flat_map>
+
+#include "associative_container_benchmarks.h"
+#include "../../GenerateInput.h"
+#include "benchmark/benchmark.h"
+
+template <class K, class V>
+struct support::adapt_operations<std::flat_multimap<K, V>> {
+ using ValueType = typename std::flat_multimap<K, V>::value_type;
+ using KeyType = typename std::flat_multimap<K, V>::key_type;
+ static ValueType value_from_key(KeyType const& k) { return {k, Generate<V>::arbitrary()}; }
+ static KeyType key_from_value(ValueType const& value) { return value.first; }
+
+ using InsertionResult = typename std::flat_multimap<K, V>::iterator;
+ static auto get_iterator(InsertionResult const& result) { return result; }
+};
+
+int main(int argc, char** argv) {
+ support::associative_container_benchmarks<std::flat_multimap<int, int>>("std::flat_multimap<int, int>");
+
+ benchmark::Initialize(&argc, argv);
+ benchmark::RunSpecifiedBenchmarks();
+ benchmark::Shutdown();
+ return 0;
+}
diff --git a/libcxx/test/benchmarks/containers/associative/map.bench.cpp b/libcxx/test/benchmarks/containers/associative/map.bench.cpp
new file mode 100644
index 000000000000000..cee669ae0a667de
--- /dev/null
+++ b/libcxx/test/benchmarks/containers/associative/map.bench.cpp
@@ -0,0 +1,37 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <map>
+#include <string>
+#include <utility>
+
+#include "associative_container_benchmarks.h"
+#include "../../GenerateInput.h"
+#include "benchmark/benchmark.h"
+
+template <class K, class V>
+struct support::adapt_operations<std::map<K, V>> {
+ using ValueType = typename std::map<K, V>::value_type;
+ using KeyType = typename std::map<K, V>::key_type;
+ static ValueType value_from_key(KeyType const& k) { return {k, Generate<V>::arbitrary()}; }
+ static KeyType key_from_value(ValueType const& value) { return value.first; }
+
+ using InsertionResult = std::pair<typename std::map<K, V>::iterator, bool>;
+ static auto get_iterator(InsertionResult const& result) { return result.first; }
+};
+
+int main(int argc, char** argv) {
+ support::associative_container_benchmarks<std::map<int, int>>("std::map<int, int>");
+
+ benchmark::Initialize(&argc, argv);
+ benchmark::RunSpecifiedBenchmarks();
+ benchmark::Shutdown();
+ return 0;
+}
diff --git a/libcxx/test/benchmarks/containers/associative/multimap.bench.cpp b/libcxx/test/benchmarks/containers/associative/multimap.bench.cpp
new file mode 100644
index 000000000000000..6ae93f06aa36385
--- /dev/null
+++ b/libcxx/test/benchmarks/containers/associative/multimap.bench.cpp
@@ -0,0 +1,36 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <map>
+#include <string>
+
+#include "associative_container_benchmarks.h"
+#include "../../GenerateInput.h"
+#include "benchmark/benchmark.h"
+
+template <class K, class V>
+struct support::adapt_operations<std::multimap<K, V>> {
+ using ValueType = typename std::multimap<K, V>::value_type;
+ using KeyType = typename std::multimap<K, V>::key_type;
+ static ValueType value_from_key(KeyType const& k) { return {k, Generate<V>::arbitrary()}; }
+ static KeyType key_from_value(ValueType const& value) { return value.first; }
+
+ using InsertionResult = typename std::multimap<K, V>::iterator;
+ static auto get_iterator(InsertionResult const& result) { return result; }
+};
+
+int main(int argc, char** argv) {
+ support::associative_container_benchmarks<std::multimap<int, int>>("std::multimap<int, int>");
+
+ benchmark::Initialize(&argc, argv);
+ benchmark::RunSpecifiedBenchmarks();
+ benchmark::Shutdown();
+ return 0;
+}
diff --git a/libcxx/test/benchmarks/containers/associative/multiset.bench.cpp b/libcxx/test/benchmarks/containers/associative/multiset.bench.cpp
new file mode 100644
index 000000000000000..894f159a52e4adb
--- /dev/null
+++ b/libcxx/test/benchmarks/containers/associative/multiset.bench.cpp
@@ -0,0 +1,34 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <set>
+
+#include "associative_container_benchmarks.h"
+#include "benchmark/benchmark.h"
+
+template <class K>
+struct support::adapt_operations<std::multiset<K>> {
+ using ValueType = typename std::multiset<K>::value_type;
+ using KeyType = typename std::multiset<K>::key_type;
+ static ValueType value_from_key(KeyType const& k) { return k; }
+ static KeyType key_from_value(ValueType const& value) { return value; }
+
+ using InsertionResult = typename std::multiset<K>::iterator;
+ static auto get_iterator(InsertionResult const& result) { return result; }
+};
+
+int main(int argc, char** argv) {
+ support::associative_container_benchmarks<std::multiset<int>>("std::multiset<int>");
+
+ benchmark::Initialize(&argc, argv);
+ benchmark::RunSpecifiedBenchmarks();
+ benchmark::Shutdown();
+ return 0;
+}
diff --git a/libcxx/test/benchmarks/containers/associative/set.bench.cpp b/libcxx/test/benchmarks/containers/associative/set.bench.cpp
new file mode 100644
index 000000000000000..6b7b142c792ba0f
--- /dev/null
+++ b/libcxx/test/benchmarks/containers/associative/set.bench.cpp
@@ -0,0 +1,35 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <set>
+#include <utility>
+
+#include "associative_container_benchmarks.h"
+#include "benchmark/benchmark.h"
+
+template <class K>
+struct support::adapt_operations<std::set<K>> {
+ using ValueType = typename std::set<K>::value_type;
+ using KeyType = typename std::set<K>::key_type;
+ static ValueType value_from_key(KeyType const& k) { return k; }
+ static KeyType key_from_value(ValueType const& value) { return value; }
+
+ using InsertionResult = std::pair<typename std::set<K>::iterator, bool>;
+ static auto get_iterator(InsertionResult const& result) { return result.first; }
+};
+
+int main(int argc, char** argv) {
+ support::associative_container_benchmarks<std::set<int>>("std::set<int>");
+
+ benchmark::Initialize(&argc, argv);
+ benchmark::RunSpecifiedBenchmarks();
+ benchmark::Shutdown();
+ return 0;
+}
diff --git a/libcxx/test/benchmarks/containers/associative/unordered_map.bench.cpp b/libcxx/test/benchmarks/containers/associative/unordered_map.bench.cpp
new file mode 100644
index 000000000000000..57adec2d214d447
--- /dev/null
+++ b/libcxx/test/benchmarks/containers/associative/unordered_map.bench.cpp
@@ -0,0 +1,36 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <unordered_map>
+#include <utility>
+
+#include "associative_container_benchmarks.h"
+#include "../../GenerateInput.h"
+#include "benchmark/benchmark.h"
+
+template <class K, class V>
+struct support::adapt_operations<std::unordered_map<K, V>> {
+ using ValueType = typename std::unordered_map<K, V>::value_type;
+ using KeyType = typename std::unordered_map<K, V>::key_type;
+ static ValueType value_from_key(KeyType const& k) { return {k, Generate<V>::arbitrary()}; }
+ static KeyType key_from_value(ValueType const& value) { return value.first; }
+
+ using InsertionResult = std::pair<typename std::unordered_map<K, V>::iterator, bool>;
+ static auto get_iterator(InsertionResult const& result) { return result.first; }
+};
+
+int main(int argc, char** argv) {
+ support::associative_container_benchmarks<std::unordered_map<int, int>>("std::unordered_map<int, int>");
+
+ benchmark::Initialize(&argc, argv);
+ benchmark::RunSpecifiedBenchmarks();
+ benchmark::Shutdown();
+ return 0;
+}
diff --git a/libcxx/test/benchmarks/containers/associative/unordered_multimap.bench.cpp b/libcxx/test/benchmarks/containers/associative/unordered_multimap.bench.cpp
new file mode 100644
index 000000000000000..8738ca4bf9f0c94
--- /dev/null
+++ b/libcxx/test/benchmarks/containers/associative/unordered_multimap.bench.cpp
@@ -0,0 +1,35 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <unordered_map>
+
+#include "associative_container_benchmarks.h"
+#include "../../GenerateInput.h"
+#include "benchmark/benchmark.h"
+
+template <class K, class V>
+struct support::adapt_operations<std::unordered_multimap<K, V>> {
+ using ValueType = typename std::unordered_multimap<K, V>::value_type;
+ using KeyType = typename std::unordered_multimap<K, V>::key_type;
+ static ValueType value_from_key(KeyType const& k) { return {k, Generate<V>::arbitrary()}; }
+ static KeyType key_from_value(ValueType const& value) { return value.first; }
+
+ using InsertionResult = typename std::unordered_multimap<K, V>::iterator;
+ static auto get_iterator(InsertionResult const& result) { return result; }
+};
+
+int main(int argc, char** argv) {
+ support::associative_container_benchmarks<std::unordered_multimap<int, int>>("std::unordered_multimap<int, int>");
+
+ benchmark::Initialize(&argc, argv);
+ benchmark::RunSpecifiedBenchmarks();
+ benchmark::Shutdown();
+ return 0;
+}
diff --git a/libcxx/test/benchmarks/containers/associative/unordered_multiset.bench.cpp b/libcxx/test/benchmarks/containers/associative/unordered_multiset.bench.cpp
new file mode 100644
index 000000000000000..4888b01bfeba07e
--- /dev/null
+++ b/libcxx/test/benchmarks/containers/associative/unordered_multiset.bench.cpp
@@ -0,0 +1,34 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <unordered_set>
+
+#include "associative_container_benchmarks.h"
+#include "benchmark/benchmark.h"
+
+template <class K>
+struct support::adapt_operations<std::unordered_multiset<K>> {
+ using ValueType = typename std::unordered_multiset<K>::value_type;
+ using KeyType = typename std::unordered_multiset<K>::key_type;
+ static ValueType value_from_key(KeyType const& k) { return k; }
+ static KeyType key_from_value(ValueType const& value) { return value; }
+
+ using InsertionResult = typename std::unordered_multiset<K>::iterator;
+ static auto get_iterator(InsertionResult const& result) { return result; }
+};
+
+int main(int argc, char** argv) {
+ support::associative_container_benchmarks<std::unordered_multiset<int>>("std::unordered_multiset<int>");
+
+ benchmark::Initialize(&argc, argv);
+ benchmark::RunSpecifiedBenchmarks();
+ benchmark::Shutdown();
+ return 0;
+}
diff --git a/libcxx/test/benchmarks/containers/associative/unordered_set.bench.cpp b/libcxx/test/benchmarks/containers/associative/unordered_set.bench.cpp
new file mode 100644
index 000000000000000..56420bdaadfbf94
--- /dev/null
+++ b/libcxx/test/benchmarks/containers/associative/unordered_set.bench.cpp
@@ -0,0 +1,35 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <unordered_set>
+#include <utility>
+
+#include "associative_container_benchmarks.h"
+#include "benchmark/benchmark.h"
+
+template <class K>
+struct support::adapt_operations<std::unordered_set<K>> {
+ using ValueType = typename std::unordered_set<K>::value_type;
+ using KeyType = typename std::unordered_set<K>::key_type;
+ static ValueType value_from_key(KeyType const& k) { return k; }
+ static KeyType key_from_value(ValueType const& value) { return value; }
+
+ using InsertionResult = std::pair<typename std::unordered_set<K>::iterator, bool>;
+ static auto get_iterator(InsertionResult const& result) { return result.first; }
+};
+
+int main(int argc, char** argv) {
+ support::associative_container_benchmarks<std::unordered_set<int>>("std::unordered_set<int>");
+
+ 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
deleted file mode 100644
index e37c7d8d5516350..000000000000000
--- a/libcxx/test/benchmarks/containers/map.bench.cpp
+++ /dev/null
@@ -1,949 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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();
-}
diff --git a/libcxx/test/benchmarks/containers/ordered_set.bench.cpp b/libcxx/test/benchmarks/containers/ordered_set.bench.cpp
deleted file mode 100644
index cb68902c6dcc88f..000000000000000
--- a/libcxx/test/benchmarks/containers/ordered_set.bench.cpp
+++ /dev/null
@@ -1,232 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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 <memory>
-#include <numeric>
-#include <random>
-#include <set>
-#include <string>
-#include <vector>
-
-#include "../CartesianBenchmarks.h"
-#include "benchmark/benchmark.h"
-#include "test_macros.h"
-
-namespace {
-
-enum class HitType { Hit, Miss };
-
-struct AllHitTypes : EnumValuesAsTuple<AllHitTypes, HitType, 2> {
- static constexpr const char* Names[] = {"Hit", "Miss"};
-};
-
-enum class AccessPattern { Ordered, Random };
-
-struct AllAccessPattern : EnumValuesAsTuple<AllAccessPattern, AccessPattern, 2> {
- static constexpr const char* Names[] = {"Ordered", "Random"};
-};
-
-void sortKeysBy(std::vector<uint64_t>& Keys, AccessPattern AP) {
- if (AP == AccessPattern::Random) {
- std::random_device R;
- std::mt19937 M(R());
- std::shuffle(std::begin(Keys), std::end(Keys), M);
- }
-}
-
-struct TestSets {
- std::vector<std::set<uint64_t> > Sets;
- std::vector<uint64_t> Keys;
-};
-
-TestSets makeTestingSets(size_t TableSize, size_t NumTables, HitType Hit, AccessPattern Access) {
- TestSets R;
- R.Sets.resize(1);
-
- for (uint64_t I = 0; I < TableSize; ++I) {
- R.Sets[0].insert(2 * I);
- R.Keys.push_back(Hit == HitType::Hit ? 2 * I : 2 * I + 1);
- }
- R.Sets.resize(NumTables, R.Sets[0]);
- sortKeysBy(R.Keys, Access);
-
- return R;
-}
-
-struct Base {
- size_t TableSize;
- size_t NumTables;
- Base(size_t T, size_t N) : TableSize(T), NumTables(N) {}
-
- bool skip() const {
- size_t Total = TableSize * NumTables;
- return Total < 100 || Total > 1000000;
- }
-
- std::string baseName() const {
- return "_TableSize" + std::to_string(TableSize) + "_NumTables" + std::to_string(NumTables);
- }
-};
-
-template <class Access>
-struct Create : Base {
- using Base::Base;
-
- void run(benchmark::State& State) const {
- std::vector<uint64_t> Keys(TableSize);
- std::iota(Keys.begin(), Keys.end(), uint64_t{0});
- sortKeysBy(Keys, Access());
-
- while (State.KeepRunningBatch(TableSize * NumTables)) {
- std::vector<std::set<uint64_t>> Sets(NumTables);
- for (auto K : Keys) {
- for (auto& Set : Sets) {
- benchmark::DoNotOptimize(Set.insert(K));
- }
- }
- }
- }
-
- std::string name() const { return "BM_Create" + Access::name() + baseName(); }
-};
-
-template <class Hit, class Access>
-struct Find : Base {
- using Base::Base;
-
- void run(benchmark::State& State) const {
- auto Data = makeTestingSets(TableSize, NumTables, Hit(), Access());
-
- while (State.KeepRunningBatch(TableSize * NumTables)) {
- for (auto K : Data.Keys) {
- for (auto& Set : Data.Sets) {
- benchmark::DoNotOptimize(Set.find(K));
- }
- }
- }
- }
-
- std::string name() const { return "BM_Find" + Hit::name() + Access::name() + baseName(); }
-};
-
-template <class Hit, class Access>
-struct FindNeEnd : Base {
- using Base::Base;
-
- void run(benchmark::State& State) const {
- auto Data = makeTestingSets(TableSize, NumTables, Hit(), Access());
-
- while (State.KeepRunningBatch(TableSize * NumTables)) {
- for (auto K : Data.Keys) {
- for (auto& Set : Data.Sets) {
- benchmark::DoNotOptimize(Set.find(K) != Set.end());
- }
- }
- }
- }
-
- std::string name() const { return "BM_FindNeEnd" + Hit::name() + Access::name() + baseName(); }
-};
-
-template <class Access>
-struct InsertHit : Base {
- using Base::Base;
-
- void run(benchmark::State& State) const {
- auto Data = makeTestingSets(TableSize, NumTables, HitType::Hit, Access());
-
- while (State.KeepRunningBatch(TableSize * NumTables)) {
- for (auto K : Data.Keys) {
- for (auto& Set : Data.Sets) {
- benchmark::DoNotOptimize(Set.insert(K));
- }
- }
- }
- }
-
- std::string name() const { return "BM_InsertHit" + Access::name() + baseName(); }
-};
-
-template <class Access>
-struct InsertMissAndErase : Base {
- using Base::Base;
-
- void run(benchmark::State& State) const {
- auto Data = makeTestingSets(TableSize, NumTables, HitType::Miss, Access());
-
- while (State.KeepRunningBatch(TableSize * NumTables)) {
- for (auto K : Data.Keys) {
- for (auto& Set : Data.Sets) {
- benchmark::DoNotOptimize(Set.erase(Set.insert(K).first));
- }
- }
- }
- }
-
- std::string name() const { return "BM_InsertMissAndErase" + Access::name() + baseName(); }
-};
-
-struct IterateRangeFor : Base {
- using Base::Base;
-
- void run(benchmark::State& State) const {
- auto Data = makeTestingSets(TableSize, NumTables, HitType::Miss, AccessPattern::Ordered);
-
- while (State.KeepRunningBatch(TableSize * NumTables)) {
- for (auto& Set : Data.Sets) {
- for (auto& V : Set) {
- benchmark::DoNotOptimize(const_cast<std::set<uint64_t>::reference>(V));
- }
- }
- }
- }
-
- std::string name() const { return "BM_IterateRangeFor" + baseName(); }
-};
-
-struct IterateBeginEnd : Base {
- using Base::Base;
-
- void run(benchmark::State& State) const {
- auto Data = makeTestingSets(TableSize, NumTables, HitType::Miss, AccessPattern::Ordered);
-
- while (State.KeepRunningBatch(TableSize * NumTables)) {
- for (auto& Set : Data.Sets) {
- for (auto it = Set.begin(); it != Set.end(); ++it) {
- benchmark::DoNotOptimize(const_cast<std::set<uint64_t>::reference>(*it));
- }
- }
- }
- }
-
- std::string name() const { return "BM_IterateBeginEnd" + baseName(); }
-};
-
-} // namespace
-
-int main(int argc, char** argv) {
- benchmark::Initialize(&argc, argv);
- if (benchmark::ReportUnrecognizedArguments(argc, argv))
- return 1;
-
- const std::vector<size_t> TableSize{1, 10, 100, 1000, 10000, 100000, 1000000};
- const std::vector<size_t> NumTables{1, 10, 100, 1000, 10000, 100000, 1000000};
-
- makeCartesianProductBenchmark<Create, AllAccessPattern>(TableSize, NumTables);
- makeCartesianProductBenchmark<Find, AllHitTypes, AllAccessPattern>(TableSize, NumTables);
- makeCartesianProductBenchmark<FindNeEnd, AllHitTypes, AllAccessPattern>(TableSize, NumTables);
- makeCartesianProductBenchmark<InsertHit, AllAccessPattern>(TableSize, NumTables);
- makeCartesianProductBenchmark<InsertMissAndErase, AllAccessPattern>(TableSize, NumTables);
- makeCartesianProductBenchmark<IterateRangeFor>(TableSize, NumTables);
- makeCartesianProductBenchmark<IterateBeginEnd>(TableSize, NumTables);
- benchmark::RunSpecifiedBenchmarks();
-}
diff --git a/libcxx/test/benchmarks/containers/unordered_set.bench.cpp b/libcxx/test/benchmarks/containers/unordered_set.bench.cpp
deleted file mode 100644
index ad8d0feaa043659..000000000000000
--- a/libcxx/test/benchmarks/containers/unordered_set.bench.cpp
+++ /dev/null
@@ -1,245 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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 <cstdint>
-#include <cstdlib>
-#include <cstring>
-#include <functional>
-#include <unordered_set>
-#include <vector>
-
-#include "benchmark/benchmark.h"
-
-#include "container_benchmarks.h"
-#include "../GenerateInput.h"
-#include "test_macros.h"
-
-using namespace ContainerBenchmarks;
-
-constexpr std::size_t TestNumInputs = 1024;
-
-// The purpose of this hash function is to NOT be implemented as the identity function,
-// which is how std::hash is implemented for smaller integral types.
-struct NonIdentityScalarHash : std::hash<unsigned long long> {};
-
-// The sole purpose of this comparator is to be used in BM_Rehash, where
-// we need something slow enough to be easily noticable in benchmark results.
-// The default implementation of operator== for strings seems to be a little
-// too fast for that specific benchmark to reliably show a noticeable
-// improvement, but unoptimized bytewise comparison fits just right.
-// Early return is there just for convenience, since we only compare strings
-// of equal length in BM_Rehash.
-struct SlowStringEq {
- SlowStringEq() = default;
- inline TEST_ALWAYS_INLINE bool operator()(const std::string& lhs, const std::string& rhs) const {
- if (lhs.size() != rhs.size())
- return false;
-
- bool eq = true;
- for (size_t i = 0; i < lhs.size(); ++i) {
- eq &= lhs[i] == rhs[i];
- }
- return eq;
- }
-};
-
-//----------------------------------------------------------------------------//
-// BM_InsertValue
-// ---------------------------------------------------------------------------//
-
-// Sorted Ascending //
-BENCHMARK_CAPTURE(
- BM_InsertValue, unordered_set_uint32, std::unordered_set<uint32_t>{}, getRandomIntegerInputs<uint32_t>)
- ->Arg(TestNumInputs);
-
-BENCHMARK_CAPTURE(
- BM_InsertValue, unordered_set_uint32_sorted, std::unordered_set<uint32_t>{}, getSortedIntegerInputs<uint32_t>)
- ->Arg(TestNumInputs);
-
-// Top Bytes //
-BENCHMARK_CAPTURE(BM_InsertValue,
- unordered_set_top_bits_uint32,
- std::unordered_set<uint32_t>{},
- getSortedTopBitsIntegerInputs<uint32_t>)
- ->Arg(TestNumInputs);
-
-BENCHMARK_CAPTURE(BM_InsertValueRehash,
- unordered_set_top_bits_uint32,
- std::unordered_set<uint32_t, NonIdentityScalarHash>{},
- getSortedTopBitsIntegerInputs<uint32_t>)
- ->Arg(TestNumInputs);
-
-// String //
-BENCHMARK_CAPTURE(BM_InsertValue, unordered_set_string, std::unordered_set<std::string>{}, getRandomStringInputs)
- ->Arg(TestNumInputs);
-
-BENCHMARK_CAPTURE(BM_InsertValueRehash, unordered_set_string, std::unordered_set<std::string>{}, getRandomStringInputs)
- ->Arg(TestNumInputs);
-
-// Prefixed String //
-BENCHMARK_CAPTURE(
- BM_InsertValue, unordered_set_prefixed_string, std::unordered_set<std::string>{}, getPrefixedRandomStringInputs)
- ->Arg(TestNumInputs);
-
-BENCHMARK_CAPTURE(BM_InsertValueRehash,
- unordered_set_prefixed_string,
- std::unordered_set<std::string>{},
- getPrefixedRandomStringInputs)
- ->Arg(TestNumInputs);
-
-//----------------------------------------------------------------------------//
-// BM_Find
-// ---------------------------------------------------------------------------//
-
-// Random //
-BENCHMARK_CAPTURE(
- BM_Find, unordered_set_random_uint64, std::unordered_set<uint64_t>{}, getRandomIntegerInputs<uint64_t>)
- ->Arg(TestNumInputs);
-
-BENCHMARK_CAPTURE(BM_FindRehash,
- unordered_set_random_uint64,
- std::unordered_set<uint64_t, NonIdentityScalarHash>{},
- getRandomIntegerInputs<uint64_t>)
- ->Arg(TestNumInputs);
-
-// Sorted //
-BENCHMARK_CAPTURE(
- BM_Find, unordered_set_sorted_uint64, std::unordered_set<uint64_t>{}, getSortedIntegerInputs<uint64_t>)
- ->Arg(TestNumInputs);
-
-BENCHMARK_CAPTURE(BM_FindRehash,
- unordered_set_sorted_uint64,
- std::unordered_set<uint64_t, NonIdentityScalarHash>{},
- getSortedIntegerInputs<uint64_t>)
- ->Arg(TestNumInputs);
-
-// Sorted //
-#ifndef TEST_HAS_NO_INT128
-BENCHMARK_CAPTURE(BM_Find,
- unordered_set_sorted_uint128,
- std::unordered_set<__uint128_t>{},
- getSortedTopBitsIntegerInputs<__uint128_t>)
- ->Arg(TestNumInputs);
-
-BENCHMARK_CAPTURE(BM_FindRehash,
- unordered_set_sorted_uint128,
- std::unordered_set<__uint128_t>{},
- getSortedTopBitsIntegerInputs<__uint128_t>)
- ->Arg(TestNumInputs);
-#endif
-
-// Sorted //
-BENCHMARK_CAPTURE(
- BM_Find, unordered_set_sorted_uint32, std::unordered_set<uint32_t>{}, getSortedIntegerInputs<uint32_t>)
- ->Arg(TestNumInputs);
-
-BENCHMARK_CAPTURE(BM_FindRehash,
- unordered_set_sorted_uint32,
- std::unordered_set<uint32_t, NonIdentityScalarHash>{},
- getSortedIntegerInputs<uint32_t>)
- ->Arg(TestNumInputs);
-
-// Sorted Ascending //
-BENCHMARK_CAPTURE(
- BM_Find, unordered_set_sorted_large_uint64, std::unordered_set<uint64_t>{}, getSortedLargeIntegerInputs<uint64_t>)
- ->Arg(TestNumInputs);
-
-BENCHMARK_CAPTURE(BM_FindRehash,
- unordered_set_sorted_large_uint64,
- std::unordered_set<uint64_t, NonIdentityScalarHash>{},
- getSortedLargeIntegerInputs<uint64_t>)
- ->Arg(TestNumInputs);
-
-// Top Bits //
-BENCHMARK_CAPTURE(
- BM_Find, unordered_set_top_bits_uint64, std::unordered_set<uint64_t>{}, getSortedTopBitsIntegerInputs<uint64_t>)
- ->Arg(TestNumInputs);
-
-BENCHMARK_CAPTURE(BM_FindRehash,
- unordered_set_top_bits_uint64,
- std::unordered_set<uint64_t, NonIdentityScalarHash>{},
- getSortedTopBitsIntegerInputs<uint64_t>)
- ->Arg(TestNumInputs);
-
-// String //
-BENCHMARK_CAPTURE(BM_Find, unordered_set_string, std::unordered_set<std::string>{}, getRandomStringInputs)
- ->Arg(TestNumInputs);
-
-BENCHMARK_CAPTURE(BM_FindRehash, unordered_set_string, std::unordered_set<std::string>{}, getRandomStringInputs)
- ->Arg(TestNumInputs);
-
-// Prefixed String //
-BENCHMARK_CAPTURE(
- BM_Find, unordered_set_prefixed_string, std::unordered_set<std::string>{}, getPrefixedRandomStringInputs)
- ->Arg(TestNumInputs);
-
-BENCHMARK_CAPTURE(
- BM_FindRehash, unordered_set_prefixed_string, std::unordered_set<std::string>{}, getPrefixedRandomStringInputs)
- ->Arg(TestNumInputs);
-
-//----------------------------------------------------------------------------//
-// BM_Rehash
-// ---------------------------------------------------------------------------//
-
-BENCHMARK_CAPTURE(BM_Rehash,
- unordered_set_string_arg,
- std::unordered_set<std::string, std::hash<std::string>, SlowStringEq>{},
- getRandomStringInputs)
- ->Arg(TestNumInputs);
-
-BENCHMARK_CAPTURE(BM_Rehash, unordered_set_int_arg, std::unordered_set<int>{}, getRandomIntegerInputs<int>)
- ->Arg(TestNumInputs);
-
-//----------------------------------------------------------------------------//
-// BM_Compare
-// ---------------------------------------------------------------------------//
-
-BENCHMARK_CAPTURE(
- BM_Compare_same_container, unordered_set_string, std::unordered_set<std::string>{}, getRandomStringInputs)
- ->Arg(TestNumInputs);
-
-BENCHMARK_CAPTURE(BM_Compare_same_container, unordered_set_int, std::unordered_set<int>{}, getRandomIntegerInputs<int>)
- ->Arg(TestNumInputs);
-
-BENCHMARK_CAPTURE(
- BM_Compare_
diff erent_containers, unordered_set_string, std::unordered_set<std::string>{}, getRandomStringInputs)
- ->Arg(TestNumInputs);
-
-BENCHMARK_CAPTURE(
- BM_Compare_
diff erent_containers, unordered_set_int, std::unordered_set<int>{}, getRandomIntegerInputs<int>)
- ->Arg(TestNumInputs);
-
-///////////////////////////////////////////////////////////////////////////////
-BENCHMARK_CAPTURE(BM_InsertDuplicate, unordered_set_int, std::unordered_set<int>{}, getRandomIntegerInputs<int>)
- ->Arg(TestNumInputs);
-BENCHMARK_CAPTURE(BM_InsertDuplicate, unordered_set_string, std::unordered_set<std::string>{}, getRandomStringInputs)
- ->Arg(TestNumInputs);
-
-BENCHMARK_CAPTURE(BM_EmplaceDuplicate, unordered_set_int, std::unordered_set<int>{}, getRandomIntegerInputs<int>)
- ->Arg(TestNumInputs);
-BENCHMARK_CAPTURE(BM_EmplaceDuplicate, unordered_set_string, std::unordered_set<std::string>{}, getRandomStringInputs)
- ->Arg(TestNumInputs);
-
-BENCHMARK_CAPTURE(
- BM_InsertDuplicate, unordered_set_int_insert_arg, std::unordered_set<int>{}, getRandomIntegerInputs<int>)
- ->Arg(TestNumInputs);
-BENCHMARK_CAPTURE(
- BM_InsertDuplicate, unordered_set_string_insert_arg, std::unordered_set<std::string>{}, getRandomStringInputs)
- ->Arg(TestNumInputs);
-
-BENCHMARK_CAPTURE(
- BM_EmplaceDuplicate, unordered_set_int_insert_arg, std::unordered_set<int>{}, getRandomIntegerInputs<unsigned>)
- ->Arg(TestNumInputs);
-
-BENCHMARK_CAPTURE(
- BM_EmplaceDuplicate, unordered_set_string_arg, std::unordered_set<std::string>{}, getRandomCStringInputs)
- ->Arg(TestNumInputs);
-
-BENCHMARK_MAIN();
More information about the libcxx-commits
mailing list