[libcxx-commits] [libcxx] [libc++] Avoid key_eq calls in hash_table rehash (PR #216551)

via libcxx-commits libcxx-commits at lists.llvm.org
Wed Aug 26 01:54:06 PDT 2026


https://github.com/xbcnn updated https://github.com/llvm/llvm-project/pull/216551

>From d6c074098ff978d447d96577df3879f7bb92c28c Mon Sep 17 00:00:00 2001
From: arbin <xbcnn at 163.com>
Date: Sun, 16 Aug 2026 16:24:05 +0800
Subject: [PATCH 1/2] [libc++] Avoid key_eq calls in hash_table rehash

We can use cached hash to avoid calling key_eq which might be costly.

For unordered_multiset and unordered_multimap, rehashing preserves the
relative ordering of equivalent elements. These elements are contiguous
and share the same key and hash.
For elements with the same hash but different keys, if any, their relative
order is not required, but we preserve it for simplicity.

This is still not applied to unique-key containers (unordered_set
and unordered_map), where same-hash contiguous blocks are rare and
the extra check would not be worthwhile.
---
 libcxx/include/__hash_table | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/libcxx/include/__hash_table b/libcxx/include/__hash_table
index ed58789378bc1..d2ce69fda8e57 100644
--- a/libcxx/include/__hash_table
+++ b/libcxx/include/__hash_table
@@ -1773,9 +1773,7 @@ void __hash_table<_Tp, _Hash, _Equal, _Alloc>::__do_rehash(size_type __bucket_co
       } else {
         __next_pointer __np = __cp;
         if _LIBCPP_CONSTEXPR (!_UniqueKeys) {
-          for (; __np->__next_ != nullptr &&
-                 key_eq()(__cp->__upcast()->__get_value(), __np->__next_->__upcast()->__get_value());
-               __np = __np->__next_)
+          for (; __np->__next_ != nullptr && __cp->__hash() == __np->__next_->__hash(); __np = __np->__next_)
             ;
         }
         __pp->__next_                    = __np->__next_;

>From b4ed5f3a3508cce19045f8a1e24080c63eb9c346 Mon Sep 17 00:00:00 2001
From: yangxiaobing <xbcnn at 163.com>
Date: Wed, 26 Aug 2026 16:51:28 +0800
Subject: [PATCH 2/2] Add benchmarks

---
 .../associative_container_benchmarks.h        | 22 +++++++++++++++++++
 .../associative/unordered_multiset.bench.cpp  |  1 +
 2 files changed, 23 insertions(+)

diff --git a/libcxx/test/benchmarks/containers/associative/associative_container_benchmarks.h b/libcxx/test/benchmarks/containers/associative/associative_container_benchmarks.h
index 34cd1ca90b647..21230878fa9b0 100644
--- a/libcxx/test/benchmarks/containers/associative/associative_container_benchmarks.h
+++ b/libcxx/test/benchmarks/containers/associative/associative_container_benchmarks.h
@@ -731,6 +731,28 @@ void associative_container_benchmarks(std::string container) {
     bench_non_empty(
         "equal_range(key)", query_bench([](Container const& c, Key const& key) { return c.equal_range(key); }));
   }
+
+  if constexpr (is_multi_key_container) {
+      bench_non_empty("rehash (half keys tripled)", [=](auto& st) TEST_ALIGN_BENCHMARK {
+        const std::size_t size = st.range(0);
+        std::vector<Value> in  = make_value_types(generate_unique_keys(size));
+        for (std::size_t i = 0; i != size / 2; ++i) {
+          in.push_back(in.at(i * 2));
+          in.push_back(in.at(i * 2));
+        }
+        Container c(in.begin(), in.end());
+
+        for ([[maybe_unused]] auto _ : st) {
+          c.rehash(c.bucket_count() * 2);
+          benchmark::DoNotOptimize(c);
+          benchmark::ClobberMemory();
+
+          st.PauseTiming();
+          c = Container(in.begin(), in.end());
+          st.ResumeTiming();
+        }
+      });
+  }
 }
 
 } // namespace support
diff --git a/libcxx/test/benchmarks/containers/associative/unordered_multiset.bench.cpp b/libcxx/test/benchmarks/containers/associative/unordered_multiset.bench.cpp
index 09412fc4aeae7..ad1bb714e1629 100644
--- a/libcxx/test/benchmarks/containers/associative/unordered_multiset.bench.cpp
+++ b/libcxx/test/benchmarks/containers/associative/unordered_multiset.bench.cpp
@@ -29,6 +29,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();



More information about the libcxx-commits mailing list