diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst index 4ad5452cc29cc..93b745ac356fe 100644 --- a/libcxx/docs/ReleaseNotes/22.rst +++ b/libcxx/docs/ReleaseNotes/22.rst @@ -53,9 +53,10 @@ Improvements and New Features - The performance of ``find(key)`` in ``map``, ``set``, ``multimap`` and ``multiset`` has been improved by up to 2.3x - Some reallocations are now avoided in `std::filesystem::path::lexically_relative`, resulting in a performance improvement of up to 1.7x. -- The performance of the ``(iterator, iterator)`` constructors of ``multimap`` and ``multiset`` +- The performance of the ``(iterator, iterator)`` constructors of ``map``, ``set``, ``multimap`` and ``multiset`` has been improved by up to 3x -- The performance of ``insert(iterator, iterator)`` of ``multimap`` and ``multiset`` has been improved by up to 2.5x +- The performance of ``insert(iterator, iterator)`` of ``map``, ``set``, ``multimap`` and ``multiset`` has been improved + by up to 2.5x - The performance of ``erase(iterator, iterator)`` in the unordered containers has been improved by up to 1.9x - ``ofstream::write`` has been optimized to pass through large strings to system calls directly instead of copying them diff --git a/libcxx/include/__tree b/libcxx/include/__tree index 3ad2129ba9ddf..0ab79e1404afa 100644 --- a/libcxx/include/__tree +++ b/libcxx/include/__tree @@ -1025,6 +1025,58 @@ public: _LIBCPP_HIDE_FROM_ABI iterator __node_insert_multi(__node_pointer __nd); _LIBCPP_HIDE_FROM_ABI iterator __node_insert_multi(const_iterator __p, __node_pointer __nd); + template + _LIBCPP_HIDE_FROM_ABI void __insert_range_unique(_InIter __first, _Sent __last) { + if (__first == __last) + return; + + if (__root() == nullptr) { + __insert_node_at( + __end_node(), __end_node()->__left_, static_cast<__node_base_pointer>(__construct_node(*__first).release())); + ++__first; + } + + auto __max_node = static_cast<__node_pointer>(std::__tree_max(static_cast<__node_base_pointer>(__root()))); + + using __reference = decltype(*__first); + + for (; __first != __last; ++__first) { + std::__try_key_extraction( + [this, &__max_node](const key_type& __key, __reference&& __val) { + if (value_comp()(__max_node->__get_value(), __key)) { // __key > __max_node + __node_holder __nd = __construct_node(std::forward<__reference>(__val)); + __insert_node_at(static_cast<__end_node_pointer>(__max_node), + __max_node->__right_, + static_cast<__node_base_pointer>(__nd.get())); + __max_node = __nd.release(); + } else { + __end_node_pointer __parent; + __node_base_pointer& __child = __find_equal(__parent, __key); + if (__child == nullptr) { + __node_holder __nd = __construct_node(std::forward<__reference>(__val)); + __insert_node_at(__parent, __child, static_cast<__node_base_pointer>(__nd.release())); + } + } + }, + [this, &__max_node](__reference&& __val) { + __node_holder __nd = __construct_node(std::forward<__reference>(__val)); + if (value_comp()(__max_node->__get_value(), __nd->__get_value())) { // __node > __max_node + __insert_node_at(static_cast<__end_node_pointer>(__max_node), + __max_node->__right_, + static_cast<__node_base_pointer>(__nd.get())); + __max_node = __nd.release(); + } else { + __end_node_pointer __parent; + __node_base_pointer& __child = __find_equal(__parent, __nd->__get_value()); + if (__child == nullptr) { + __insert_node_at(__parent, __child, static_cast<__node_base_pointer>(__nd.release())); + } + } + }, + *__first); + } + } + _LIBCPP_HIDE_FROM_ABI iterator __remove_node_pointer(__node_pointer) _NOEXCEPT; #if _LIBCPP_STD_VER >= 17 diff --git a/libcxx/include/map b/libcxx/include/map index b53e1c4213487..8ba7719450f66 100644 --- a/libcxx/include/map +++ b/libcxx/include/map @@ -1089,18 +1089,14 @@ public: # endif template - _LIBCPP_HIDE_FROM_ABI void insert(_InputIterator __f, _InputIterator __l) { - for (const_iterator __e = cend(); __f != __l; ++__f) - insert(__e.__i_, *__f); + _LIBCPP_HIDE_FROM_ABI void insert(_InputIterator __first, _InputIterator __last) { + __tree_.__insert_range_unique(__first, __last); } # if _LIBCPP_STD_VER >= 23 template <_ContainerCompatibleRange _Range> _LIBCPP_HIDE_FROM_ABI void insert_range(_Range&& __range) { - const_iterator __end = cend(); - for (auto&& __element : __range) { - insert(__end.__i_, std::forward(__element)); - } + __tree_.__insert_range_unique(ranges::begin(__range), ranges::end(__range)); } # endif diff --git a/libcxx/include/set b/libcxx/include/set index b444e8357052a..6470894517fd8 100644 --- a/libcxx/include/set +++ b/libcxx/include/set @@ -742,18 +742,14 @@ public: } template - _LIBCPP_HIDE_FROM_ABI void insert(_InputIterator __f, _InputIterator __l) { - for (const_iterator __e = cend(); __f != __l; ++__f) - __tree_.__emplace_hint_unique(__e, *__f); + _LIBCPP_HIDE_FROM_ABI void insert(_InputIterator __first, _InputIterator __last) { + __tree_.__insert_range_unique(__first, __last); } # if _LIBCPP_STD_VER >= 23 template <_ContainerCompatibleRange _Range> _LIBCPP_HIDE_FROM_ABI void insert_range(_Range&& __range) { - const_iterator __end = cend(); - for (auto&& __element : __range) { - __tree_.__emplace_hint_unique(__end, std::forward(__element)); - } + __tree_.__insert_range_unique(ranges::begin(__range), ranges::end(__range)); } # endif diff --git a/libcxx/test/std/containers/associative/map/map.modifiers/insert_iter_iter.pass.cpp b/libcxx/test/std/containers/associative/map/map.modifiers/insert_iter_iter.pass.cpp index 0baff65f61938..dfbeb33698e6c 100644 --- a/libcxx/test/std/containers/associative/map/map.modifiers/insert_iter_iter.pass.cpp +++ b/libcxx/test/std/containers/associative/map/map.modifiers/insert_iter_iter.pass.cpp @@ -13,64 +13,188 @@ // template // void insert(InputIterator first, InputIterator last); -#include +#include #include +#include -#include "test_macros.h" #include "test_iterators.h" #include "min_allocator.h" -int main(int, char**) { - { - typedef std::map M; - typedef std::pair P; - P ar[] = { - P(1, 1), - P(1, 1.5), - P(1, 2), - P(2, 1), - P(2, 1.5), - P(2, 2), - P(3, 1), - P(3, 1.5), - P(3, 2), - }; - M m; - m.insert(cpp17_input_iterator(ar), cpp17_input_iterator(ar + sizeof(ar) / sizeof(ar[0]))); - assert(m.size() == 3); - assert(m.begin()->first == 1); - assert(m.begin()->second == 1); - assert(std::next(m.begin())->first == 2); - assert(std::next(m.begin())->second == 1); - assert(std::next(m.begin(), 2)->first == 3); - assert(std::next(m.begin(), 2)->second == 1); +template +void test_alloc() { + { // Check that an empty range works correctly + { // Without elements in the container + using Map = std::map, Alloc>; + + std::array, 0> arr; + + Map map; + map.insert(Iter(arr.data()), Iter(arr.data() + arr.size())); + assert(map.size() == 0); + assert(map.begin() == map.end()); + } + { // With 1 element in the container + using Map = std::map, Alloc>; + using Pair = std::pair; + + std::array arr; + + Map map; + map.insert(Pair(0, 0)); + map.insert(Iter(arr.data()), Iter(arr.data() + arr.size())); + assert(map.size() == 1); + assert(*std::next(map.begin(), 0) == Pair(0, 0)); + assert(std::next(map.begin(), 1) == map.end()); + } + { // With multiple elements in the container + using Map = std::map, Alloc>; + using Pair = std::pair; + + std::array arr; + + Map map; + map.insert(Pair(0, 0)); + map.insert(Pair(1, 1)); + map.insert(Pair(2, 2)); + map.insert(Iter(arr.data()), Iter(arr.data() + arr.size())); + assert(map.size() == 3); + assert(*std::next(map.begin(), 0) == Pair(0, 0)); + assert(*std::next(map.begin(), 1) == Pair(1, 1)); + assert(*std::next(map.begin(), 2) == Pair(2, 2)); + assert(std::next(map.begin(), 3) == map.end()); + } } -#if TEST_STD_VER >= 11 - { - typedef std::map, min_allocator>> M; - typedef std::pair P; - P ar[] = { - P(1, 1), - P(1, 1.5), - P(1, 2), - P(2, 1), - P(2, 1.5), - P(2, 2), - P(3, 1), - P(3, 1.5), - P(3, 2), - }; - M m; - m.insert(cpp17_input_iterator(ar), cpp17_input_iterator(ar + sizeof(ar) / sizeof(ar[0]))); - assert(m.size() == 3); - assert(m.begin()->first == 1); - assert(m.begin()->second == 1); - assert(std::next(m.begin())->first == 2); - assert(std::next(m.begin())->second == 1); - assert(std::next(m.begin(), 2)->first == 3); - assert(std::next(m.begin(), 2)->second == 1); + { // Check that 1 element is inserted correctly + { // Without elements in the container + using Map = std::map, Alloc>; + using Pair = std::pair; + + Pair arr[] = {Pair(1, 1)}; + + Map map; + map.insert(Iter(std::begin(arr)), Iter(std::end(arr))); + assert(map.size() == 1); + assert(*std::next(map.begin(), 0) == Pair(1, 1)); + assert(std::next(map.begin(), 1) == map.end()); + } + { // With 1 element in the container - a different key + using Map = std::map, Alloc>; + using Pair = std::pair; + + Pair arr[] = {Pair(1, 1)}; + + Map map; + map.insert(Pair(0, 0)); + map.insert(Iter(std::begin(arr)), Iter(std::end(arr))); + assert(map.size() == 2); + assert(*std::next(map.begin(), 0) == Pair(0, 0)); + assert(*std::next(map.begin(), 1) == Pair(1, 1)); + assert(std::next(map.begin(), 2) == map.end()); + } + { // With 1 element in the container - the same key + using Map = std::map, Alloc>; + using Pair = std::pair; + + Pair arr[] = {Pair(1, 1)}; + + Map map; + map.insert(Pair(1, 2)); + map.insert(Iter(std::begin(arr)), Iter(std::end(arr))); + assert(map.size() == 1); + assert(*std::next(map.begin(), 0) == Pair(1, 2)); + assert(std::next(map.begin(), 1) == map.end()); + } + { // With multiple elements in the container + using Map = std::map, Alloc>; + using Pair = std::pair; + + Pair arr[] = {Pair(1, 1)}; + + Map map; + map.insert(Pair(0, 0)); + map.insert(Pair(1, 1)); + map.insert(Pair(2, 2)); + map.insert(Iter(std::begin(arr)), Iter(std::end(arr))); + assert(map.size() == 3); + assert(*std::next(map.begin(), 0) == Pair(0, 0)); + assert(*std::next(map.begin(), 1) == Pair(1, 1)); + assert(*std::next(map.begin(), 2) == Pair(2, 2)); + assert(std::next(map.begin(), 3) == map.end()); + } } -#endif + { // Check that multiple elements are inserted correctly + { // Without elements in the container + using Map = std::map, Alloc>; + using Pair = std::pair; + + Pair arr[] = {Pair(1, 1), Pair(1, 1), Pair(3, 3)}; + + Map map; + map.insert(Iter(std::begin(arr)), Iter(std::end(arr))); + assert(map.size() == 2); + assert(*std::next(map.begin(), 0) == Pair(1, 1)); + assert(*std::next(map.begin(), 1) == Pair(3, 3)); + assert(std::next(map.begin(), 2) == map.end()); + } + { // With 1 element in the container - a different key + using Map = std::map, Alloc>; + using Pair = std::pair; + + Pair arr[] = {Pair(1, 1), Pair(1, 1), Pair(3, 3)}; + + Map map; + map.insert(Pair(0, 0)); + map.insert(Iter(std::begin(arr)), Iter(std::end(arr))); + assert(map.size() == 3); + assert(*std::next(map.begin(), 0) == Pair(0, 0)); + assert(*std::next(map.begin(), 1) == Pair(1, 1)); + assert(*std::next(map.begin(), 2) == Pair(3, 3)); + assert(std::next(map.begin(), 3) == map.end()); + } + { // With 1 element in the container - the same key + using Map = std::map, Alloc>; + using Pair = std::pair; + + Pair arr[] = {Pair(1, 1), Pair(2, 2), Pair(3, 3)}; + + Map map; + map.insert(Pair(1, 1)); + map.insert(Iter(std::begin(arr)), Iter(std::end(arr))); + assert(map.size() == 3); + assert(*std::next(map.begin(), 0) == Pair(1, 1)); + assert(*std::next(map.begin(), 1) == Pair(2, 2)); + assert(*std::next(map.begin(), 2) == Pair(3, 3)); + assert(std::next(map.begin(), 3) == map.end()); + } + { // With multiple elements in the container + using Map = std::map, Alloc>; + using Pair = std::pair; + + Pair arr[] = {Pair(1, 1), Pair(3, 3), Pair(4, 4)}; + + Map map; + map.insert(Pair(0, 0)); + map.insert(Pair(1, 1)); + map.insert(Pair(2, 2)); + map.insert(Iter(std::begin(arr)), Iter(std::end(arr))); + assert(map.size() == 5); + assert(*std::next(map.begin(), 0) == Pair(0, 0)); + assert(*std::next(map.begin(), 1) == Pair(1, 1)); + assert(*std::next(map.begin(), 2) == Pair(2, 2)); + assert(*std::next(map.begin(), 3) == Pair(3, 3)); + assert(*std::next(map.begin(), 4) == Pair(4, 4)); + assert(std::next(map.begin(), 5) == map.end()); + } + } +} + +void test() { + test_alloc*>, std::allocator > >(); + test_alloc*>, min_allocator > >(); +} + +int main(int, char**) { + test(); return 0; } diff --git a/libcxx/test/std/containers/associative/set/insert_iter_iter.pass.cpp b/libcxx/test/std/containers/associative/set/insert_iter_iter.pass.cpp index 6c9707ca31a81..8576c63a72de9 100644 --- a/libcxx/test/std/containers/associative/set/insert_iter_iter.pass.cpp +++ b/libcxx/test/std/containers/associative/set/insert_iter_iter.pass.cpp @@ -13,38 +13,178 @@ // template // void insert(InputIterator first, InputIterator last); -#include +#include #include +#include -#include "test_macros.h" -#include "test_iterators.h" #include "min_allocator.h" +#include "test_iterators.h" -int main(int, char**) { - { - typedef std::set M; - typedef int V; - V ar[] = {1, 1, 1, 2, 2, 2, 3, 3, 3}; - M m; - m.insert(cpp17_input_iterator(ar), cpp17_input_iterator(ar + sizeof(ar) / sizeof(ar[0]))); - assert(m.size() == 3); - assert(*m.begin() == 1); - assert(*std::next(m.begin()) == 2); - assert(*std::next(m.begin(), 2) == 3); +template +void test_alloc() { + { // Check that an empty range works correctly + { // Without elements in the container + using Map = std::set, Alloc>; + + std::array arr; + + Map map; + map.insert(Iter(arr.data()), Iter(arr.data() + arr.size())); + assert(map.size() == 0); + assert(map.begin() == map.end()); + } + { // With 1 element in the container + using Map = std::set, Alloc>; + + std::array arr; + + Map map; + map.insert(0); + map.insert(Iter(arr.data()), Iter(arr.data() + arr.size())); + assert(map.size() == 1); + assert(*std::next(map.begin(), 0) == 0); + assert(std::next(map.begin(), 1) == map.end()); + } + { // With multiple elements in the container + using Map = std::set, Alloc>; + + std::array arr; + + Map map; + map.insert(0); + map.insert(1); + map.insert(2); + map.insert(Iter(arr.data()), Iter(arr.data() + arr.size())); + assert(map.size() == 3); + assert(*std::next(map.begin(), 0) == 0); + assert(*std::next(map.begin(), 1) == 1); + assert(*std::next(map.begin(), 2) == 2); + assert(std::next(map.begin(), 3) == map.end()); + } } -#if TEST_STD_VER >= 11 - { - typedef std::set, min_allocator> M; - typedef int V; - V ar[] = {1, 1, 1, 2, 2, 2, 3, 3, 3}; - M m; - m.insert(cpp17_input_iterator(ar), cpp17_input_iterator(ar + sizeof(ar) / sizeof(ar[0]))); - assert(m.size() == 3); - assert(*m.begin() == 1); - assert(*std::next(m.begin()) == 2); - assert(*std::next(m.begin(), 2) == 3); + { // Check that 1 element is inserted correctly + { // Without elements in the container + using Map = std::set, Alloc>; + + int arr[] = {1}; + + Map map; + map.insert(Iter(std::begin(arr)), Iter(std::end(arr))); + assert(map.size() == 1); + assert(*std::next(map.begin(), 0) == 1); + assert(std::next(map.begin(), 1) == map.end()); + } + { // With 1 element in the container - a different key + using Map = std::set, Alloc>; + + int arr[] = {1}; + + Map map; + map.insert(0); + map.insert(Iter(std::begin(arr)), Iter(std::end(arr))); + assert(map.size() == 2); + assert(*std::next(map.begin(), 0) == 0); + assert(*std::next(map.begin(), 1) == 1); + assert(std::next(map.begin(), 2) == map.end()); + } + { // With 1 element in the container - the same key + using Map = std::set, Alloc>; + + int arr[] = {1}; + + Map map; + map.insert(1); + map.insert(Iter(std::begin(arr)), Iter(std::end(arr))); + assert(map.size() == 1); + assert(*std::next(map.begin(), 0) == 1); + assert(std::next(map.begin(), 1) == map.end()); + } + { // With multiple elements in the container + using Map = std::set, Alloc>; + + int arr[] = {1}; + + Map map; + map.insert(0); + map.insert(1); + map.insert(2); + map.insert(Iter(std::begin(arr)), Iter(std::end(arr))); + assert(map.size() == 3); + assert(*std::next(map.begin(), 0) == 0); + assert(*std::next(map.begin(), 1) == 1); + assert(*std::next(map.begin(), 2) == 2); + assert(std::next(map.begin(), 3) == map.end()); + } + } + { // Check that multiple elements are inserted correctly + { // Without elements in the container + using Map = std::set, Alloc>; + + int arr[] = {1, 1, 3}; + + Map map; + map.insert(Iter(std::begin(arr)), Iter(std::end(arr))); + assert(map.size() == 2); + assert(*std::next(map.begin(), 0) == 1); + assert(*std::next(map.begin(), 1) == 3); + assert(std::next(map.begin(), 2) == map.end()); + } + { // With 1 element in the container - a different key + using Map = std::set, Alloc>; + + int arr[] = {1, 1, 3}; + + Map map; + map.insert(0); + map.insert(Iter(std::begin(arr)), Iter(std::end(arr))); + assert(map.size() == 3); + assert(*std::next(map.begin(), 0) == 0); + assert(*std::next(map.begin(), 1) == 1); + assert(*std::next(map.begin(), 2) == 3); + assert(std::next(map.begin(), 3) == map.end()); + } + { // With 1 element in the container - the same key + using Map = std::set, Alloc>; + + int arr[] = {1, 2, 3}; + + Map map; + map.insert(1); + map.insert(Iter(std::begin(arr)), Iter(std::end(arr))); + assert(map.size() == 3); + assert(*std::next(map.begin(), 0) == 1); + assert(*std::next(map.begin(), 1) == 2); + assert(*std::next(map.begin(), 2) == 3); + assert(std::next(map.begin(), 3) == map.end()); + } + { // With multiple elements in the container + using Map = std::set, Alloc>; + + int arr[] = {1, 3, 4}; + + Map map; + map.insert(0); + map.insert(1); + map.insert(2); + map.insert(Iter(std::begin(arr)), Iter(std::end(arr))); + assert(map.size() == 5); + assert(*std::next(map.begin(), 0) == 0); + assert(*std::next(map.begin(), 1) == 1); + assert(*std::next(map.begin(), 2) == 2); + assert(*std::next(map.begin(), 3) == 3); + assert(*std::next(map.begin(), 4) == 4); + assert(std::next(map.begin(), 5) == map.end()); + } } -#endif +} + +void test() { + test_alloc, std::allocator >(); + test_alloc, min_allocator >(); +} + +int main(int, char**) { + test(); return 0; }