Skip to content

Commit dddd4fb

Browse files
committed
[libc++] Optimize __tree copy/move constructor/assignment with allocator
1 parent f1c1063 commit dddd4fb

File tree

14 files changed

+202
-132
lines changed

14 files changed

+202
-132
lines changed

libcxx/include/__tree

Lines changed: 95 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -899,6 +899,18 @@ public:
899899
}
900900

901901
_LIBCPP_HIDE_FROM_ABI __tree(const __tree& __t);
902+
903+
_LIBCPP_HIDE_FROM_ABI __tree(const __tree& __other, const allocator_type& __alloc)
904+
: __begin_node_(__end_node()), __node_alloc_(__alloc), __size_(0), __value_comp_(__other.value_comp()) {
905+
if (__other.size() == 0)
906+
return;
907+
908+
*__root_ptr() = static_cast<__node_base_pointer>(__copy_construct_tree(__other.__root()));
909+
__root()->__parent_ = __end_node();
910+
__begin_node_ = static_cast<__end_node_pointer>(std::__tree_min(__end_node()->__left_));
911+
__size_ = __other.size();
912+
}
913+
902914
_LIBCPP_HIDE_FROM_ABI __tree& operator=(const __tree& __t);
903915
template <class _ForwardIterator>
904916
_LIBCPP_HIDE_FROM_ABI void __assign_unique(_ForwardIterator __first, _ForwardIterator __last);
@@ -1007,27 +1019,6 @@ public:
10071019
std::forward<_Args>(__args)...);
10081020
}
10091021

1010-
template <class _ValueT = _Tp, __enable_if_t<__is_tree_value_type_v<_ValueT>, int> = 0>
1011-
_LIBCPP_HIDE_FROM_ABI void
1012-
__insert_unique_from_orphaned_node(const_iterator __p, __get_node_value_type_t<_Tp>&& __value) {
1013-
__emplace_hint_unique(__p, const_cast<key_type&&>(__value.first), std::move(__value.second));
1014-
}
1015-
1016-
template <class _ValueT = _Tp, __enable_if_t<!__is_tree_value_type_v<_ValueT>, int> = 0>
1017-
_LIBCPP_HIDE_FROM_ABI void __insert_unique_from_orphaned_node(const_iterator __p, _Tp&& __value) {
1018-
__emplace_hint_unique(__p, std::move(__value));
1019-
}
1020-
1021-
template <class _ValueT = _Tp, __enable_if_t<__is_tree_value_type_v<_ValueT>, int> = 0>
1022-
_LIBCPP_HIDE_FROM_ABI void __insert_multi_from_orphaned_node(const_iterator __p, value_type&& __value) {
1023-
__emplace_hint_multi(__p, const_cast<key_type&&>(__value.first), std::move(__value.second));
1024-
}
1025-
1026-
template <class _ValueT = _Tp, __enable_if_t<!__is_tree_value_type_v<_ValueT>, int> = 0>
1027-
_LIBCPP_HIDE_FROM_ABI void __insert_multi_from_orphaned_node(const_iterator __p, _Tp&& __value) {
1028-
__emplace_hint_multi(__p, std::move(__value));
1029-
}
1030-
10311022
template <class _InIter, class _Sent>
10321023
_LIBCPP_HIDE_FROM_ABI void __insert_range_multi(_InIter __first, _Sent __last) {
10331024
if (__first == __last)
@@ -1400,19 +1391,19 @@ private:
14001391
// copy the exact structure 1:1. Since this is for copy construction _only_ we know that we get a correct tree. If we
14011392
// didn't get a correct tree, the invariants of __tree are broken and we have a much bigger problem than an improperly
14021393
// balanced tree.
1394+
template <class _NodeConstructor>
14031395
#ifdef _LIBCPP_COMPILER_CLANG_BASED // FIXME: GCC complains about not being able to always_inline a recursive function
14041396
_LIBCPP_HIDE_FROM_ABI
14051397
#endif
1406-
__node_pointer
1407-
__copy_construct_tree(__node_pointer __src) {
1398+
__node_pointer __construct_from_tree(__node_pointer __src, _NodeConstructor __construct) {
14081399
if (!__src)
14091400
return nullptr;
14101401

1411-
__node_holder __new_node = __construct_node(__src->__get_value());
1402+
__node_holder __new_node = __construct(__src->__get_value());
14121403

14131404
unique_ptr<__node, __tree_deleter> __left(
1414-
__copy_construct_tree(static_cast<__node_pointer>(__src->__left_)), __node_alloc_);
1415-
__node_pointer __right = __copy_construct_tree(static_cast<__node_pointer>(__src->__right_));
1405+
__construct_from_tree(static_cast<__node_pointer>(__src->__left_), __construct), __node_alloc_);
1406+
__node_pointer __right = __construct_from_tree(static_cast<__node_pointer>(__src->__right_), __construct);
14161407

14171408
__node_pointer __new_node_ptr = __new_node.release();
14181409

@@ -1426,46 +1417,85 @@ private:
14261417
return __new_node_ptr;
14271418
}
14281419

1420+
_LIBCPP_HIDE_FROM_ABI __node_pointer __copy_construct_tree(__node_pointer __src) {
1421+
return __construct_from_tree(__src, [this](const value_type& __val) { return __construct_node(__val); });
1422+
}
1423+
1424+
template <class _ValueT = _Tp, __enable_if_t<__is_tree_value_type_v<_ValueT>, int> = 0>
1425+
_LIBCPP_HIDE_FROM_ABI __node_pointer __move_construct_tree(__node_pointer __src) {
1426+
return __construct_from_tree(__src, [this](value_type& __val) {
1427+
return __construct_node(const_cast<key_type&&>(__val.first), std::move(__val.second));
1428+
});
1429+
}
1430+
1431+
template <class _ValueT = _Tp, __enable_if_t<!__is_tree_value_type_v<_ValueT>, int> = 0>
1432+
_LIBCPP_HIDE_FROM_ABI __node_pointer __move_construct_tree(__node_pointer __src) {
1433+
return __construct_from_tree(__src, [this](value_type& __val) { return __construct_node(std::move(__val)); });
1434+
}
1435+
1436+
template <class _Assignment, class _ConstructionAlg>
14291437
// This copy assignment will always produce a correct red-black-tree assuming the incoming tree is correct, since our
14301438
// own tree is a red-black-tree and the incoming tree is a red-black-tree. The invariants of a red-black-tree are
14311439
// temporarily not met until all of the incoming red-black tree is copied.
14321440
#ifdef _LIBCPP_COMPILER_CLANG_BASED // FIXME: GCC complains about not being able to always_inline a recursive function
14331441
_LIBCPP_HIDE_FROM_ABI
14341442
#endif
1435-
__node_pointer
1436-
__copy_assign_tree(__node_pointer __dest, __node_pointer __src) {
1443+
__node_pointer __assign_from_tree(
1444+
__node_pointer __dest, __node_pointer __src, _Assignment __assign, _ConstructionAlg __continue_with_construct) {
14371445
if (!__src) {
14381446
destroy(__dest);
14391447
return nullptr;
14401448
}
14411449

1442-
__assign_value(__dest->__get_value(), __src->__get_value());
1450+
__assign(__dest->__get_value(), __src->__get_value());
14431451
__dest->__is_black_ = __src->__is_black_;
14441452

14451453
// If we already have a left node in the destination tree, reuse it and copy-assign recursively
14461454
if (__dest->__left_) {
1447-
__dest->__left_ = static_cast<__node_base_pointer>(__copy_assign_tree(
1448-
static_cast<__node_pointer>(__dest->__left_), static_cast<__node_pointer>(__src->__left_)));
1455+
__dest->__left_ = static_cast<__node_base_pointer>(__assign_from_tree(
1456+
static_cast<__node_pointer>(__dest->__left_),
1457+
static_cast<__node_pointer>(__src->__left_),
1458+
__assign,
1459+
__continue_with_construct));
14491460

14501461
// Otherwise, we must create new nodes; copy-construct from here on
14511462
} else if (__src->__left_) {
1452-
auto __new_left = __copy_construct_tree(static_cast<__node_pointer>(__src->__left_));
1463+
auto __new_left = __continue_with_construct(static_cast<__node_pointer>(__src->__left_));
14531464
__dest->__left_ = static_cast<__node_base_pointer>(__new_left);
14541465
__new_left->__parent_ = static_cast<__end_node_pointer>(__dest);
14551466
}
14561467

14571468
// Identical to the left case above, just for the right nodes
14581469
if (__dest->__right_) {
1459-
__dest->__right_ = static_cast<__node_base_pointer>(__copy_assign_tree(
1460-
static_cast<__node_pointer>(__dest->__right_), static_cast<__node_pointer>(__src->__right_)));
1470+
__dest->__right_ = static_cast<__node_base_pointer>(__assign_from_tree(
1471+
static_cast<__node_pointer>(__dest->__right_),
1472+
static_cast<__node_pointer>(__src->__right_),
1473+
__assign,
1474+
__continue_with_construct));
14611475
} else if (__src->__right_) {
1462-
auto __new_right = __copy_construct_tree(static_cast<__node_pointer>(__src->__right_));
1476+
auto __new_right = __continue_with_construct(static_cast<__node_pointer>(__src->__right_));
14631477
__dest->__right_ = static_cast<__node_base_pointer>(__new_right);
14641478
__new_right->__parent_ = static_cast<__end_node_pointer>(__dest);
14651479
}
14661480

14671481
return __dest;
14681482
}
1483+
1484+
_LIBCPP_HIDE_FROM_ABI __node_pointer __copy_assign_tree(__node_pointer __dest, __node_pointer __src) {
1485+
return __assign_from_tree(
1486+
__dest,
1487+
__src,
1488+
[](value_type& __lhs, const value_type& __rhs) { __assign_value(__lhs, std::move(__rhs)); },
1489+
[this](__node_pointer __nd) { return __copy_construct_tree(__nd); });
1490+
}
1491+
1492+
_LIBCPP_HIDE_FROM_ABI __node_pointer __move_assign_tree(__node_pointer __dest, __node_pointer __src) {
1493+
return __assign_from_tree(
1494+
__dest,
1495+
__src,
1496+
[](value_type& __lhs, value_type& __rhs) { __assign_value(__lhs, std::move(__rhs)); },
1497+
[this](__node_pointer __nd) { return __move_construct_tree(__nd); });
1498+
}
14691499
};
14701500

14711501
// Precondition: __size_ != 0
@@ -1606,21 +1636,26 @@ __tree<_Tp, _Compare, _Allocator>::__tree(__tree&& __t) _NOEXCEPT_(
16061636

16071637
template <class _Tp, class _Compare, class _Allocator>
16081638
__tree<_Tp, _Compare, _Allocator>::__tree(__tree&& __t, const allocator_type& __a)
1609-
: __node_alloc_(__node_allocator(__a)), __size_(0), __value_comp_(std::move(__t.value_comp())) {
1639+
: __begin_node_(__end_node()),
1640+
__node_alloc_(__node_allocator(__a)),
1641+
__size_(0),
1642+
__value_comp_(std::move(__t.value_comp())) {
1643+
if (__t.size() == 0)
1644+
return;
16101645
if (__a == __t.__alloc()) {
1611-
if (__t.__size_ == 0)
1612-
__begin_node_ = __end_node();
1613-
else {
1614-
__begin_node_ = __t.__begin_node_;
1615-
__end_node()->__left_ = __t.__end_node()->__left_;
1616-
__end_node()->__left_->__parent_ = static_cast<__end_node_pointer>(__end_node());
1617-
__size_ = __t.__size_;
1618-
__t.__begin_node_ = __t.__end_node();
1619-
__t.__end_node()->__left_ = nullptr;
1620-
__t.__size_ = 0;
1621-
}
1646+
__begin_node_ = __t.__begin_node_;
1647+
__end_node()->__left_ = __t.__end_node()->__left_;
1648+
__end_node()->__left_->__parent_ = static_cast<__end_node_pointer>(__end_node());
1649+
__size_ = __t.__size_;
1650+
__t.__begin_node_ = __t.__end_node();
1651+
__t.__end_node()->__left_ = nullptr;
1652+
__t.__size_ = 0;
16221653
} else {
1623-
__begin_node_ = __end_node();
1654+
*__root_ptr() = static_cast<__node_base_pointer>(__move_construct_tree(__t.__root()));
1655+
__root()->__parent_ = __end_node();
1656+
__begin_node_ = static_cast<__end_node_pointer>(std::__tree_min(__end_node()->__left_));
1657+
__size_ = __t.size();
1658+
__t.clear(); // Ensure that __t is in a valid state after moving out the keys
16241659
}
16251660
}
16261661

@@ -1645,22 +1680,21 @@ void __tree<_Tp, _Compare, _Allocator>::__move_assign(__tree& __t, true_type)
16451680

16461681
template <class _Tp, class _Compare, class _Allocator>
16471682
void __tree<_Tp, _Compare, _Allocator>::__move_assign(__tree& __t, false_type) {
1648-
if (__node_alloc() == __t.__node_alloc())
1683+
if (__node_alloc() == __t.__node_alloc()) {
16491684
__move_assign(__t, true_type());
1650-
else {
1651-
value_comp() = std::move(__t.value_comp());
1652-
const_iterator __e = end();
1685+
} else {
1686+
value_comp() = std::move(__t.value_comp());
16531687
if (__size_ != 0) {
1654-
_DetachedTreeCache __cache(this);
1655-
while (__cache.__get() != nullptr && __t.__size_ != 0) {
1656-
__assign_value(__cache.__get()->__get_value(), std::move(__t.remove(__t.begin())->__get_value()));
1657-
__node_insert_multi(__cache.__get());
1658-
__cache.__advance();
1659-
}
1660-
}
1661-
while (__t.__size_ != 0) {
1662-
__insert_multi_from_orphaned_node(__e, std::move(__t.remove(__t.begin())->__get_value()));
1688+
*__root_ptr() = static_cast<__node_base_pointer>(__move_assign_tree(__root(), __t.__root()));
1689+
} else {
1690+
*__root_ptr() = static_cast<__node_base_pointer>(__move_construct_tree(__t.__root()));
1691+
if (__root())
1692+
__root()->__parent_ = __end_node();
16631693
}
1694+
__begin_node_ =
1695+
__end_node()->__left_ ? static_cast<__end_node_pointer>(std::__tree_min(__end_node()->__left_)) : __end_node();
1696+
__size_ = __t.size();
1697+
__t.clear(); // Ensure that __t is in a valid state after moving out the keys
16641698
}
16651699
}
16661700

libcxx/include/map

Lines changed: 4 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -997,7 +997,7 @@ public:
997997

998998
_LIBCPP_HIDE_FROM_ABI map(map&& __m) = default;
999999

1000-
_LIBCPP_HIDE_FROM_ABI map(map&& __m, const allocator_type& __a);
1000+
_LIBCPP_HIDE_FROM_ABI map(map&& __m, const allocator_type& __a) : __tree_(std::move(__m.__tree_), __a) {}
10011001

10021002
_LIBCPP_HIDE_FROM_ABI map& operator=(map&& __m) = default;
10031003

@@ -1025,10 +1025,7 @@ public:
10251025

10261026
_LIBCPP_HIDE_FROM_ABI explicit map(const allocator_type& __a) : __tree_(typename __base::allocator_type(__a)) {}
10271027

1028-
_LIBCPP_HIDE_FROM_ABI map(const map& __m, const allocator_type& __a)
1029-
: __tree_(__m.__tree_.value_comp(), typename __base::allocator_type(__a)) {
1030-
insert(__m.begin(), __m.end());
1031-
}
1028+
_LIBCPP_HIDE_FROM_ABI map(const map& __m, const allocator_type& __alloc) : __tree_(__m.__tree_, __alloc) {}
10321029

10331030
_LIBCPP_HIDE_FROM_ABI ~map() { static_assert(sizeof(std::__diagnose_non_const_comparator<_Key, _Compare>()), ""); }
10341031

@@ -1428,18 +1425,6 @@ map(initializer_list<pair<_Key, _Tp>>, _Allocator)
14281425
# endif
14291426

14301427
# ifndef _LIBCPP_CXX03_LANG
1431-
template <class _Key, class _Tp, class _Compare, class _Allocator>
1432-
map<_Key, _Tp, _Compare, _Allocator>::map(map&& __m, const allocator_type& __a)
1433-
: __tree_(std::move(__m.__tree_), typename __base::allocator_type(__a)) {
1434-
if (__a != __m.get_allocator()) {
1435-
const_iterator __e = cend();
1436-
while (!__m.empty()) {
1437-
__tree_.__insert_unique_from_orphaned_node(
1438-
__e.__i_, std::move(__m.__tree_.remove(__m.begin().__i_)->__get_value()));
1439-
}
1440-
}
1441-
}
1442-
14431428
template <class _Key, class _Tp, class _Compare, class _Allocator>
14441429
_Tp& map<_Key, _Tp, _Compare, _Allocator>::operator[](const key_type& __k) {
14451430
return __tree_.__emplace_unique(std::piecewise_construct, std::forward_as_tuple(__k), std::forward_as_tuple())
@@ -1685,7 +1670,7 @@ public:
16851670

16861671
_LIBCPP_HIDE_FROM_ABI multimap(multimap&& __m) = default;
16871672

1688-
_LIBCPP_HIDE_FROM_ABI multimap(multimap&& __m, const allocator_type& __a);
1673+
_LIBCPP_HIDE_FROM_ABI multimap(multimap&& __m, const allocator_type& __a) : __tree_(std::move(__m.__tree_), __a) {}
16891674

16901675
_LIBCPP_HIDE_FROM_ABI multimap& operator=(multimap&& __m) = default;
16911676

@@ -1714,10 +1699,7 @@ public:
17141699

17151700
_LIBCPP_HIDE_FROM_ABI explicit multimap(const allocator_type& __a) : __tree_(typename __base::allocator_type(__a)) {}
17161701

1717-
_LIBCPP_HIDE_FROM_ABI multimap(const multimap& __m, const allocator_type& __a)
1718-
: __tree_(__m.__tree_.value_comp(), typename __base::allocator_type(__a)) {
1719-
insert(__m.begin(), __m.end());
1720-
}
1702+
_LIBCPP_HIDE_FROM_ABI multimap(const multimap& __m, const allocator_type& __a) : __tree_(__m.__tree_, __a) {}
17211703

17221704
_LIBCPP_HIDE_FROM_ABI ~multimap() {
17231705
static_assert(sizeof(std::__diagnose_non_const_comparator<_Key, _Compare>()), "");
@@ -1992,19 +1974,6 @@ multimap(initializer_list<pair<_Key, _Tp>>, _Allocator)
19921974
-> multimap<remove_const_t<_Key>, _Tp, less<remove_const_t<_Key>>, _Allocator>;
19931975
# endif
19941976

1995-
# ifndef _LIBCPP_CXX03_LANG
1996-
template <class _Key, class _Tp, class _Compare, class _Allocator>
1997-
multimap<_Key, _Tp, _Compare, _Allocator>::multimap(multimap&& __m, const allocator_type& __a)
1998-
: __tree_(std::move(__m.__tree_), typename __base::allocator_type(__a)) {
1999-
if (__a != __m.get_allocator()) {
2000-
const_iterator __e = cend();
2001-
while (!__m.empty())
2002-
__tree_.__insert_multi_from_orphaned_node(
2003-
__e.__i_, std::move(__m.__tree_.remove(__m.begin().__i_)->__get_value()));
2004-
}
2005-
}
2006-
# endif
2007-
20081977
template <class _Key, class _Tp, class _Compare, class _Allocator>
20091978
inline _LIBCPP_HIDE_FROM_ABI bool
20101979
operator==(const multimap<_Key, _Tp, _Compare, _Allocator>& __x, const multimap<_Key, _Tp, _Compare, _Allocator>& __y) {

libcxx/include/set

Lines changed: 4 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -673,12 +673,10 @@ public:
673673

674674
_LIBCPP_HIDE_FROM_ABI explicit set(const allocator_type& __a) : __tree_(__a) {}
675675

676-
_LIBCPP_HIDE_FROM_ABI set(const set& __s, const allocator_type& __a) : __tree_(__s.__tree_.value_comp(), __a) {
677-
insert(__s.begin(), __s.end());
678-
}
676+
_LIBCPP_HIDE_FROM_ABI set(const set& __s, const allocator_type& __alloc) : __tree_(__s.__tree_, __alloc) {}
679677

680678
# ifndef _LIBCPP_CXX03_LANG
681-
_LIBCPP_HIDE_FROM_ABI set(set&& __s, const allocator_type& __a);
679+
_LIBCPP_HIDE_FROM_ABI set(set&& __s, const allocator_type& __alloc) : __tree_(std::move(__s.__tree_), __alloc) {}
682680

683681
_LIBCPP_HIDE_FROM_ABI set(initializer_list<value_type> __il, const value_compare& __comp = value_compare())
684682
: __tree_(__comp) {
@@ -948,19 +946,6 @@ template <class _Key, class _Allocator, class = enable_if_t<__is_allocator_v<_Al
948946
set(initializer_list<_Key>, _Allocator) -> set<_Key, less<_Key>, _Allocator>;
949947
# endif
950948

951-
# ifndef _LIBCPP_CXX03_LANG
952-
953-
template <class _Key, class _Compare, class _Allocator>
954-
set<_Key, _Compare, _Allocator>::set(set&& __s, const allocator_type& __a) : __tree_(std::move(__s.__tree_), __a) {
955-
if (__a != __s.get_allocator()) {
956-
const_iterator __e = cend();
957-
while (!__s.empty())
958-
insert(__e, std::move(__s.__tree_.remove(__s.begin())->__get_value()));
959-
}
960-
}
961-
962-
# endif // _LIBCPP_CXX03_LANG
963-
964949
template <class _Key, class _Compare, class _Allocator>
965950
inline _LIBCPP_HIDE_FROM_ABI bool
966951
operator==(const set<_Key, _Compare, _Allocator>& __x, const set<_Key, _Compare, _Allocator>& __y) {
@@ -1130,13 +1115,10 @@ public:
11301115
# ifndef _LIBCPP_CXX03_LANG
11311116
_LIBCPP_HIDE_FROM_ABI multiset(multiset&& __s) = default;
11321117

1133-
_LIBCPP_HIDE_FROM_ABI multiset(multiset&& __s, const allocator_type& __a);
1118+
_LIBCPP_HIDE_FROM_ABI multiset(multiset&& __s, const allocator_type& __a) : __tree_(std::move(__s.__tree_), __a) {}
11341119
# endif // _LIBCPP_CXX03_LANG
11351120
_LIBCPP_HIDE_FROM_ABI explicit multiset(const allocator_type& __a) : __tree_(__a) {}
1136-
_LIBCPP_HIDE_FROM_ABI multiset(const multiset& __s, const allocator_type& __a)
1137-
: __tree_(__s.__tree_.value_comp(), __a) {
1138-
insert(__s.begin(), __s.end());
1139-
}
1121+
_LIBCPP_HIDE_FROM_ABI multiset(const multiset& __s, const allocator_type& __a) : __tree_(__s.__tree_, __a) {}
11401122

11411123
# ifndef _LIBCPP_CXX03_LANG
11421124
_LIBCPP_HIDE_FROM_ABI multiset(initializer_list<value_type> __il, const value_compare& __comp = value_compare())
@@ -1409,20 +1391,6 @@ template <class _Key, class _Allocator, class = enable_if_t<__is_allocator_v<_Al
14091391
multiset(initializer_list<_Key>, _Allocator) -> multiset<_Key, less<_Key>, _Allocator>;
14101392
# endif
14111393

1412-
# ifndef _LIBCPP_CXX03_LANG
1413-
1414-
template <class _Key, class _Compare, class _Allocator>
1415-
multiset<_Key, _Compare, _Allocator>::multiset(multiset&& __s, const allocator_type& __a)
1416-
: __tree_(std::move(__s.__tree_), __a) {
1417-
if (__a != __s.get_allocator()) {
1418-
const_iterator __e = cend();
1419-
while (!__s.empty())
1420-
insert(__e, std::move(__s.__tree_.remove(__s.begin())->__get_value()));
1421-
}
1422-
}
1423-
1424-
# endif // _LIBCPP_CXX03_LANG
1425-
14261394
template <class _Key, class _Compare, class _Allocator>
14271395
inline _LIBCPP_HIDE_FROM_ABI bool
14281396
operator==(const multiset<_Key, _Compare, _Allocator>& __x, const multiset<_Key, _Compare, _Allocator>& __y) {

0 commit comments

Comments
 (0)