Skip to content

Commit 01b932d

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

File tree

5 files changed

+136
-26
lines changed

5 files changed

+136
-26
lines changed

libcxx/include/__tree

Lines changed: 115 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1213,6 +1213,86 @@ 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+
class __allocate_node_builder {
1241+
__tree& __base_;
1242+
1243+
public:
1244+
__allocate_node_builder(__tree& __base) : __base_(__base) {}
1245+
1246+
template <class _Up>
1247+
__node_holder operator()(_Up&& __v) {
1248+
return __base_.__construct_node(std::forward<_Up>(__v));
1249+
}
1250+
};
1251+
1252+
class __cache_node_builder {
1253+
__tree& __base_;
1254+
_DetachedTreeCache& __cache_;
1255+
1256+
public:
1257+
__cache_node_builder(__tree& __base, _DetachedTreeCache& __cache) : __base_(__base), __cache_(__cache) {}
1258+
1259+
template <class _Up>
1260+
__node_holder operator()(_Up&& __v) {
1261+
__node_pointer __ret = __cache_.__get();
1262+
if (__ret) {
1263+
__assign_value(__ret->__value_, std::forward<_Up>(__v));
1264+
__cache_.__advance();
1265+
}
1266+
return __node_holder(__ret, _Dp(__base_.__node_alloc_));
1267+
}
1268+
};
1269+
1270+
template <class _NodeBuilder>
1271+
_LIBCPP_HIDE_FROM_ABI __node_pointer __copy_tree(_NodeBuilder& __builder, __node_pointer __src) {
1272+
if (!__src)
1273+
return nullptr;
1274+
1275+
__node_holder __new_node = __builder(__src->__value_);
1276+
if (!__new_node) {
1277+
__allocate_node_builder __alloc_builder(*this);
1278+
return __copy_tree(__alloc_builder, __src);
1279+
}
1280+
1281+
unique_ptr<__node, __tree_deleter> __left(
1282+
__copy_tree(__builder, static_cast<__node_pointer>(__src->__left_)), __node_alloc_);
1283+
__node_pointer __right = __copy_tree(__builder, static_cast<__node_pointer>(__src->__right_));
1284+
1285+
__node_pointer __new_node_ptr = __new_node.release();
1286+
1287+
__new_node_ptr->__is_black_ = __src->__is_black_;
1288+
__new_node_ptr->__left_ = static_cast<__node_base_pointer>(__left.release());
1289+
__new_node_ptr->__right_ = static_cast<__node_base_pointer>(__right);
1290+
if (__new_node_ptr->__left_)
1291+
__new_node_ptr->__left_->__parent_ = static_cast<__end_node_pointer>(__new_node_ptr);
1292+
if (__right)
1293+
__right->__parent_ = static_cast<__end_node_pointer>(__new_node_ptr);
1294+
return __new_node_ptr;
1295+
}
12161296
};
12171297

12181298
template <class _Tp, class _Compare, class _Allocator>
@@ -1277,11 +1357,31 @@ __tree<_Tp, _Compare, _Allocator>::_DetachedTreeCache::__detach_next(__node_poin
12771357

12781358
template <class _Tp, class _Compare, class _Allocator>
12791359
__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());
1360+
if (this == std::addressof(__t))
1361+
return *this;
1362+
1363+
value_comp() = __t.value_comp();
1364+
__copy_assign_alloc(__t);
1365+
1366+
if (__t.size() == 0) {
1367+
clear();
1368+
return *this;
1369+
}
1370+
1371+
if (__size_ != 0) {
1372+
_DetachedTreeCache __cache(this);
1373+
__cache_node_builder __builder(*this, __cache);
1374+
__end_node_.__left_ =
1375+
static_cast<__node_base_pointer>(__copy_tree(__builder, static_cast<__node_pointer>(__t.__end_node_.__left_)));
1376+
} else {
1377+
__allocate_node_builder __builder(*this);
1378+
__end_node_.__left_ =
1379+
static_cast<__node_base_pointer>(__copy_tree(__builder, static_cast<__node_pointer>(__t.__end_node_.__left_)));
12841380
}
1381+
__end_node_.__left_->__parent_ = __end_node();
1382+
__begin_node_ = static_cast<__end_node_pointer>(std::__tree_min(static_cast<__node_base_pointer>(__end_node())));
1383+
__size_ = __t.__size_;
1384+
12851385
return *this;
12861386
}
12871387

@@ -1327,11 +1427,19 @@ void __tree<_Tp, _Compare, _Allocator>::__assign_multi(_InputIterator __first, _
13271427

13281428
template <class _Tp, class _Compare, class _Allocator>
13291429
__tree<_Tp, _Compare, _Allocator>::__tree(const __tree& __t)
1330-
: __begin_node_(),
1430+
: __begin_node_(__end_node()),
13311431
__node_alloc_(__node_traits::select_on_container_copy_construction(__t.__node_alloc())),
13321432
__size_(0),
13331433
__value_comp_(__t.value_comp()) {
1334-
__begin_node_ = __end_node();
1434+
if (__t.__size_ == 0)
1435+
return;
1436+
1437+
__allocate_node_builder __builder(*this);
1438+
__end_node_.__left_ =
1439+
static_cast<__node_base_pointer>(__copy_tree(__builder, static_cast<__node_pointer>(__t.__end_node_.__left_)));
1440+
__end_node_.__left_->__parent_ = __end_node();
1441+
__begin_node_ = static_cast<__end_node_pointer>(std::__tree_min(static_cast<__node_base_pointer>(__end_node())));
1442+
__size_ = __t.__size_;
13351443
}
13361444

13371445
template <class _Tp, class _Compare, class _Allocator>
@@ -1430,13 +1538,7 @@ __tree<_Tp, _Compare, _Allocator>::~__tree() {
14301538

14311539
template <class _Tp, class _Compare, class _Allocator>
14321540
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-
}
1541+
(__tree_deleter(__node_alloc_))(__nd);
14401542
}
14411543

14421544
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)