[libcxx-commits] [libcxx] 23ad5c8 - [libc++] Avoid key_eq calls in hash_table rehash (#216551)
via libcxx-commits
libcxx-commits at lists.llvm.org
Mon Oct 12 03:20:08 PDT 2026
Author: xbcnn
Date: 2026-10-12T12:20:03+02:00
New Revision: 23ad5c89c1a5961af2bc2884fe5621936bd2c147
URL: https://github.com/llvm/llvm-project/commit/23ad5c89c1a5961af2bc2884fe5621936bd2c147
DIFF: https://github.com/llvm/llvm-project/commit/23ad5c89c1a5961af2bc2884fe5621936bd2c147.diff
LOG: [libc++] Avoid key_eq calls in hash_table rehash (#216551)
Use the cached hash to avoid calling `key_eq` which might be costly.
Per `[unord.req.general]#9`, 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 it is preserved 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.
Added a dedicated benchmark for `std::unordered_multiset::rehash`. The
`std::string/8192` case (half keys tripled) showed a ~29.9% performance
improvement (137.2 us -> 96.4 us) in local tests.
Added:
Modified:
libcxx/include/__hash_table
libcxx/test/benchmarks/containers/associative/associative_container_benchmarks.h
libcxx/test/benchmarks/containers/associative/unordered_multiset.bench.cpp
Removed:
################################################################################
diff --git a/libcxx/include/__hash_table b/libcxx/include/__hash_table
index a03aa03090424..05b8034d499fe 100644
--- a/libcxx/include/__hash_table
+++ b/libcxx/include/__hash_table
@@ -1800,9 +1800,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_;
diff --git a/libcxx/test/benchmarks/containers/associative/associative_container_benchmarks.h b/libcxx/test/benchmarks/containers/associative/associative_container_benchmarks.h
index 1ee567e2a990d..910a1e6305863 100644
--- a/libcxx/test/benchmarks/containers/associative/associative_container_benchmarks.h
+++ b/libcxx/test/benchmarks/containers/associative/associative_container_benchmarks.h
@@ -86,6 +86,8 @@ void associative_container_benchmarks(std::string container) {
static constexpr bool is_map_like = requires { typename Container::mapped_type; };
+ static constexpr bool is_hash_table_based = requires(Container c) { c.bucket_count(); };
+
// 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().
@@ -753,6 +755,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 && is_hash_table_based) {
+ 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 93c0630c03878..9a006b5b48216 100644
--- a/libcxx/test/benchmarks/containers/associative/unordered_multiset.bench.cpp
+++ b/libcxx/test/benchmarks/containers/associative/unordered_multiset.bench.cpp
@@ -29,6 +29,8 @@ 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