[libcxx-commits] [libcxx] [libcxx] Avoid hash key in __hash_table::find() if it is empty. (PR #126837)
via libcxx-commits
libcxx-commits at lists.llvm.org
Thu May 8 00:29:49 PDT 2025
https://github.com/xbcnn updated https://github.com/llvm/llvm-project/pull/126837
>From 0f93eba81b388211c80da1eeb2f9ad35a9f75947 Mon Sep 17 00:00:00 2001
From: yangxiaobing <yangxiaobing at jwzg.com>
Date: Tue, 11 Feb 2025 18:16:09 +0800
Subject: [PATCH 1/6] [libcxx] Avoid hash key in __hash_table::find() if no
buckets yet.
If the hash table has no buckets yet, it's empty and the find will do fast
return end(). Then compute hash key is useless and can be avoided, since
it could be expensive for some key types, such as long string.
This is a small optimization but useful in cases like a checklist (
implemented as unordered_set) that is mostly empty.
---
libcxx/include/__hash_table | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/libcxx/include/__hash_table b/libcxx/include/__hash_table
index d7b312f8774fc..a1d06d07f7c8d 100644
--- a/libcxx/include/__hash_table
+++ b/libcxx/include/__hash_table
@@ -1771,9 +1771,9 @@ template <class _Tp, class _Hash, class _Equal, class _Alloc>
template <class _Key>
typename __hash_table<_Tp, _Hash, _Equal, _Alloc>::iterator
__hash_table<_Tp, _Hash, _Equal, _Alloc>::find(const _Key& __k) {
- size_t __hash = hash_function()(__k);
size_type __bc = bucket_count();
if (__bc != 0) {
+ size_t __hash = hash_function()(__k);
size_t __chash = std::__constrain_hash(__hash, __bc);
__next_pointer __nd = __bucket_list_[__chash];
if (__nd != nullptr) {
@@ -1792,9 +1792,9 @@ template <class _Tp, class _Hash, class _Equal, class _Alloc>
template <class _Key>
typename __hash_table<_Tp, _Hash, _Equal, _Alloc>::const_iterator
__hash_table<_Tp, _Hash, _Equal, _Alloc>::find(const _Key& __k) const {
- size_t __hash = hash_function()(__k);
size_type __bc = bucket_count();
if (__bc != 0) {
+ size_t __hash = hash_function()(__k);
size_t __chash = std::__constrain_hash(__hash, __bc);
__next_pointer __nd = __bucket_list_[__chash];
if (__nd != nullptr) {
>From a2f7b3c549b63ce9669c025c863980da27481b50 Mon Sep 17 00:00:00 2001
From: yangxiaobing <yangxiaobing at jwzg.com>
Date: Thu, 13 Feb 2025 16:33:13 +0800
Subject: [PATCH 2/6] Add benchmarks.
---
.../associative/hash_table_find.bench.cpp | 70 +++++++++++++++++++
1 file changed, 70 insertions(+)
create mode 100644 libcxx/test/benchmarks/containers/associative/hash_table_find.bench.cpp
diff --git a/libcxx/test/benchmarks/containers/associative/hash_table_find.bench.cpp b/libcxx/test/benchmarks/containers/associative/hash_table_find.bench.cpp
new file mode 100644
index 0000000000000..e6f0ae79caef3
--- /dev/null
+++ b/libcxx/test/benchmarks/containers/associative/hash_table_find.bench.cpp
@@ -0,0 +1,70 @@
+#include <unordered_set>
+#include <string>
+#include <random>
+#include <vector>
+
+#include "../../GenerateInput.h"
+#include "benchmark/benchmark.h"
+
+// Generate random strings of at least 32 chars
+struct LongStringGenerator {
+ static std::vector<std::string> cached_strings;
+
+ static void ensure_strings(size_t count) {
+ cached_strings.clear();
+
+ std::mt19937_64 gen(42); // Fixed seed for reproducibility
+ std::uniform_int_distribution<size_t> len_dist(32, 128);
+
+ cached_strings.reserve(count);
+ for (size_t i = 0; i < count; i++) {
+ std::string str(len_dist(gen), 0);
+ for (char& c : str) {
+ c = 'a' + (gen() % 26);
+ }
+ cached_strings.push_back(std::move(str));
+ }
+ }
+
+ const std::string& generate(size_t i) { return cached_strings[i]; }
+};
+
+std::vector<std::string> LongStringGenerator::cached_strings;
+[[maybe_unused]] auto dummy = [] { // Pre-generate 32K strings
+ LongStringGenerator::ensure_strings(1 << 15);
+ return 0;
+}();
+
+template <class Gen>
+static void BM_UnorderedSet_Find_EmptySet(benchmark::State& state, Gen g) {
+ const size_t lookup_count = state.range(0);
+ std::unordered_set<std::string> s; // Empty set
+
+ for (auto _ : state) {
+ for (size_t i = 0; i < lookup_count; i++) {
+ benchmark::DoNotOptimize(s.find(g.generate(i)));
+ }
+ }
+}
+
+template <class Gen>
+static void BM_UnorderedSet_Find(benchmark::State& state, Gen g) {
+ const size_t lookup_count = state.range(0);
+ std::unordered_set<std::string> s{"hello"}; // Non-empty set
+
+ for (auto _ : state) {
+ for (size_t i = 0; i < lookup_count; i++) {
+ benchmark::DoNotOptimize(s.find(g.generate(i)));
+ }
+ }
+}
+
+BENCHMARK_CAPTURE(BM_UnorderedSet_Find_EmptySet, long_string, LongStringGenerator())
+ ->RangeMultiplier(2)
+ ->Range(1 << 10, 1 << 15); // Test from 1K to 32K lookups
+
+BENCHMARK_CAPTURE(BM_UnorderedSet_Find, long_string, LongStringGenerator())
+ ->RangeMultiplier(2)
+ ->Range(1 << 10, 1 << 15); // Test from 1K to 32K lookups
+
+BENCHMARK_MAIN();
>From 96ef4a128145d14e093f878258e5f49f4f7b573d Mon Sep 17 00:00:00 2001
From: yangxiaobing <yangxiaobing at jwzg.com>
Date: Fri, 14 Feb 2025 08:52:59 +0800
Subject: [PATCH 3/6] Fix clang-format issue.
---
.../benchmarks/containers/associative/hash_table_find.bench.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libcxx/test/benchmarks/containers/associative/hash_table_find.bench.cpp b/libcxx/test/benchmarks/containers/associative/hash_table_find.bench.cpp
index e6f0ae79caef3..93fddac02ee6f 100644
--- a/libcxx/test/benchmarks/containers/associative/hash_table_find.bench.cpp
+++ b/libcxx/test/benchmarks/containers/associative/hash_table_find.bench.cpp
@@ -50,7 +50,7 @@ static void BM_UnorderedSet_Find_EmptySet(benchmark::State& state, Gen g) {
template <class Gen>
static void BM_UnorderedSet_Find(benchmark::State& state, Gen g) {
const size_t lookup_count = state.range(0);
- std::unordered_set<std::string> s{"hello"}; // Non-empty set
+ std::unordered_set<std::string> s{"hello"}; // Non-empty set
for (auto _ : state) {
for (size_t i = 0; i < lookup_count; i++) {
>From bc3ce69cfa55766f24c7751e33c57874eaf76395 Mon Sep 17 00:00:00 2001
From: yangxiaobing <yangxiaobing at jwzg.com>
Date: Fri, 14 Feb 2025 14:29:16 +0800
Subject: [PATCH 4/6] Add license header and UNSUPPORTED
---
.../containers/associative/hash_table_find.bench.cpp | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/libcxx/test/benchmarks/containers/associative/hash_table_find.bench.cpp b/libcxx/test/benchmarks/containers/associative/hash_table_find.bench.cpp
index 93fddac02ee6f..874d4a44186e7 100644
--- a/libcxx/test/benchmarks/containers/associative/hash_table_find.bench.cpp
+++ b/libcxx/test/benchmarks/containers/associative/hash_table_find.bench.cpp
@@ -1,3 +1,13 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
#include <unordered_set>
#include <string>
#include <random>
>From 0102da7acc9ec4a8c17825b82b8507cd6a5cbf73 Mon Sep 17 00:00:00 2001
From: yangxiaobing <yangxiaobing at jwzg.com>
Date: Mon, 17 Feb 2025 10:34:36 +0800
Subject: [PATCH 5/6] Add size check to __hash_table::find() and update
benchmark cases
---
libcxx/include/__hash_table | 4 +--
.../associative/hash_table_find.bench.cpp | 33 ++++++++++++++-----
2 files changed, 27 insertions(+), 10 deletions(-)
diff --git a/libcxx/include/__hash_table b/libcxx/include/__hash_table
index a1d06d07f7c8d..60f292c86719a 100644
--- a/libcxx/include/__hash_table
+++ b/libcxx/include/__hash_table
@@ -1772,7 +1772,7 @@ template <class _Key>
typename __hash_table<_Tp, _Hash, _Equal, _Alloc>::iterator
__hash_table<_Tp, _Hash, _Equal, _Alloc>::find(const _Key& __k) {
size_type __bc = bucket_count();
- if (__bc != 0) {
+ if (__bc != 0 && size() != 0) {
size_t __hash = hash_function()(__k);
size_t __chash = std::__constrain_hash(__hash, __bc);
__next_pointer __nd = __bucket_list_[__chash];
@@ -1793,7 +1793,7 @@ template <class _Key>
typename __hash_table<_Tp, _Hash, _Equal, _Alloc>::const_iterator
__hash_table<_Tp, _Hash, _Equal, _Alloc>::find(const _Key& __k) const {
size_type __bc = bucket_count();
- if (__bc != 0) {
+ if (__bc != 0 && size() != 0) {
size_t __hash = hash_function()(__k);
size_t __chash = std::__constrain_hash(__hash, __bc);
__next_pointer __nd = __bucket_list_[__chash];
diff --git a/libcxx/test/benchmarks/containers/associative/hash_table_find.bench.cpp b/libcxx/test/benchmarks/containers/associative/hash_table_find.bench.cpp
index 874d4a44186e7..5eea4f8a7219f 100644
--- a/libcxx/test/benchmarks/containers/associative/hash_table_find.bench.cpp
+++ b/libcxx/test/benchmarks/containers/associative/hash_table_find.bench.cpp
@@ -6,7 +6,7 @@
//
//===----------------------------------------------------------------------===//
-// UNSUPPORTED: c++03
+// UNSUPPORTED: c++03, c++11, c++14, c++17
#include <unordered_set>
#include <string>
@@ -46,9 +46,9 @@ std::vector<std::string> LongStringGenerator::cached_strings;
}();
template <class Gen>
-static void BM_UnorderedSet_Find_EmptySet(benchmark::State& state, Gen g) {
+static void BM_UnorderedSet_Find_EmptyNoBuckets(benchmark::State& state, Gen g) {
const size_t lookup_count = state.range(0);
- std::unordered_set<std::string> s; // Empty set
+ std::unordered_set<std::string> s; // Empty and no buckets
for (auto _ : state) {
for (size_t i = 0; i < lookup_count; i++) {
@@ -58,9 +58,10 @@ static void BM_UnorderedSet_Find_EmptySet(benchmark::State& state, Gen g) {
}
template <class Gen>
-static void BM_UnorderedSet_Find(benchmark::State& state, Gen g) {
+static void BM_UnorderedSet_Find_EmptyWithBuckets(benchmark::State& state, Gen g) {
const size_t lookup_count = state.range(0);
- std::unordered_set<std::string> s{"hello"}; // Non-empty set
+ std::unordered_set<std::string> s;
+ s.reserve(1); // Still empty but reserved buckets
for (auto _ : state) {
for (size_t i = 0; i < lookup_count; i++) {
@@ -69,12 +70,28 @@ static void BM_UnorderedSet_Find(benchmark::State& state, Gen g) {
}
}
-BENCHMARK_CAPTURE(BM_UnorderedSet_Find_EmptySet, long_string, LongStringGenerator())
+template <class Gen>
+static void BM_UnorderedSet_Find_NonEmpty(benchmark::State& state, Gen g) {
+ const size_t lookup_count = state.range(0);
+ std::unordered_set<std::string> s{"hello"};
+
+ for (auto _ : state) {
+ for (size_t i = 0; i < lookup_count; i++) {
+ benchmark::DoNotOptimize(s.find(g.generate(i)));
+ }
+ }
+}
+
+BENCHMARK_CAPTURE(BM_UnorderedSet_Find_EmptyNoBuckets, long_string, LongStringGenerator())
->RangeMultiplier(2)
->Range(1 << 10, 1 << 15); // Test from 1K to 32K lookups
-BENCHMARK_CAPTURE(BM_UnorderedSet_Find, long_string, LongStringGenerator())
+BENCHMARK_CAPTURE(BM_UnorderedSet_Find_EmptyWithBuckets, long_string, LongStringGenerator())
->RangeMultiplier(2)
- ->Range(1 << 10, 1 << 15); // Test from 1K to 32K lookups
+ ->Range(1 << 10, 1 << 15);
+
+BENCHMARK_CAPTURE(BM_UnorderedSet_Find_NonEmpty, long_string, LongStringGenerator())
+ ->RangeMultiplier(2)
+ ->Range(1 << 10, 1 << 15);
BENCHMARK_MAIN();
>From 1de7487453450c6f5c559db9af0b80e40cb28d15 Mon Sep 17 00:00:00 2001
From: yangxiaobing <yangxiaobing at jwzg.com>
Date: Thu, 8 May 2025 14:37:17 +0800
Subject: [PATCH 6/6] Move benchmarks for querying on empty containers to the
generic tests
For expensive key, such as std::unordered_set<std::string>, the benchmark reulst:
1. With the opt:
-------------------------------------------------------------------------------------------------------------------------------------
Benchmark Time CPU Iterations
-------------------------------------------------------------------------------------------------------------------------------------
std::unordered_set<int>::ctor(const&)/32 4717 ns 4718 ns 143456
std::unordered_set<int>::ctor(const&)/1024 160766 ns 160764 ns 4384
std::unordered_set<int>::ctor(const&)/8192 1362608 ns 1362523 ns 544
std::unordered_set<int>::ctor(iterator, iterator) (unsorted sequence)/32 6493 ns 6493 ns 104416
std::unordered_set<int>::ctor(iterator, iterator) (unsorted sequence)/1024 204541 ns 204539 ns 3456
std::unordered_set<int>::ctor(iterator, iterator) (unsorted sequence)/8192 1712671 ns 1712620 ns 416
std::unordered_set<int>::ctor(iterator, iterator) (sorted sequence)/32 6533 ns 6534 ns 107136
std::unordered_set<int>::ctor(iterator, iterator) (sorted sequence)/1024 202608 ns 202603 ns 3456
std::unordered_set<int>::ctor(iterator, iterator) (sorted sequence)/8192 1707584 ns 1707476 ns 416
std::unordered_set<int>::operator=(const&)/32 4814 ns 4814 ns 147424
std::unordered_set<int>::operator=(const&)/1024 154541 ns 154535 ns 4416
std::unordered_set<int>::operator=(const&)/8192 1310847 ns 1310769 ns 416
std::unordered_set<int>::insert(value) (already present)/32 57.5 ns 57.5 ns 12193408
std::unordered_set<int>::insert(value) (already present)/1024 60.4 ns 60.4 ns 11587808
std::unordered_set<int>::insert(value) (already present)/8192 60.7 ns 60.7 ns 9435360
std::unordered_set<int>::insert(value) (new value)/32 153 ns 154 ns 4433696
std::unordered_set<int>::insert(value) (new value)/1024 164 ns 164 ns 3980992
std::unordered_set<int>::insert(value) (new value)/8192 165 ns 165 ns 4045664
std::unordered_set<int>::insert(iterator, iterator) (all new keys)/32 6416 ns 6419 ns 102879
std::unordered_set<int>::insert(iterator, iterator) (all new keys)/1024 205733 ns 205748 ns 3425
std::unordered_set<int>::insert(iterator, iterator) (all new keys)/8192 1583461 ns 1583454 ns 443
std::unordered_set<int>::insert(iterator, iterator) (half new keys)/32 4363 ns 4370 ns 154592
std::unordered_set<int>::insert(iterator, iterator) (half new keys)/1024 141932 ns 141921 ns 5014
std::unordered_set<int>::insert(iterator, iterator) (half new keys)/8192 1096349 ns 1096330 ns 641
std::unordered_set<int>::erase(key) (existent)/32 149 ns 149 ns 4715776
std::unordered_set<int>::erase(key) (existent)/1024 153 ns 153 ns 4587936
std::unordered_set<int>::erase(key) (existent)/8192 157 ns 157 ns 3200000
std::unordered_set<int>::erase(key) (non-existent)/32 48.6 ns 48.6 ns 14433088
std::unordered_set<int>::erase(key) (non-existent)/1024 55.4 ns 55.4 ns 14368768
std::unordered_set<int>::erase(key) (non-existent)/8192 50.1 ns 50.0 ns 12491488
std::unordered_set<int>::erase(iterator)/32 106 ns 106 ns 6649824
std::unordered_set<int>::erase(iterator)/1024 108 ns 107 ns 6551744
std::unordered_set<int>::erase(iterator)/8192 108 ns 108 ns 6368928
std::unordered_set<int>::erase(iterator, iterator) (erase half the container)/32 1869 ns 1861 ns 373187
std::unordered_set<int>::erase(iterator, iterator) (erase half the container)/1024 50300 ns 50256 ns 13894
std::unordered_set<int>::erase(iterator, iterator) (erase half the container)/8192 410477 ns 410379 ns 1707
std::unordered_set<int>::clear()/32 1047 ns 1038 ns 672424
std::unordered_set<int>::clear()/1024 24126 ns 24090 ns 29056
std::unordered_set<int>::clear()/8192 188620 ns 188534 ns 3693
std::unordered_set<int>::find(key) (existent)/32 52.2 ns 52.2 ns 13406688
std::unordered_set<int>::find(key) (existent)/1024 48.4 ns 48.4 ns 13773728
std::unordered_set<int>::find(key) (existent)/8192 55.8 ns 55.8 ns 12571776
std::unordered_set<int>::find(key) (non-existent)/32 41.0 ns 41.0 ns 14667808
std::unordered_set<int>::find(key) (non-existent)/1024 43.5 ns 43.5 ns 16278688
std::unordered_set<int>::find(key) (non-existent)/8192 41.2 ns 41.2 ns 15790752
std::unordered_set<int>::find(key) (expensive-empty)/32 14.6 ns 14.6 ns 47838272
std::unordered_set<int>::find(key) (expensive-empty)/1024 14.6 ns 14.6 ns 47741568
std::unordered_set<int>::find(key) (expensive-empty)/8192 14.7 ns 14.7 ns 47780224
std::unordered_set<int>::count(key) (existent)/32 58.4 ns 58.4 ns 12129728
std::unordered_set<int>::count(key) (existent)/1024 57.4 ns 57.4 ns 12091968
std::unordered_set<int>::count(key) (existent)/8192 61.1 ns 61.1 ns 12053312
std::unordered_set<int>::count(key) (non-existent)/32 60.7 ns 60.7 ns 13021152
std::unordered_set<int>::count(key) (non-existent)/1024 57.1 ns 57.1 ns 12352160
std::unordered_set<int>::count(key) (non-existent)/8192 61.1 ns 61.1 ns 14251424
std::unordered_set<int>::count(key) (expensive-empty)/32 19.4 ns 19.4 ns 36120640
std::unordered_set<int>::count(key) (expensive-empty)/1024 19.4 ns 19.4 ns 36122336
std::unordered_set<int>::count(key) (expensive-empty)/8192 19.4 ns 19.4 ns 35992544
std::unordered_set<int>::contains(key) (existent)/32 57.7 ns 57.7 ns 11540576
std::unordered_set<int>::contains(key) (existent)/1024 56.1 ns 56.1 ns 11500288
std::unordered_set<int>::contains(key) (existent)/8192 60.5 ns 60.5 ns 11040832
std::unordered_set<int>::contains(key) (non-existent)/32 58.9 ns 58.9 ns 13091488
std::unordered_set<int>::contains(key) (non-existent)/1024 53.0 ns 53.0 ns 12597184
std::unordered_set<int>::contains(key) (non-existent)/8192 56.1 ns 56.1 ns 12456960
std::unordered_set<int>::contains(key) (expensive-empty)/32 21.4 ns 21.4 ns 32681408
std::unordered_set<int>::contains(key) (expensive-empty)/1024 21.4 ns 21.4 ns 32516640
std::unordered_set<int>::contains(key) (expensive-empty)/8192 21.4 ns 21.4 ns 32573888
std::unordered_set<std::string>::ctor(const&)/32 24879 ns 24880 ns 25856
std::unordered_set<std::string>::ctor(const&)/1024 884089 ns 884042 ns 832
std::unordered_set<std::string>::ctor(const&)/8192 8585689 ns 8585383 ns 96
std::unordered_set<std::string>::ctor(iterator, iterator) (unsorted sequence)/32 29851 ns 29853 ns 22944
std::unordered_set<std::string>::ctor(iterator, iterator) (unsorted sequence)/1024 1149105 ns 1149052 ns 768
std::unordered_set<std::string>::ctor(iterator, iterator) (unsorted sequence)/8192 9769845 ns 9769243 ns 64
std::unordered_set<std::string>::ctor(iterator, iterator) (sorted sequence)/32 33674 ns 33678 ns 26624
std::unordered_set<std::string>::ctor(iterator, iterator) (sorted sequence)/1024 1058258 ns 1058235 ns 544
std::unordered_set<std::string>::ctor(iterator, iterator) (sorted sequence)/8192 9624318 ns 9624047 ns 96
std::unordered_set<std::string>::operator=(const&)/32 48214 ns 48216 ns 15296
std::unordered_set<std::string>::operator=(const&)/1024 1523473 ns 1523422 ns 448
std::unordered_set<std::string>::operator=(const&)/8192 13500351 ns 13499850 ns 64
std::unordered_set<std::string>::insert(value) (already present)/32 324 ns 324 ns 2154816
std::unordered_set<std::string>::insert(value) (already present)/1024 731 ns 731 ns 964224
std::unordered_set<std::string>::insert(value) (already present)/8192 1463 ns 1463 ns 460064
std::unordered_set<std::string>::insert(value) (new value)/32 1005 ns 1004 ns 1843488
std::unordered_set<std::string>::insert(value) (new value)/1024 1299 ns 1191 ns 636960
std::unordered_set<std::string>::insert(value) (new value)/8192 1726 ns 1702 ns 320000
std::unordered_set<std::string>::insert(iterator, iterator) (all new keys)/32 44153 ns 42927 ns 17413
std::unordered_set<std::string>::insert(iterator, iterator) (all new keys)/1024 1476626 ns 1447248 ns 482
std::unordered_set<std::string>::insert(iterator, iterator) (all new keys)/8192 13019197 ns 12896650 ns 55
std::unordered_set<std::string>::insert(iterator, iterator) (half new keys)/32 40150 ns 40125 ns 16551
std::unordered_set<std::string>::insert(iterator, iterator) (half new keys)/1024 1407984 ns 1341443 ns 497
std::unordered_set<std::string>::insert(iterator, iterator) (half new keys)/8192 12215376 ns 12203965 ns 57
std::unordered_set<std::string>::erase(key) (existent)/32 2569 ns 1997 ns 349312
std::unordered_set<std::string>::erase(key) (existent)/1024 1970 ns 1927 ns 930304
std::unordered_set<std::string>::erase(key) (existent)/8192 1621 ns 1618 ns 529216
std::unordered_set<std::string>::erase(key) (non-existent)/32 1174 ns 986 ns 623360
std::unordered_set<std::string>::erase(key) (non-existent)/1024 1194 ns 1048 ns 676672
std::unordered_set<std::string>::erase(key) (non-existent)/8192 983 ns 962 ns 688000
std::unordered_set<std::string>::erase(iterator)/32 213 ns 209 ns 3374560
std::unordered_set<std::string>::erase(iterator)/1024 212 ns 207 ns 3474080
std::unordered_set<std::string>::erase(iterator)/8192 218 ns 206 ns 3805824
std::unordered_set<std::string>::erase(iterator, iterator) (erase half the container)/32 2854 ns 2804 ns 208147
std::unordered_set<std::string>::erase(iterator, iterator) (erase half the container)/1024 64984 ns 64778 ns 10493
std::unordered_set<std::string>::erase(iterator, iterator) (erase half the container)/8192 695737 ns 695149 ns 1013
std::unordered_set<std::string>::clear()/32 1388 ns 1370 ns 506834
std::unordered_set<std::string>::clear()/1024 46217 ns 46073 ns 15204
std::unordered_set<std::string>::clear()/8192 599831 ns 599498 ns 1180
std::unordered_set<std::string>::find(key) (existent)/32 753 ns 753 ns 1039520
std::unordered_set<std::string>::find(key) (existent)/1024 662 ns 662 ns 968320
std::unordered_set<std::string>::find(key) (existent)/8192 610 ns 610 ns 1020864
std::unordered_set<std::string>::find(key) (non-existent)/32 625 ns 625 ns 1117408
std::unordered_set<std::string>::find(key) (non-existent)/1024 655 ns 655 ns 1083008
std::unordered_set<std::string>::find(key) (non-existent)/8192 650 ns 650 ns 1042560
std::unordered_set<std::string>::find(key) (expensive-empty)/32 14.4 ns 14.4 ns 48641472
std::unordered_set<std::string>::find(key) (expensive-empty)/1024 14.4 ns 14.4 ns 48623392
std::unordered_set<std::string>::find(key) (expensive-empty)/8192 14.4 ns 14.4 ns 48591616
std::unordered_set<std::string>::count(key) (existent)/32 824 ns 824 ns 985760
std::unordered_set<std::string>::count(key) (existent)/1024 776 ns 776 ns 929376
std::unordered_set<std::string>::count(key) (existent)/8192 703 ns 702 ns 995936
std::unordered_set<std::string>::count(key) (non-existent)/32 725 ns 725 ns 1087584
std::unordered_set<std::string>::count(key) (non-existent)/1024 709 ns 709 ns 937664
std::unordered_set<std::string>::count(key) (non-existent)/8192 625 ns 625 ns 1144000
std::unordered_set<std::string>::count(key) (expensive-empty)/32 19.3 ns 19.3 ns 36164832
std::unordered_set<std::string>::count(key) (expensive-empty)/1024 19.3 ns 19.3 ns 36393312
std::unordered_set<std::string>::count(key) (expensive-empty)/8192 19.3 ns 19.3 ns 36143328
std::unordered_set<std::string>::contains(key) (existent)/32 709 ns 709 ns 1048064
std::unordered_set<std::string>::contains(key) (existent)/1024 748 ns 748 ns 1024768
std::unordered_set<std::string>::contains(key) (existent)/8192 831 ns 831 ns 895008
std::unordered_set<std::string>::contains(key) (non-existent)/32 714 ns 714 ns 1075264
std::unordered_set<std::string>::contains(key) (non-existent)/1024 504 ns 504 ns 1222240
std::unordered_set<std::string>::contains(key) (non-existent)/8192 717 ns 717 ns 1022432
std::unordered_set<std::string>::contains(key) (expensive-empty)/32 22.0 ns 22.0 ns 31752096
std::unordered_set<std::string>::contains(key) (expensive-empty)/1024 22.0 ns 22.0 ns 31857600
std::unordered_set<std::string>::contains(key) (expensive-empty)/8192 22.0 ns 22.0 ns 31783040
2. Without the opt:
-------------------------------------------------------------------------------------------------------------------------------------
Benchmark Time CPU Iterations
-------------------------------------------------------------------------------------------------------------------------------------
std::unordered_set<int>::ctor(const&)/32 4920 ns 4921 ns 142304
std::unordered_set<int>::ctor(const&)/1024 161004 ns 161004 ns 4384
std::unordered_set<int>::ctor(const&)/8192 1369984 ns 1369913 ns 512
std::unordered_set<int>::ctor(iterator, iterator) (unsorted sequence)/32 6583 ns 6584 ns 105536
std::unordered_set<int>::ctor(iterator, iterator) (unsorted sequence)/1024 203335 ns 203328 ns 3488
std::unordered_set<int>::ctor(iterator, iterator) (unsorted sequence)/8192 1712738 ns 1712659 ns 416
std::unordered_set<int>::ctor(iterator, iterator) (sorted sequence)/32 6417 ns 6418 ns 105920
std::unordered_set<int>::ctor(iterator, iterator) (sorted sequence)/1024 204252 ns 204248 ns 3456
std::unordered_set<int>::ctor(iterator, iterator) (sorted sequence)/8192 1718282 ns 1718226 ns 416
std::unordered_set<int>::operator=(const&)/32 4777 ns 4778 ns 143776
std::unordered_set<int>::operator=(const&)/1024 155375 ns 155370 ns 4416
std::unordered_set<int>::operator=(const&)/8192 1318345 ns 1318318 ns 416
std::unordered_set<int>::insert(value) (already present)/32 57.1 ns 57.1 ns 12182624
std::unordered_set<int>::insert(value) (already present)/1024 60.2 ns 60.2 ns 11640704
std::unordered_set<int>::insert(value) (already present)/8192 59.3 ns 59.3 ns 11515456
std::unordered_set<int>::insert(value) (new value)/32 152 ns 152 ns 4575936
std::unordered_set<int>::insert(value) (new value)/1024 161 ns 161 ns 4283488
std::unordered_set<int>::insert(value) (new value)/8192 172 ns 172 ns 4288736
std::unordered_set<int>::insert(iterator, iterator) (all new keys)/32 6430 ns 6434 ns 107732
std::unordered_set<int>::insert(iterator, iterator) (all new keys)/1024 206167 ns 206184 ns 3429
std::unordered_set<int>::insert(iterator, iterator) (all new keys)/8192 1587621 ns 1587634 ns 417
std::unordered_set<int>::insert(iterator, iterator) (half new keys)/32 4588 ns 4587 ns 150781
std::unordered_set<int>::insert(iterator, iterator) (half new keys)/1024 132835 ns 132832 ns 4951
std::unordered_set<int>::insert(iterator, iterator) (half new keys)/8192 1095236 ns 1095210 ns 641
std::unordered_set<int>::erase(key) (existent)/32 154 ns 154 ns 4492416
std::unordered_set<int>::erase(key) (existent)/1024 160 ns 160 ns 4383520
std::unordered_set<int>::erase(key) (existent)/8192 155 ns 155 ns 4363744
std::unordered_set<int>::erase(key) (non-existent)/32 57.8 ns 57.8 ns 14141664
std::unordered_set<int>::erase(key) (non-existent)/1024 57.4 ns 57.4 ns 14369216
std::unordered_set<int>::erase(key) (non-existent)/8192 56.4 ns 56.4 ns 13152576
std::unordered_set<int>::erase(iterator)/32 108 ns 108 ns 6519872
std::unordered_set<int>::erase(iterator)/1024 110 ns 110 ns 6271040
std::unordered_set<int>::erase(iterator)/8192 112 ns 112 ns 6295872
std::unordered_set<int>::erase(iterator, iterator) (erase half the container)/32 1815 ns 1808 ns 363644
std::unordered_set<int>::erase(iterator, iterator) (erase half the container)/1024 51034 ns 51007 ns 13752
std::unordered_set<int>::erase(iterator, iterator) (erase half the container)/8192 414144 ns 414067 ns 1692
std::unordered_set<int>::clear()/32 1053 ns 1049 ns 660370
std::unordered_set<int>::clear()/1024 24122 ns 24110 ns 29062
std::unordered_set<int>::clear()/8192 189528 ns 189457 ns 3704
std::unordered_set<int>::find(key) (existent)/32 52.0 ns 52.0 ns 12869664
std::unordered_set<int>::find(key) (existent)/1024 51.0 ns 51.0 ns 13863392
std::unordered_set<int>::find(key) (existent)/8192 52.8 ns 52.8 ns 13966656
std::unordered_set<int>::find(key) (non-existent)/32 43.0 ns 43.0 ns 13238752
std::unordered_set<int>::find(key) (non-existent)/1024 46.9 ns 46.9 ns 17560800
std::unordered_set<int>::find(key) (non-existent)/8192 40.5 ns 40.5 ns 17009920
std::unordered_set<int>::find(key) (expensive-empty)/32 16.4 ns 16.4 ns 42983616
std::unordered_set<int>::find(key) (expensive-empty)/1024 16.5 ns 16.5 ns 42652352
std::unordered_set<int>::find(key) (expensive-empty)/8192 16.3 ns 16.3 ns 42610592
std::unordered_set<int>::count(key) (existent)/32 58.6 ns 58.6 ns 11978688
std::unordered_set<int>::count(key) (existent)/1024 56.3 ns 56.3 ns 12275488
std::unordered_set<int>::count(key) (existent)/8192 61.3 ns 61.3 ns 11718880
std::unordered_set<int>::count(key) (non-existent)/32 51.6 ns 51.6 ns 13867456
std::unordered_set<int>::count(key) (non-existent)/1024 59.7 ns 59.7 ns 12946400
std::unordered_set<int>::count(key) (non-existent)/8192 57.9 ns 57.9 ns 12481344
std::unordered_set<int>::count(key) (expensive-empty)/32 21.5 ns 21.5 ns 32519840
std::unordered_set<int>::count(key) (expensive-empty)/1024 21.5 ns 21.5 ns 32430592
std::unordered_set<int>::count(key) (expensive-empty)/8192 21.5 ns 21.5 ns 32623872
std::unordered_set<int>::contains(key) (existent)/32 58.3 ns 58.3 ns 11878944
std::unordered_set<int>::contains(key) (existent)/1024 61.1 ns 61.1 ns 11275648
std::unordered_set<int>::contains(key) (existent)/8192 59.8 ns 59.8 ns 11298112
std::unordered_set<int>::contains(key) (non-existent)/32 52.4 ns 52.4 ns 11874720
std::unordered_set<int>::contains(key) (non-existent)/1024 56.1 ns 56.1 ns 11843904
std::unordered_set<int>::contains(key) (non-existent)/8192 57.3 ns 57.3 ns 10858048
std::unordered_set<int>::contains(key) (expensive-empty)/32 23.1 ns 23.1 ns 30402240
std::unordered_set<int>::contains(key) (expensive-empty)/1024 23.1 ns 23.1 ns 30244128
std::unordered_set<int>::contains(key) (expensive-empty)/8192 23.1 ns 23.1 ns 30230528
std::unordered_set<std::string>::ctor(const&)/32 26497 ns 26497 ns 25280
std::unordered_set<std::string>::ctor(const&)/1024 825878 ns 825839 ns 832
std::unordered_set<std::string>::ctor(const&)/8192 8670858 ns 8670549 ns 96
std::unordered_set<std::string>::ctor(iterator, iterator) (unsorted sequence)/32 27310 ns 27311 ns 26464
std::unordered_set<std::string>::ctor(iterator, iterator) (unsorted sequence)/1024 946828 ns 946803 ns 768
std::unordered_set<std::string>::ctor(iterator, iterator) (unsorted sequence)/8192 9053635 ns 9053223 ns 96
std::unordered_set<std::string>::ctor(iterator, iterator) (sorted sequence)/32 27548 ns 27548 ns 23648
std::unordered_set<std::string>::ctor(iterator, iterator) (sorted sequence)/1024 898692 ns 898659 ns 800
std::unordered_set<std::string>::ctor(iterator, iterator) (sorted sequence)/8192 9010872 ns 9010567 ns 96
std::unordered_set<std::string>::operator=(const&)/32 47648 ns 47650 ns 14080
std::unordered_set<std::string>::operator=(const&)/1024 1475556 ns 1475497 ns 448
std::unordered_set<std::string>::operator=(const&)/8192 13089133 ns 13088427 ns 64
std::unordered_set<std::string>::insert(value) (already present)/32 946 ns 946 ns 796928
std::unordered_set<std::string>::insert(value) (already present)/1024 729 ns 729 ns 2565824
std::unordered_set<std::string>::insert(value) (already present)/8192 805 ns 805 ns 2718624
std::unordered_set<std::string>::insert(value) (new value)/32 1161 ns 1161 ns 1403360
std::unordered_set<std::string>::insert(value) (new value)/1024 1246 ns 1246 ns 1978080
std::unordered_set<std::string>::insert(value) (new value)/8192 1110 ns 1110 ns 985376
std::unordered_set<std::string>::insert(iterator, iterator) (all new keys)/32 30256 ns 30276 ns 25741
std::unordered_set<std::string>::insert(iterator, iterator) (all new keys)/1024 908942 ns 909062 ns 745
std::unordered_set<std::string>::insert(iterator, iterator) (all new keys)/8192 7286785 ns 7286437 ns 96
std::unordered_set<std::string>::insert(iterator, iterator) (half new keys)/32 26705 ns 26699 ns 26325
std::unordered_set<std::string>::insert(iterator, iterator) (half new keys)/1024 837638 ns 837685 ns 835
std::unordered_set<std::string>::insert(iterator, iterator) (half new keys)/8192 6982924 ns 6982639 ns 100
std::unordered_set<std::string>::erase(key) (existent)/32 1082 ns 1082 ns 1315040
std::unordered_set<std::string>::erase(key) (existent)/1024 815 ns 815 ns 1036096
std::unordered_set<std::string>::erase(key) (existent)/8192 1004 ns 1004 ns 868960
std::unordered_set<std::string>::erase(key) (non-existent)/32 632 ns 632 ns 1007424
std::unordered_set<std::string>::erase(key) (non-existent)/1024 609 ns 609 ns 1200256
std::unordered_set<std::string>::erase(key) (non-existent)/8192 749 ns 749 ns 1040416
std::unordered_set<std::string>::erase(iterator)/32 132 ns 131 ns 5600064
std::unordered_set<std::string>::erase(iterator)/1024 125 ns 125 ns 5720800
std::unordered_set<std::string>::erase(iterator)/8192 130 ns 130 ns 5698336
std::unordered_set<std::string>::erase(iterator, iterator) (erase half the container)/32 2188 ns 2166 ns 329336
std::unordered_set<std::string>::erase(iterator, iterator) (erase half the container)/1024 66079 ns 65889 ns 10574
std::unordered_set<std::string>::erase(iterator, iterator) (erase half the container)/8192 685952 ns 685607 ns 1001
std::unordered_set<std::string>::clear()/32 1388 ns 1372 ns 507157
std::unordered_set<std::string>::clear()/1024 47859 ns 47698 ns 15356
std::unordered_set<std::string>::clear()/8192 573679 ns 573370 ns 1137
std::unordered_set<std::string>::find(key) (existent)/32 728 ns 728 ns 955840
std::unordered_set<std::string>::find(key) (existent)/1024 682 ns 682 ns 926912
std::unordered_set<std::string>::find(key) (existent)/8192 648 ns 648 ns 923872
std::unordered_set<std::string>::find(key) (non-existent)/32 646 ns 646 ns 958112
std::unordered_set<std::string>::find(key) (non-existent)/1024 702 ns 702 ns 1090208
std::unordered_set<std::string>::find(key) (non-existent)/8192 594 ns 594 ns 1150432
std::unordered_set<std::string>::find(key) (expensive-empty)/32 313 ns 313 ns 2236000
std::unordered_set<std::string>::find(key) (expensive-empty)/1024 313 ns 313 ns 2237376
std::unordered_set<std::string>::find(key) (expensive-empty)/8192 313 ns 313 ns 2233696
std::unordered_set<std::string>::count(key) (existent)/32 803 ns 803 ns 987712
std::unordered_set<std::string>::count(key) (existent)/1024 670 ns 670 ns 919680
std::unordered_set<std::string>::count(key) (existent)/8192 668 ns 668 ns 878944
std::unordered_set<std::string>::count(key) (non-existent)/32 706 ns 706 ns 966848
std::unordered_set<std::string>::count(key) (non-existent)/1024 558 ns 558 ns 979904
std::unordered_set<std::string>::count(key) (non-existent)/8192 632 ns 632 ns 1027840
std::unordered_set<std::string>::count(key) (expensive-empty)/32 319 ns 319 ns 2196512
std::unordered_set<std::string>::count(key) (expensive-empty)/1024 318 ns 318 ns 2199328
std::unordered_set<std::string>::count(key) (expensive-empty)/8192 318 ns 318 ns 2197472
std::unordered_set<std::string>::contains(key) (existent)/32 708 ns 708 ns 1037120
std::unordered_set<std::string>::contains(key) (existent)/1024 765 ns 765 ns 889856
std::unordered_set<std::string>::contains(key) (existent)/8192 740 ns 740 ns 945600
std::unordered_set<std::string>::contains(key) (non-existent)/32 730 ns 730 ns 1077984
std::unordered_set<std::string>::contains(key) (non-existent)/1024 662 ns 662 ns 1001536
std::unordered_set<std::string>::contains(key) (non-existent)/8192 662 ns 662 ns 1024864
std::unordered_set<std::string>::contains(key) (expensive-empty)/32 321 ns 321 ns 2182656
std::unordered_set<std::string>::contains(key) (expensive-empty)/1024 320 ns 320 ns 2188096
std::unordered_set<std::string>::contains(key) (expensive-empty)/8192 320 ns 320 ns 2188192
---
.../associative_container_benchmarks.h | 28 ++++++
.../associative/hash_table_find.bench.cpp | 97 -------------------
.../associative/unordered_map.bench.cpp | 1 +
.../associative/unordered_multimap.bench.cpp | 1 +
.../associative/unordered_multiset.bench.cpp | 2 +
.../associative/unordered_set.bench.cpp | 2 +
6 files changed, 34 insertions(+), 97 deletions(-)
delete mode 100644 libcxx/test/benchmarks/containers/associative/hash_table_find.bench.cpp
diff --git a/libcxx/test/benchmarks/containers/associative/associative_container_benchmarks.h b/libcxx/test/benchmarks/containers/associative/associative_container_benchmarks.h
index fb4455c4aa9da..2f6115c5698dd 100644
--- a/libcxx/test/benchmarks/containers/associative/associative_container_benchmarks.h
+++ b/libcxx/test/benchmarks/containers/associative/associative_container_benchmarks.h
@@ -47,6 +47,14 @@ void associative_container_benchmarks(std::string container) {
return std::vector<Key>(keys.begin(), keys.end());
};
+ auto generate_expensive_keys = [=](std::size_t n) {
+ std::vector<Key> keys;
+ for (std::size_t i = 0; i < n; ++i) {
+ keys.push_back(Generate<Key>::expensive());
+ }
+ return keys;
+ };
+
auto make_value_types = [](std::vector<Key> const& keys) {
std::vector<Value> kv;
for (Key const& k : keys)
@@ -480,17 +488,37 @@ void associative_container_benchmarks(std::string container) {
};
};
+ auto with_expensive_key_empty = [=](auto func) {
+ return [=](auto& st) {
+ const std::size_t size = st.range(0);
+ std::vector<Key> keys = generate_expensive_keys(size);
+ Container c;
+
+ 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));
+ bench("find(key) (expensive-empty)", with_expensive_key_empty(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));
+ bench("count(key) (expensive-empty)", with_expensive_key_empty(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));
+ bench("contains(key) (expensive-empty)", with_expensive_key_empty(contains));
if constexpr (is_ordered_container) {
auto lower_bound = [](Container const& c, Key const& key) { return c.lower_bound(key); };
diff --git a/libcxx/test/benchmarks/containers/associative/hash_table_find.bench.cpp b/libcxx/test/benchmarks/containers/associative/hash_table_find.bench.cpp
deleted file mode 100644
index 5eea4f8a7219f..0000000000000
--- a/libcxx/test/benchmarks/containers/associative/hash_table_find.bench.cpp
+++ /dev/null
@@ -1,97 +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 <unordered_set>
-#include <string>
-#include <random>
-#include <vector>
-
-#include "../../GenerateInput.h"
-#include "benchmark/benchmark.h"
-
-// Generate random strings of at least 32 chars
-struct LongStringGenerator {
- static std::vector<std::string> cached_strings;
-
- static void ensure_strings(size_t count) {
- cached_strings.clear();
-
- std::mt19937_64 gen(42); // Fixed seed for reproducibility
- std::uniform_int_distribution<size_t> len_dist(32, 128);
-
- cached_strings.reserve(count);
- for (size_t i = 0; i < count; i++) {
- std::string str(len_dist(gen), 0);
- for (char& c : str) {
- c = 'a' + (gen() % 26);
- }
- cached_strings.push_back(std::move(str));
- }
- }
-
- const std::string& generate(size_t i) { return cached_strings[i]; }
-};
-
-std::vector<std::string> LongStringGenerator::cached_strings;
-[[maybe_unused]] auto dummy = [] { // Pre-generate 32K strings
- LongStringGenerator::ensure_strings(1 << 15);
- return 0;
-}();
-
-template <class Gen>
-static void BM_UnorderedSet_Find_EmptyNoBuckets(benchmark::State& state, Gen g) {
- const size_t lookup_count = state.range(0);
- std::unordered_set<std::string> s; // Empty and no buckets
-
- for (auto _ : state) {
- for (size_t i = 0; i < lookup_count; i++) {
- benchmark::DoNotOptimize(s.find(g.generate(i)));
- }
- }
-}
-
-template <class Gen>
-static void BM_UnorderedSet_Find_EmptyWithBuckets(benchmark::State& state, Gen g) {
- const size_t lookup_count = state.range(0);
- std::unordered_set<std::string> s;
- s.reserve(1); // Still empty but reserved buckets
-
- for (auto _ : state) {
- for (size_t i = 0; i < lookup_count; i++) {
- benchmark::DoNotOptimize(s.find(g.generate(i)));
- }
- }
-}
-
-template <class Gen>
-static void BM_UnorderedSet_Find_NonEmpty(benchmark::State& state, Gen g) {
- const size_t lookup_count = state.range(0);
- std::unordered_set<std::string> s{"hello"};
-
- for (auto _ : state) {
- for (size_t i = 0; i < lookup_count; i++) {
- benchmark::DoNotOptimize(s.find(g.generate(i)));
- }
- }
-}
-
-BENCHMARK_CAPTURE(BM_UnorderedSet_Find_EmptyNoBuckets, long_string, LongStringGenerator())
- ->RangeMultiplier(2)
- ->Range(1 << 10, 1 << 15); // Test from 1K to 32K lookups
-
-BENCHMARK_CAPTURE(BM_UnorderedSet_Find_EmptyWithBuckets, long_string, LongStringGenerator())
- ->RangeMultiplier(2)
- ->Range(1 << 10, 1 << 15);
-
-BENCHMARK_CAPTURE(BM_UnorderedSet_Find_NonEmpty, long_string, LongStringGenerator())
- ->RangeMultiplier(2)
- ->Range(1 << 10, 1 << 15);
-
-BENCHMARK_MAIN();
diff --git a/libcxx/test/benchmarks/containers/associative/unordered_map.bench.cpp b/libcxx/test/benchmarks/containers/associative/unordered_map.bench.cpp
index 57adec2d214d4..89fddccb4d531 100644
--- a/libcxx/test/benchmarks/containers/associative/unordered_map.bench.cpp
+++ b/libcxx/test/benchmarks/containers/associative/unordered_map.bench.cpp
@@ -28,6 +28,7 @@ struct support::adapt_operations<std::unordered_map<K, V>> {
int main(int argc, char** argv) {
support::associative_container_benchmarks<std::unordered_map<int, int>>("std::unordered_map<int, int>");
+ support::associative_container_benchmarks<std::unordered_map<std::string, int>>("std::unordered_map<std::string, int>");
benchmark::Initialize(&argc, argv);
benchmark::RunSpecifiedBenchmarks();
diff --git a/libcxx/test/benchmarks/containers/associative/unordered_multimap.bench.cpp b/libcxx/test/benchmarks/containers/associative/unordered_multimap.bench.cpp
index 8738ca4bf9f0c..080cf61087ddd 100644
--- a/libcxx/test/benchmarks/containers/associative/unordered_multimap.bench.cpp
+++ b/libcxx/test/benchmarks/containers/associative/unordered_multimap.bench.cpp
@@ -27,6 +27,7 @@ struct support::adapt_operations<std::unordered_multimap<K, V>> {
int main(int argc, char** argv) {
support::associative_container_benchmarks<std::unordered_multimap<int, int>>("std::unordered_multimap<int, int>");
+ support::associative_container_benchmarks<std::unordered_multimap<std::string, int>>("std::unordered_multimap<std::string, int>");
benchmark::Initialize(&argc, argv);
benchmark::RunSpecifiedBenchmarks();
diff --git a/libcxx/test/benchmarks/containers/associative/unordered_multiset.bench.cpp b/libcxx/test/benchmarks/containers/associative/unordered_multiset.bench.cpp
index 4888b01bfeba0..c4e1dc4c42651 100644
--- a/libcxx/test/benchmarks/containers/associative/unordered_multiset.bench.cpp
+++ b/libcxx/test/benchmarks/containers/associative/unordered_multiset.bench.cpp
@@ -8,6 +8,7 @@
// UNSUPPORTED: c++03, c++11, c++14, c++17
+#include <string>
#include <unordered_set>
#include "associative_container_benchmarks.h"
@@ -26,6 +27,7 @@ struct support::adapt_operations<std::unordered_multiset<K>> {
int main(int argc, char** argv) {
support::associative_container_benchmarks<std::unordered_multiset<int>>("std::unordered_multiset<int>");
+ support::associative_container_benchmarks<std::unordered_multiset<std::string>>("std::unordered_multiset<std::string>");
benchmark::Initialize(&argc, argv);
benchmark::RunSpecifiedBenchmarks();
diff --git a/libcxx/test/benchmarks/containers/associative/unordered_set.bench.cpp b/libcxx/test/benchmarks/containers/associative/unordered_set.bench.cpp
index 56420bdaadfbf..89443a597e85a 100644
--- a/libcxx/test/benchmarks/containers/associative/unordered_set.bench.cpp
+++ b/libcxx/test/benchmarks/containers/associative/unordered_set.bench.cpp
@@ -8,6 +8,7 @@
// UNSUPPORTED: c++03, c++11, c++14, c++17
+#include <string>
#include <unordered_set>
#include <utility>
@@ -27,6 +28,7 @@ struct support::adapt_operations<std::unordered_set<K>> {
int main(int argc, char** argv) {
support::associative_container_benchmarks<std::unordered_set<int>>("std::unordered_set<int>");
+ support::associative_container_benchmarks<std::unordered_set<std::string>>("std::unordered_set<std::string>");
benchmark::Initialize(&argc, argv);
benchmark::RunSpecifiedBenchmarks();
More information about the libcxx-commits
mailing list