[libcxx-commits] [libcxx] [libc++] Optimize unordered_{, multi}{map, set} move construct with allocators (PR #227955)
Nikolas Klauser via libcxx-commits
libcxx-commits at lists.llvm.org
Tue Oct 6 04:34:17 PDT 2026
https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/227955
>From 117bf3df303baf0b05a2178a393521b2eb67971b Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Thu, 1 Oct 2026 07:07:58 +0200
Subject: [PATCH] [libc++] Optimize unordered_{,multi}{map,set} move construct
with allocators
---
libcxx/include/__hash_table | 67 +++++++++++++++++++++++++-----------
libcxx/include/unordered_map | 16 ++-------
libcxx/include/unordered_set | 16 ++-------
3 files changed, 51 insertions(+), 48 deletions(-)
diff --git a/libcxx/include/__hash_table b/libcxx/include/__hash_table
index 4874e4e084c38..a1cbd01d78c9f 100644
--- a/libcxx/include/__hash_table
+++ b/libcxx/include/__hash_table
@@ -678,12 +678,13 @@ private:
_LIBCPP_HIDE_FROM_ABI size_type& size() _NOEXCEPT { return __size_; }
- _LIBCPP_HIDE_FROM_ABI void
- __copy_construct(__next_pointer __other_iter, __next_pointer __own_iter, size_t __current_chash) {
+ template <class _NodeConstructor>
+ _LIBCPP_HIDE_FROM_ABI void __construct_from_hash_table(
+ __next_pointer __other_iter, __next_pointer __own_iter, size_t __current_chash, _NodeConstructor __construct) {
auto __bucket_count = bucket_count();
for (; __other_iter; __other_iter = __other_iter->__next_) {
- __node_holder __new_node = __construct_node_hash(__other_iter->__hash(), __other_iter->__upcast()->__get_value());
+ __node_holder __new_node = __construct(__other_iter);
size_t __new_chash = std::__constrain_hash(__new_node->__hash(), __bucket_count);
if (__new_chash != __current_chash) {
@@ -696,7 +697,8 @@ private:
}
}
- _LIBCPP_HIDE_FROM_ABI void __copy_construct(__next_pointer __other_iter) {
+ template <class _NodeConstructor>
+ _LIBCPP_HIDE_FROM_ABI void __construct_from_hash_table(__next_pointer __other_iter, _NodeConstructor __construct) {
__next_pointer __own_iter = __first_node_.__ptr();
// If copying a node throws, the nodes that have already been constructed and linked into the
// list have to be deallocated. When this is called from the copy constructor the enclosing
@@ -710,7 +712,7 @@ private:
std::fill_n(__bucket_list_.get(), bucket_count(), nullptr);
});
{
- __node_holder __new_node = __construct_node_hash(__other_iter->__hash(), __other_iter->__upcast()->__get_value());
+ __node_holder __new_node = __construct(__other_iter);
__own_iter->__next_ = static_cast<__next_pointer>(__new_node.release());
}
@@ -718,10 +720,38 @@ private:
__bucket_list_[__current_chash] = __own_iter;
__other_iter = __other_iter->__next_;
__own_iter = __own_iter->__next_;
- __copy_construct(__other_iter, __own_iter, __current_chash);
+ __construct_from_hash_table(__other_iter, __own_iter, __current_chash, __construct);
__guard.__complete();
}
+ _LIBCPP_HIDE_FROM_ABI void
+ __copy_construct(__next_pointer __other_iter, __next_pointer __own_iter, size_t __current_hash) {
+ __construct_from_hash_table(__other_iter, __own_iter, __current_hash, [this](__next_pointer __nd) {
+ return __construct_node_hash(__nd->__hash(), __nd->__upcast()->__get_value());
+ });
+ }
+
+ _LIBCPP_HIDE_FROM_ABI void __copy_construct(__next_pointer __other_iter) {
+ __construct_from_hash_table(__other_iter, [this](__next_pointer __nd) {
+ return __construct_node_hash(__nd->__hash(), __nd->__upcast()->__get_value());
+ });
+ }
+
+ template <class _ValueT = _Tp, __enable_if_t<__is_hash_value_type<_ValueT>::value, int> = 0>
+ _LIBCPP_HIDE_FROM_ABI void __move_construct(__next_pointer __other_iter) {
+ __construct_from_hash_table(__other_iter, [this](__next_pointer __nd) {
+ auto& __pair = __nd->__upcast()->__get_value();
+ return __construct_node_hash(__nd->__hash(), const_cast<key_type&&>(__pair.first), std::move(__pair.second));
+ });
+ }
+
+ template <class _ValueT = _Tp, __enable_if_t<!__is_hash_value_type<_ValueT>::value, int> = 0>
+ _LIBCPP_HIDE_FROM_ABI void __move_construct(__next_pointer __other_iter) {
+ __construct_from_hash_table(__other_iter, [this](__next_pointer __nd) {
+ return __construct_node_hash(__nd->__hash(), std::move(__nd->__upcast()->__get_value()));
+ });
+ }
+
public:
_LIBCPP_HIDE_FROM_ABI size_type size() const _NOEXCEPT { return __size_; }
@@ -853,20 +883,6 @@ public:
template <class... _Args>
_LIBCPP_HIDE_FROM_ABI iterator __emplace_hint_multi(const_iterator __p, _Args&&... __args);
- template <class _ValueT = _Tp, __enable_if_t<__is_hash_value_type<_ValueT>::value, int> = 0>
- _LIBCPP_HIDE_FROM_ABI void __insert_unique_from_orphaned_node(value_type&& __value) {
- __node_holder __h = __construct_node(const_cast<key_type&&>(__value.first), std::move(__value.second));
- __node_insert_unique(__h.get());
- __h.release();
- }
-
- template <class _ValueT = _Tp, __enable_if_t<!__is_hash_value_type<_ValueT>::value, int> = 0>
- _LIBCPP_HIDE_FROM_ABI void __insert_unique_from_orphaned_node(value_type&& __value) {
- __node_holder __h = __construct_node(std::move(__value));
- __node_insert_unique(__h.get());
- __h.release();
- }
-
template <class _ValueT = _Tp, __enable_if_t<__is_hash_value_type<_ValueT>::value, int> = 0>
_LIBCPP_HIDE_FROM_ABI void __insert_multi_from_orphaned_node(value_type&& __value) {
__node_holder __h = __construct_node(const_cast<key_type&&>(__value.first), std::move(__value.second));
@@ -1181,6 +1197,17 @@ __hash_table<_Tp, _Hash, _Equal, _Alloc>::__hash_table(__hash_table&& __u, const
size() = __u.size();
__u.size() = 0;
}
+ } else {
+ auto& __bucket_list_del = __bucket_list_.get_deleter();
+ auto __bucket_count = __u.bucket_count();
+ __bucket_list_.reset(__pointer_alloc_traits::allocate(__bucket_list_del.__alloc(), __bucket_count));
+ __bucket_list_del.size() = __bucket_count;
+
+ std::fill_n(__bucket_list_.get(), __bucket_count, nullptr);
+
+ __move_construct(__u.__first_node_.__next_);
+ __size_ = __u.size();
+ __u.clear(); // Ensure that __u is in a valid state after moving out the keys
}
}
diff --git a/libcxx/include/unordered_map b/libcxx/include/unordered_map
index 68157df7d319c..8e495682424a3 100644
--- a/libcxx/include/unordered_map
+++ b/libcxx/include/unordered_map
@@ -1517,13 +1517,7 @@ unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::unordered_map(const unordered_ma
template <class _Key, class _Tp, class _Hash, class _Pred, class _Alloc>
unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::unordered_map(unordered_map&& __u, const allocator_type& __a)
- : __table_(std::move(__u.__table_), typename __table::allocator_type(__a)) {
- if (__a != __u.get_allocator()) {
- iterator __i = __u.begin();
- while (__u.size() != 0)
- __table_.__insert_unique_from_orphaned_node(std::move(__u.__table_.remove((__i++).__i_)->__get_value()));
- }
-}
+ : __table_(std::move(__u.__table_), typename __table::allocator_type(__a)) {}
template <class _Key, class _Tp, class _Hash, class _Pred, class _Alloc>
unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::unordered_map(initializer_list<value_type> __il) {
@@ -2259,13 +2253,7 @@ unordered_multimap<_Key, _Tp, _Hash, _Pred, _Alloc>::unordered_multimap(
template <class _Key, class _Tp, class _Hash, class _Pred, class _Alloc>
unordered_multimap<_Key, _Tp, _Hash, _Pred, _Alloc>::unordered_multimap(
unordered_multimap&& __u, const allocator_type& __a)
- : __table_(std::move(__u.__table_), typename __table::allocator_type(__a)) {
- if (__a != __u.get_allocator()) {
- iterator __i = __u.begin();
- while (__u.size() != 0)
- __table_.__insert_multi_from_orphaned_node(std::move(__u.__table_.remove((__i++).__i_)->__get_value()));
- }
-}
+ : __table_(std::move(__u.__table_), typename __table::allocator_type(__a)) {}
template <class _Key, class _Tp, class _Hash, class _Pred, class _Alloc>
unordered_multimap<_Key, _Tp, _Hash, _Pred, _Alloc>::unordered_multimap(initializer_list<value_type> __il) {
diff --git a/libcxx/include/unordered_set b/libcxx/include/unordered_set
index d01f94212e478..05f4c45b8ded4 100644
--- a/libcxx/include/unordered_set
+++ b/libcxx/include/unordered_set
@@ -1100,13 +1100,7 @@ unordered_set<_Value, _Hash, _Pred, _Alloc>::unordered_set(const unordered_set&
template <class _Value, class _Hash, class _Pred, class _Alloc>
unordered_set<_Value, _Hash, _Pred, _Alloc>::unordered_set(unordered_set&& __u, const allocator_type& __a)
- : __table_(std::move(__u.__table_), __a) {
- if (__a != __u.get_allocator()) {
- iterator __i = __u.begin();
- while (__u.size() != 0)
- __table_.__emplace_unique(std::move(__u.__table_.remove(__i++)->__get_value()));
- }
-}
+ : __table_(std::move(__u.__table_), __a) {}
template <class _Value, class _Hash, class _Pred, class _Alloc>
unordered_set<_Value, _Hash, _Pred, _Alloc>::unordered_set(initializer_list<value_type> __il) {
@@ -1715,13 +1709,7 @@ unordered_multiset<_Value, _Hash, _Pred, _Alloc>::unordered_multiset(
template <class _Value, class _Hash, class _Pred, class _Alloc>
unordered_multiset<_Value, _Hash, _Pred, _Alloc>::unordered_multiset(
unordered_multiset&& __u, const allocator_type& __a)
- : __table_(std::move(__u.__table_), __a) {
- if (__a != __u.get_allocator()) {
- iterator __i = __u.begin();
- while (__u.size() != 0)
- __table_.__emplace_multi(std::move(__u.__table_.remove(__i++)->__get_value()));
- }
-}
+ : __table_(std::move(__u.__table_), __a) {}
template <class _Value, class _Hash, class _Pred, class _Alloc>
unordered_multiset<_Value, _Hash, _Pred, _Alloc>::unordered_multiset(initializer_list<value_type> __il) {
More information about the libcxx-commits
mailing list