Skip to content

Commit 651b570

Browse files
committed
[libc++] Optimize copy construction and assignment of __tree
1 parent cdb67e1 commit 651b570

File tree

6 files changed

+129
-26
lines changed

6 files changed

+129
-26
lines changed

libcxx/docs/ReleaseNotes/22.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ Implemented Papers
4343
Improvements and New Features
4444
-----------------------------
4545

46+
- The performance of ``map::map(const map&)`` has been improved up to 2.3x
47+
- The performance of ``map::operator=(const map&)`` has been improved by up to 11x
48+
4649
Deprecations and Removals
4750
-------------------------
4851

libcxx/include/__tree

Lines changed: 105 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1213,6 +1213,80 @@ private:
12131213
__node_pointer __cache_root_;
12141214
__node_pointer __cache_elem_;
12151215
};
1216+
1217+
class __tree_deleter {
1218+
__node_allocator& __alloc_;
1219+
1220+
public:
1221+
using pointer = __node_pointer;
1222+
1223+
_LIBCPP_HIDE_FROM_ABI __tree_deleter(__node_allocator& __alloc) : __alloc_(__alloc) {}
1224+
1225+
void operator()(__node_pointer __ptr) {
1226+
if (!__ptr)
1227+
return;
1228+
1229+
(*this)(static_cast<__node_pointer>(__ptr->__left_));
1230+
1231+
auto __right = __ptr->__right_;
1232+
1233+
__node_traits::destroy(__alloc_, std::addressof(__ptr->__value_));
1234+
__node_traits::deallocate(__alloc_, __ptr, 1);
1235+
1236+
(*this)(static_cast<__node_pointer>(__right));
1237+
}
1238+
};
1239+
1240+
_LIBCPP_HIDE_FROM_ABI __node_pointer __copy_construct_tree(__node_pointer __src) {
1241+
if (!__src)
1242+
return nullptr;
1243+
1244+
__node_holder __new_node = __construct_node(__src->__value_);
1245+
1246+
unique_ptr<__node, __tree_deleter> __left(
1247+
__copy_construct_tree(static_cast<__node_pointer>(__src->__left_)), __node_alloc_);
1248+
__node_pointer __right = __copy_construct_tree(static_cast<__node_pointer>(__src->__right_));
1249+
1250+
__node_pointer __new_node_ptr = __new_node.release();
1251+
1252+
__new_node_ptr->__is_black_ = __src->__is_black_;
1253+
__new_node_ptr->__left_ = static_cast<__node_base_pointer>(__left.release());
1254+
__new_node_ptr->__right_ = static_cast<__node_base_pointer>(__right);
1255+
if (__new_node_ptr->__left_)
1256+
__new_node_ptr->__left_->__parent_ = static_cast<__end_node_pointer>(__new_node_ptr);
1257+
if (__new_node_ptr->__right_)
1258+
__new_node_ptr->__right_->__parent_ = static_cast<__end_node_pointer>(__new_node_ptr);
1259+
return __new_node_ptr;
1260+
}
1261+
1262+
_LIBCPP_HIDE_FROM_ABI __node_pointer __copy_assign_tree(__node_pointer __assign_to, __node_pointer __src) {
1263+
if (!__src) {
1264+
destroy(__assign_to);
1265+
return nullptr;
1266+
}
1267+
1268+
__assign_value(__assign_to->__value_, __src->__value_);
1269+
1270+
if (__assign_to->__left_) {
1271+
__assign_to->__left_ = static_cast<__node_base_pointer>(__copy_assign_tree(
1272+
static_cast<__node_pointer>(__assign_to->__left_), static_cast<__node_pointer>(__src->__left_)));
1273+
} else if (__src->__left_) {
1274+
auto __new_left = __copy_construct_tree(static_cast<__node_pointer>(__src->__left_));
1275+
__assign_to->__left_ = static_cast<__node_base_pointer>(__new_left);
1276+
__new_left->__parent_ = static_cast<__end_node_pointer>(__assign_to);
1277+
}
1278+
1279+
if (__assign_to->__right_) {
1280+
__assign_to->__right_ = static_cast<__node_base_pointer>(__copy_assign_tree(
1281+
static_cast<__node_pointer>(__assign_to->__right_), static_cast<__node_pointer>(__src->__right_)));
1282+
} else if (__src->__right_) {
1283+
auto __new_right = __copy_construct_tree(static_cast<__node_pointer>(__src->__right_));
1284+
__assign_to->__right_ = static_cast<__node_base_pointer>(__new_right);
1285+
__new_right->__parent_ = static_cast<__end_node_pointer>(__assign_to);
1286+
}
1287+
1288+
return __assign_to;
1289+
}
12161290
};
12171291

12181292
template <class _Tp, class _Compare, class _Allocator>
@@ -1277,11 +1351,28 @@ __tree<_Tp, _Compare, _Allocator>::_DetachedTreeCache::__detach_next(__node_poin
12771351

12781352
template <class _Tp, class _Compare, class _Allocator>
12791353
__tree<_Tp, _Compare, _Allocator>& __tree<_Tp, _Compare, _Allocator>::operator=(const __tree& __t) {
1280-
if (this != std::addressof(__t)) {
1281-
value_comp() = __t.value_comp();
1282-
__copy_assign_alloc(__t);
1283-
__assign_multi(__t.begin(), __t.end());
1354+
if (this == std::addressof(__t))
1355+
return *this;
1356+
1357+
value_comp() = __t.value_comp();
1358+
__copy_assign_alloc(__t);
1359+
1360+
if (__t.size() == 0) {
1361+
clear();
1362+
return *this;
1363+
}
1364+
1365+
if (__size_ != 0) {
1366+
__end_node_.__left_ = static_cast<__node_base_pointer>(__copy_assign_tree(
1367+
static_cast<__node_pointer>(__end_node_.__left_), static_cast<__node_pointer>(__t.__end_node_.__left_)));
1368+
} else {
1369+
__end_node_.__left_ =
1370+
static_cast<__node_base_pointer>(__copy_construct_tree(static_cast<__node_pointer>(__t.__end_node_.__left_)));
1371+
__end_node_.__left_->__parent_ = __end_node();
12841372
}
1373+
__begin_node_ = static_cast<__end_node_pointer>(std::__tree_min(static_cast<__node_base_pointer>(__end_node())));
1374+
__size_ = __t.__size_;
1375+
12851376
return *this;
12861377
}
12871378

@@ -1327,11 +1418,18 @@ void __tree<_Tp, _Compare, _Allocator>::__assign_multi(_InputIterator __first, _
13271418

13281419
template <class _Tp, class _Compare, class _Allocator>
13291420
__tree<_Tp, _Compare, _Allocator>::__tree(const __tree& __t)
1330-
: __begin_node_(),
1421+
: __begin_node_(__end_node()),
13311422
__node_alloc_(__node_traits::select_on_container_copy_construction(__t.__node_alloc())),
13321423
__size_(0),
13331424
__value_comp_(__t.value_comp()) {
1334-
__begin_node_ = __end_node();
1425+
if (__t.__size_ == 0)
1426+
return;
1427+
1428+
__end_node_.__left_ =
1429+
static_cast<__node_base_pointer>(__copy_construct_tree(static_cast<__node_pointer>(__t.__end_node_.__left_)));
1430+
__end_node_.__left_->__parent_ = __end_node();
1431+
__begin_node_ = static_cast<__end_node_pointer>(std::__tree_min(static_cast<__node_base_pointer>(__end_node())));
1432+
__size_ = __t.__size_;
13351433
}
13361434

13371435
template <class _Tp, class _Compare, class _Allocator>
@@ -1430,13 +1528,7 @@ __tree<_Tp, _Compare, _Allocator>::~__tree() {
14301528

14311529
template <class _Tp, class _Compare, class _Allocator>
14321530
void __tree<_Tp, _Compare, _Allocator>::destroy(__node_pointer __nd) _NOEXCEPT {
1433-
if (__nd != nullptr) {
1434-
destroy(static_cast<__node_pointer>(__nd->__left_));
1435-
destroy(static_cast<__node_pointer>(__nd->__right_));
1436-
__node_allocator& __na = __node_alloc();
1437-
__node_traits::destroy(__na, std::addressof(__nd->__value_));
1438-
__node_traits::deallocate(__na, __nd, 1);
1439-
}
1531+
(__tree_deleter(__node_alloc_))(__nd);
14401532
}
14411533

14421534
template <class _Tp, class _Compare, class _Allocator>

libcxx/include/map

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -970,7 +970,7 @@ public:
970970
: map(from_range, std::forward<_Range>(__range), key_compare(), __a) {}
971971
# endif
972972

973-
_LIBCPP_HIDE_FROM_ABI map(const map& __m) : __tree_(__m.__tree_) { insert(__m.begin(), __m.end()); }
973+
_LIBCPP_HIDE_FROM_ABI map(const map& __m) = default;
974974

975975
_LIBCPP_HIDE_FROM_ABI map& operator=(const map& __m) = default;
976976

@@ -1637,11 +1637,7 @@ public:
16371637
: multimap(from_range, std::forward<_Range>(__range), key_compare(), __a) {}
16381638
# endif
16391639

1640-
_LIBCPP_HIDE_FROM_ABI multimap(const multimap& __m)
1641-
: __tree_(__m.__tree_.value_comp(),
1642-
__alloc_traits::select_on_container_copy_construction(__m.__tree_.__alloc())) {
1643-
insert(__m.begin(), __m.end());
1644-
}
1640+
_LIBCPP_HIDE_FROM_ABI multimap(const multimap& __m) = default;
16451641

16461642
_LIBCPP_HIDE_FROM_ABI multimap& operator=(const multimap& __m) = default;
16471643

libcxx/include/set

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -660,7 +660,7 @@ public:
660660
: set(from_range, std::forward<_Range>(__range), key_compare(), __a) {}
661661
# endif
662662

663-
_LIBCPP_HIDE_FROM_ABI set(const set& __s) : __tree_(__s.__tree_) { insert(__s.begin(), __s.end()); }
663+
_LIBCPP_HIDE_FROM_ABI set(const set& __s) = default;
664664

665665
_LIBCPP_HIDE_FROM_ABI set& operator=(const set& __s) = default;
666666

@@ -1119,11 +1119,7 @@ public:
11191119
: multiset(from_range, std::forward<_Range>(__range), key_compare(), __a) {}
11201120
# endif
11211121

1122-
_LIBCPP_HIDE_FROM_ABI multiset(const multiset& __s)
1123-
: __tree_(__s.__tree_.value_comp(),
1124-
__alloc_traits::select_on_container_copy_construction(__s.__tree_.__alloc())) {
1125-
insert(__s.begin(), __s.end());
1126-
}
1122+
_LIBCPP_HIDE_FROM_ABI multiset(const multiset& __s) = default;
11271123

11281124
_LIBCPP_HIDE_FROM_ABI multiset& operator=(const multiset& __s) = default;
11291125

libcxx/test/benchmarks/containers/associative/associative_container_benchmarks.h

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ void associative_container_benchmarks(std::string container) {
151151
/////////////////////////
152152
// Assignment
153153
/////////////////////////
154-
bench("operator=(const&)", [=](auto& st) {
154+
bench("operator=(const&) (into cleared Container)", [=](auto& st) {
155155
const std::size_t size = st.range(0);
156156
std::vector<Value> in = make_value_types(generate_unique_keys(size));
157157
Container src(in.begin(), in.end());
@@ -172,6 +172,21 @@ void associative_container_benchmarks(std::string container) {
172172
}
173173
});
174174

175+
bench("operator=(const&) (into populated Container)", [=](auto& st) {
176+
const std::size_t size = st.range(0);
177+
std::vector<Value> in = make_value_types(generate_unique_keys(size));
178+
Container src(in.begin(), in.end());
179+
Container c[BatchSize];
180+
181+
while (st.KeepRunningBatch(BatchSize)) {
182+
for (std::size_t i = 0; i != BatchSize; ++i) {
183+
c[i] = src;
184+
benchmark::DoNotOptimize(c[i]);
185+
benchmark::ClobberMemory();
186+
}
187+
}
188+
});
189+
175190
/////////////////////////
176191
// Insertion
177192
/////////////////////////

libcxx/test/benchmarks/containers/associative/map.bench.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ struct support::adapt_operations<std::map<K, V>> {
2929

3030
int main(int argc, char** argv) {
3131
support::associative_container_benchmarks<std::map<int, int>>("std::map<int, int>");
32+
support::associative_container_benchmarks<std::map<std::string, int>>("std::map<std::string, int>");
3233

3334
benchmark::Initialize(&argc, argv);
3435
benchmark::RunSpecifiedBenchmarks();

0 commit comments

Comments
 (0)