Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions libcxx/docs/ReleaseNotes/22.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
52 changes: 52 additions & 0 deletions libcxx/include/__tree
Original file line number Diff line number Diff line change
Expand Up @@ -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 <class _InIter, class _Sent>
_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<key_type>(
[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
Expand Down
10 changes: 3 additions & 7 deletions libcxx/include/map
Original file line number Diff line number Diff line change
Expand Up @@ -1089,18 +1089,14 @@ public:
# endif

template <class _InputIterator>
_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<value_type> _Range>
_LIBCPP_HIDE_FROM_ABI void insert_range(_Range&& __range) {
const_iterator __end = cend();
for (auto&& __element : __range) {
insert(__end.__i_, std::forward<decltype(__element)>(__element));
}
__tree_.__insert_range_unique(ranges::begin(__range), ranges::end(__range));
}
# endif

Expand Down
10 changes: 3 additions & 7 deletions libcxx/include/set
Original file line number Diff line number Diff line change
Expand Up @@ -742,18 +742,14 @@ public:
}

template <class _InputIterator>
_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<value_type> _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<decltype(__element)>(__element));
}
__tree_.__insert_range_unique(ranges::begin(__range), ranges::end(__range));
}
# endif

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,64 +13,188 @@
// template <class InputIterator>
// void insert(InputIterator first, InputIterator last);

#include <map>
#include <array>
#include <cassert>
#include <map>

#include "test_macros.h"
#include "test_iterators.h"
#include "min_allocator.h"

int main(int, char**) {
{
typedef std::map<int, double> M;
typedef std::pair<int, double> 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<P*>(ar), cpp17_input_iterator<P*>(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 <class Iter, class Alloc>
void test_alloc() {
{ // Check that an empty range works correctly
{ // Without elements in the container
using Map = std::map<int, int, std::less<int>, Alloc>;

std::array<std::pair<const int, int>, 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<int, int, std::less<int>, Alloc>;
using Pair = std::pair<const int, int>;

std::array<Pair, 0> 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<int, int, std::less<int>, Alloc>;
using Pair = std::pair<const int, int>;

std::array<Pair, 0> 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<int, double, std::less<int>, min_allocator<std::pair<const int, double>>> M;
typedef std::pair<int, double> 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<P*>(ar), cpp17_input_iterator<P*>(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<int, int, std::less<int>, Alloc>;
using Pair = std::pair<const int, int>;

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<int, int, std::less<int>, Alloc>;
using Pair = std::pair<const int, int>;

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<int, int, std::less<int>, Alloc>;
using Pair = std::pair<const int, int>;

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<int, int, std::less<int>, Alloc>;
using Pair = std::pair<const int, int>;

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<int, int, std::less<int>, Alloc>;
using Pair = std::pair<const int, int>;

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<int, int, std::less<int>, Alloc>;
using Pair = std::pair<const int, int>;

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<int, int, std::less<int>, Alloc>;
using Pair = std::pair<const int, int>;

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<int, int, std::less<int>, Alloc>;
using Pair = std::pair<const int, int>;

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<cpp17_input_iterator<std::pair<const int, int>*>, std::allocator<std::pair<const int, int> > >();
test_alloc<cpp17_input_iterator<std::pair<const int, int>*>, min_allocator<std::pair<const int, int> > >();
}

int main(int, char**) {
test();

return 0;
}
Loading
Loading