[libc++] Optimize __tree::find and __tree::__erase_unique (#152370)
This patch changes `__tree::find` to return when it has found any equal element instead of the lower bound of the equal elements. For `map` and `set` there is no observable difference, since the keys are unique. However for their `multi` versions this can mean a change in behaviour since it's not longer guaranteed that `find` will return the first element. ``` ------------------------------------------------------------------------------------------ Benchmark old new ------------------------------------------------------------------------------------------ std::map<int, int>::erase(key) (existent)/0 24.4 ns 24.9 ns std::map<int, int>::erase(key) (existent)/32 39.8 ns 32.1 ns std::map<int, int>::erase(key) (existent)/1024 83.8 ns 52.5 ns std::map<int, int>::erase(key) (existent)/8192 91.4 ns 66.4 ns std::map<int, int>::erase(key) (non-existent)/0 0.511 ns 0.328 ns std::map<int, int>::erase(key) (non-existent)/32 9.12 ns 5.62 ns std::map<int, int>::erase(key) (non-existent)/1024 26.6 ns 11.3 ns std::map<int, int>::erase(key) (non-existent)/8192 37.0 ns 16.9 ns std::map<int, int>::find(key) (existent)/0 0.007 ns 0.007 ns std::map<int, int>::find(key) (existent)/32 6.02 ns 4.32 ns std::map<int, int>::find(key) (existent)/1024 13.6 ns 8.35 ns std::map<int, int>::find(key) (existent)/8192 30.3 ns 12.8 ns std::map<int, int>::find(key) (non-existent)/0 0.299 ns 0.545 ns std::map<int, int>::find(key) (non-existent)/32 8.78 ns 4.60 ns std::map<int, int>::find(key) (non-existent)/1024 26.1 ns 21.8 ns std::map<int, int>::find(key) (non-existent)/8192 36.2 ns 27.9 ns std::map<std::string, int>::erase(key) (existent)/0 74.1 ns 76.7 ns std::map<std::string, int>::erase(key) (existent)/32 161 ns 114 ns std::map<std::string, int>::erase(key) (existent)/1024 196 ns 126 ns std::map<std::string, int>::erase(key) (existent)/8192 207 ns 160 ns std::map<std::string, int>::erase(key) (non-existent)/0 0.754 ns 0.328 ns std::map<std::string, int>::erase(key) (non-existent)/32 47.3 ns 40.7 ns std::map<std::string, int>::erase(key) (non-existent)/1024 122 ns 96.1 ns std::map<std::string, int>::erase(key) (non-existent)/8192 168 ns 123 ns std::map<std::string, int>::find(key) (existent)/0 0.059 ns 0.058 ns std::map<std::string, int>::find(key) (existent)/32 54.3 ns 34.6 ns std::map<std::string, int>::find(key) (existent)/1024 125 ns 64.5 ns std::map<std::string, int>::find(key) (existent)/8192 159 ns 79.2 ns std::map<std::string, int>::find(key) (non-existent)/0 0.311 ns 0.299 ns std::map<std::string, int>::find(key) (non-existent)/32 44.0 ns 42.7 ns std::map<std::string, int>::find(key) (non-existent)/1024 120 ns 92.6 ns std::map<std::string, int>::find(key) (non-existent)/8192 189 ns 124 ns std::set<int>::erase(key) (existent)/0 25.1 ns 25.1 ns std::set<int>::erase(key) (existent)/32 42.1 ns 33.1 ns std::set<int>::erase(key) (existent)/1024 73.8 ns 55.5 ns std::set<int>::erase(key) (existent)/8192 101 ns 68.8 ns std::set<int>::erase(key) (non-existent)/0 0.511 ns 0.328 ns std::set<int>::erase(key) (non-existent)/32 9.60 ns 4.67 ns std::set<int>::erase(key) (non-existent)/1024 26.5 ns 11.2 ns std::set<int>::erase(key) (non-existent)/8192 46.2 ns 16.8 ns std::set<int>::find(key) (existent)/0 0.008 ns 0.007 ns std::set<int>::find(key) (existent)/32 5.87 ns 4.51 ns std::set<int>::find(key) (existent)/1024 14.3 ns 8.69 ns std::set<int>::find(key) (existent)/8192 30.2 ns 12.8 ns std::set<int>::find(key) (non-existent)/0 0.531 ns 0.530 ns std::set<int>::find(key) (non-existent)/32 8.77 ns 4.64 ns std::set<int>::find(key) (non-existent)/1024 26.1 ns 21.7 ns std::set<int>::find(key) (non-existent)/8192 36.3 ns 27.8 ns std::set<std::string>::erase(key) (existent)/0 93.2 ns 70.2 ns std::set<std::string>::erase(key) (existent)/32 164 ns 116 ns std::set<std::string>::erase(key) (existent)/1024 161 ns 136 ns std::set<std::string>::erase(key) (existent)/8192 231 ns 140 ns std::set<std::string>::erase(key) (non-existent)/0 0.532 ns 0.326 ns std::set<std::string>::erase(key) (non-existent)/32 43.4 ns 40.1 ns std::set<std::string>::erase(key) (non-existent)/1024 122 ns 99.5 ns std::set<std::string>::erase(key) (non-existent)/8192 168 ns 125 ns std::set<std::string>::find(key) (existent)/0 0.059 ns 0.059 ns std::set<std::string>::find(key) (existent)/32 53.1 ns 35.5 ns std::set<std::string>::find(key) (existent)/1024 124 ns 61.2 ns std::set<std::string>::find(key) (existent)/8192 154 ns 73.9 ns std::set<std::string>::find(key) (non-existent)/0 0.532 ns 0.301 ns std::set<std::string>::find(key) (non-existent)/32 44.4 ns 39.5 ns std::set<std::string>::find(key) (non-existent)/1024 120 ns 95.5 ns std::set<std::string>::find(key) (non-existent)/8192 193 ns 119 ns std::multimap<int, int>::erase(key) (existent)/0 26.5 ns 26.6 ns std::multimap<int, int>::erase(key) (existent)/32 33.5 ns 32.9 ns std::multimap<int, int>::erase(key) (existent)/1024 55.5 ns 58.0 ns std::multimap<int, int>::erase(key) (existent)/8192 67.4 ns 70.0 ns std::multimap<int, int>::erase(key) (non-existent)/0 0.523 ns 0.532 ns std::multimap<int, int>::erase(key) (non-existent)/32 5.08 ns 5.09 ns std::multimap<int, int>::erase(key) (non-existent)/1024 13.0 ns 12.9 ns std::multimap<int, int>::erase(key) (non-existent)/8192 19.6 ns 19.8 ns std::multimap<int, int>::find(key) (existent)/0 0.015 ns 0.037 ns std::multimap<int, int>::find(key) (existent)/32 7.07 ns 3.85 ns std::multimap<int, int>::find(key) (existent)/1024 22.0 ns 7.44 ns std::multimap<int, int>::find(key) (existent)/8192 37.6 ns 12.0 ns std::multimap<int, int>::find(key) (non-existent)/0 0.297 ns 0.305 ns std::multimap<int, int>::find(key) (non-existent)/32 8.79 ns 4.59 ns std::multimap<int, int>::find(key) (non-existent)/1024 26.0 ns 11.2 ns std::multimap<int, int>::find(key) (non-existent)/8192 36.4 ns 16.8 ns std::multimap<std::string, int>::erase(key) (existent)/0 93.4 ns 84.5 ns std::multimap<std::string, int>::erase(key) (existent)/32 101 ns 101 ns std::multimap<std::string, int>::erase(key) (existent)/1024 118 ns 126 ns std::multimap<std::string, int>::erase(key) (existent)/8192 108 ns 124 ns std::multimap<std::string, int>::erase(key) (non-existent)/0 2.39 ns 2.43 ns std::multimap<std::string, int>::erase(key) (non-existent)/32 44.4 ns 49.7 ns std::multimap<std::string, int>::erase(key) (non-existent)/1024 108 ns 103 ns std::multimap<std::string, int>::erase(key) (non-existent)/8192 140 ns 125 ns std::multimap<std::string, int>::find(key) (existent)/0 0.059 ns 0.058 ns std::multimap<std::string, int>::find(key) (existent)/32 52.3 ns 32.6 ns std::multimap<std::string, int>::find(key) (existent)/1024 122 ns 58.9 ns std::multimap<std::string, int>::find(key) (existent)/8192 160 ns 72.7 ns std::multimap<std::string, int>::find(key) (non-existent)/0 0.524 ns 0.494 ns std::multimap<std::string, int>::find(key) (non-existent)/32 43.8 ns 38.9 ns std::multimap<std::string, int>::find(key) (non-existent)/1024 123 ns 90.8 ns std::multimap<std::string, int>::find(key) (non-existent)/8192 190 ns 126 ns std::multiset<int>::erase(key) (existent)/0 27.1 ns 26.8 ns std::multiset<int>::erase(key) (existent)/32 33.3 ns 34.1 ns std::multiset<int>::erase(key) (existent)/1024 58.5 ns 58.8 ns std::multiset<int>::erase(key) (existent)/8192 66.7 ns 64.1 ns std::multiset<int>::erase(key) (non-existent)/0 0.318 ns 0.325 ns std::multiset<int>::erase(key) (non-existent)/32 5.15 ns 5.25 ns std::multiset<int>::erase(key) (non-existent)/1024 12.9 ns 12.7 ns std::multiset<int>::erase(key) (non-existent)/8192 20.3 ns 20.3 ns std::multiset<int>::find(key) (existent)/0 0.043 ns 0.015 ns std::multiset<int>::find(key) (existent)/32 6.94 ns 4.22 ns std::multiset<int>::find(key) (existent)/1024 21.4 ns 8.23 ns std::multiset<int>::find(key) (existent)/8192 37.4 ns 12.6 ns std::multiset<int>::find(key) (non-existent)/0 0.515 ns 0.300 ns std::multiset<int>::find(key) (non-existent)/32 8.52 ns 4.62 ns std::multiset<int>::find(key) (non-existent)/1024 25.5 ns 11.3 ns std::multiset<int>::find(key) (non-existent)/8192 36.5 ns 27.0 ns std::multiset<std::string>::erase(key) (existent)/0 81.9 ns 77.5 ns std::multiset<std::string>::erase(key) (existent)/32 113 ns 129 ns std::multiset<std::string>::erase(key) (existent)/1024 132 ns 148 ns std::multiset<std::string>::erase(key) (existent)/8192 114 ns 165 ns std::multiset<std::string>::erase(key) (non-existent)/0 2.33 ns 2.32 ns std::multiset<std::string>::erase(key) (non-existent)/32 44.4 ns 42.0 ns std::multiset<std::string>::erase(key) (non-existent)/1024 97.3 ns 95.1 ns std::multiset<std::string>::erase(key) (non-existent)/8192 132 ns 123 ns std::multiset<std::string>::find(key) (existent)/0 0.058 ns 0.059 ns std::multiset<std::string>::find(key) (existent)/32 48.3 ns 34.4 ns std::multiset<std::string>::find(key) (existent)/1024 121 ns 61.9 ns std::multiset<std::string>::find(key) (existent)/8192 155 ns 77.7 ns std::multiset<std::string>::find(key) (non-existent)/0 0.524 ns 0.306 ns std::multiset<std::string>::find(key) (non-existent)/32 44.1 ns 40.4 ns std::multiset<std::string>::find(key) (non-existent)/1024 121 ns 96.3 ns std::multiset<std::string>::find(key) (non-existent)/8192 193 ns 121 ns ```
This commit is contained in:
parent
cbbf303ff5
commit
d9bc548fac
@ -47,6 +47,8 @@ Improvements and New Features
|
||||
- The performance of ``map::operator=(const map&)`` has been improved by up to 11x
|
||||
- The performance of ``unordered_set::unordered_set(const unordered_set&)`` has been improved by up to 3.3x.
|
||||
- The performance of ``unordered_set::operator=(const unordered_set&)`` has been improved by up to 5x.
|
||||
- The performance of ``map::erase`` and ``set::erase`` has been improved by up to 2x
|
||||
- The performance of ``find(key)`` in ``map``, ``set``, ``multimap`` and ``multiset`` has been improved by up to 2.3x
|
||||
|
||||
Deprecations and Removals
|
||||
-------------------------
|
||||
|
@ -1038,9 +1038,22 @@ public:
|
||||
__insert_node_at(__end_node_pointer __parent, __node_base_pointer& __child, __node_base_pointer __new_node) _NOEXCEPT;
|
||||
|
||||
template <class _Key>
|
||||
_LIBCPP_HIDE_FROM_ABI iterator find(const _Key& __v);
|
||||
_LIBCPP_HIDE_FROM_ABI iterator find(const _Key& __key) {
|
||||
__end_node_pointer __parent;
|
||||
__node_base_pointer __match = __find_equal(__parent, __key);
|
||||
if (__match == nullptr)
|
||||
return end();
|
||||
return iterator(static_cast<__node_pointer>(__match));
|
||||
}
|
||||
|
||||
template <class _Key>
|
||||
_LIBCPP_HIDE_FROM_ABI const_iterator find(const _Key& __v) const;
|
||||
_LIBCPP_HIDE_FROM_ABI const_iterator find(const _Key& __key) const {
|
||||
__end_node_pointer __parent;
|
||||
__node_base_pointer __match = __find_equal(__parent, __key);
|
||||
if (__match == nullptr)
|
||||
return end();
|
||||
return const_iterator(static_cast<__node_pointer>(__match));
|
||||
}
|
||||
|
||||
template <class _Key>
|
||||
_LIBCPP_HIDE_FROM_ABI size_type __count_unique(const _Key& __k) const;
|
||||
@ -2060,25 +2073,6 @@ __tree<_Tp, _Compare, _Allocator>::__erase_multi(const _Key& __k) {
|
||||
return __r;
|
||||
}
|
||||
|
||||
template <class _Tp, class _Compare, class _Allocator>
|
||||
template <class _Key>
|
||||
typename __tree<_Tp, _Compare, _Allocator>::iterator __tree<_Tp, _Compare, _Allocator>::find(const _Key& __v) {
|
||||
iterator __p = __lower_bound(__v, __root(), __end_node());
|
||||
if (__p != end() && !value_comp()(__v, *__p))
|
||||
return __p;
|
||||
return end();
|
||||
}
|
||||
|
||||
template <class _Tp, class _Compare, class _Allocator>
|
||||
template <class _Key>
|
||||
typename __tree<_Tp, _Compare, _Allocator>::const_iterator
|
||||
__tree<_Tp, _Compare, _Allocator>::find(const _Key& __v) const {
|
||||
const_iterator __p = __lower_bound(__v, __root(), __end_node());
|
||||
if (__p != end() && !value_comp()(__v, *__p))
|
||||
return __p;
|
||||
return end();
|
||||
}
|
||||
|
||||
template <class _Tp, class _Compare, class _Allocator>
|
||||
template <class _Key>
|
||||
typename __tree<_Tp, _Compare, _Allocator>::size_type
|
||||
|
@ -21,6 +21,15 @@
|
||||
#include "private_constructor.h"
|
||||
#include "is_transparent.h"
|
||||
|
||||
template <class Iter>
|
||||
bool iter_in_range(Iter first, Iter last, Iter to_find) {
|
||||
for (; first != last; ++first) {
|
||||
if (first == to_find)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
typedef std::pair<const int, double> V;
|
||||
{
|
||||
@ -30,15 +39,15 @@ int main(int, char**) {
|
||||
V ar[] = {V(5, 1), V(5, 2), V(5, 3), V(7, 1), V(7, 2), V(7, 3), V(9, 1), V(9, 2), V(9, 3)};
|
||||
M m(ar, ar + sizeof(ar) / sizeof(ar[0]));
|
||||
R r = m.find(5);
|
||||
assert(r == m.begin());
|
||||
assert(iter_in_range(std::next(m.begin(), 0), std::next(m.begin(), 3), r));
|
||||
r = m.find(6);
|
||||
assert(r == m.end());
|
||||
r = m.find(7);
|
||||
assert(r == std::next(m.begin(), 3));
|
||||
assert(iter_in_range(std::next(m.begin(), 3), std::next(m.begin(), 6), r));
|
||||
r = m.find(8);
|
||||
assert(r == m.end());
|
||||
r = m.find(9);
|
||||
assert(r == std::next(m.begin(), 6));
|
||||
assert(iter_in_range(std::next(m.begin(), 6), std::next(m.begin(), 9), r));
|
||||
r = m.find(10);
|
||||
assert(r == m.end());
|
||||
}
|
||||
@ -47,15 +56,15 @@ int main(int, char**) {
|
||||
V ar[] = {V(5, 1), V(5, 2), V(5, 3), V(7, 1), V(7, 2), V(7, 3), V(9, 1), V(9, 2), V(9, 3)};
|
||||
const M m(ar, ar + sizeof(ar) / sizeof(ar[0]));
|
||||
R r = m.find(5);
|
||||
assert(r == m.begin());
|
||||
assert(iter_in_range(std::next(m.begin(), 0), std::next(m.begin(), 3), r));
|
||||
r = m.find(6);
|
||||
assert(r == m.end());
|
||||
r = m.find(7);
|
||||
assert(r == std::next(m.begin(), 3));
|
||||
assert(iter_in_range(std::next(m.begin(), 3), std::next(m.begin(), 6), r));
|
||||
r = m.find(8);
|
||||
assert(r == m.end());
|
||||
r = m.find(9);
|
||||
assert(r == std::next(m.begin(), 6));
|
||||
assert(iter_in_range(std::next(m.begin(), 6), std::next(m.begin(), 9), r));
|
||||
r = m.find(10);
|
||||
assert(r == m.end());
|
||||
}
|
||||
@ -68,15 +77,15 @@ int main(int, char**) {
|
||||
V ar[] = {V(5, 1), V(5, 2), V(5, 3), V(7, 1), V(7, 2), V(7, 3), V(9, 1), V(9, 2), V(9, 3)};
|
||||
M m(ar, ar + sizeof(ar) / sizeof(ar[0]));
|
||||
R r = m.find(5);
|
||||
assert(r == m.begin());
|
||||
assert(iter_in_range(std::next(m.begin(), 0), std::next(m.begin(), 3), r));
|
||||
r = m.find(6);
|
||||
assert(r == m.end());
|
||||
r = m.find(7);
|
||||
assert(r == std::next(m.begin(), 3));
|
||||
assert(iter_in_range(std::next(m.begin(), 3), std::next(m.begin(), 6), r));
|
||||
r = m.find(8);
|
||||
assert(r == m.end());
|
||||
r = m.find(9);
|
||||
assert(r == std::next(m.begin(), 6));
|
||||
assert(iter_in_range(std::next(m.begin(), 6), std::next(m.begin(), 9), r));
|
||||
r = m.find(10);
|
||||
assert(r == m.end());
|
||||
}
|
||||
@ -85,15 +94,15 @@ int main(int, char**) {
|
||||
V ar[] = {V(5, 1), V(5, 2), V(5, 3), V(7, 1), V(7, 2), V(7, 3), V(9, 1), V(9, 2), V(9, 3)};
|
||||
const M m(ar, ar + sizeof(ar) / sizeof(ar[0]));
|
||||
R r = m.find(5);
|
||||
assert(r == m.begin());
|
||||
assert(iter_in_range(std::next(m.begin(), 0), std::next(m.begin(), 3), r));
|
||||
r = m.find(6);
|
||||
assert(r == m.end());
|
||||
r = m.find(7);
|
||||
assert(r == std::next(m.begin(), 3));
|
||||
assert(iter_in_range(std::next(m.begin(), 3), std::next(m.begin(), 6), r));
|
||||
r = m.find(8);
|
||||
assert(r == m.end());
|
||||
r = m.find(9);
|
||||
assert(r == std::next(m.begin(), 6));
|
||||
assert(iter_in_range(std::next(m.begin(), 6), std::next(m.begin(), 9), r));
|
||||
r = m.find(10);
|
||||
assert(r == m.end());
|
||||
}
|
||||
@ -107,28 +116,28 @@ int main(int, char**) {
|
||||
V ar[] = {V(5, 1), V(5, 2), V(5, 3), V(7, 1), V(7, 2), V(7, 3), V(9, 1), V(9, 2), V(9, 3)};
|
||||
M m(ar, ar + sizeof(ar) / sizeof(ar[0]));
|
||||
R r = m.find(5);
|
||||
assert(r == m.begin());
|
||||
assert(iter_in_range(std::next(m.begin(), 0), std::next(m.begin(), 3), r));
|
||||
r = m.find(6);
|
||||
assert(r == m.end());
|
||||
r = m.find(7);
|
||||
assert(r == std::next(m.begin(), 3));
|
||||
assert(iter_in_range(std::next(m.begin(), 3), std::next(m.begin(), 6), r));
|
||||
r = m.find(8);
|
||||
assert(r == m.end());
|
||||
r = m.find(9);
|
||||
assert(r == std::next(m.begin(), 6));
|
||||
assert(iter_in_range(std::next(m.begin(), 6), std::next(m.begin(), 9), r));
|
||||
r = m.find(10);
|
||||
assert(r == m.end());
|
||||
|
||||
r = m.find(C2Int(5));
|
||||
assert(r == m.begin());
|
||||
assert(iter_in_range(std::next(m.begin(), 0), std::next(m.begin(), 3), r));
|
||||
r = m.find(C2Int(6));
|
||||
assert(r == m.end());
|
||||
r = m.find(C2Int(7));
|
||||
assert(r == std::next(m.begin(), 3));
|
||||
assert(iter_in_range(std::next(m.begin(), 3), std::next(m.begin(), 6), r));
|
||||
r = m.find(C2Int(8));
|
||||
assert(r == m.end());
|
||||
r = m.find(C2Int(9));
|
||||
assert(r == std::next(m.begin(), 6));
|
||||
assert(iter_in_range(std::next(m.begin(), 6), std::next(m.begin(), 9), r));
|
||||
r = m.find(C2Int(10));
|
||||
assert(r == m.end());
|
||||
}
|
||||
@ -150,15 +159,15 @@ int main(int, char**) {
|
||||
m.insert(std::make_pair<PC, double>(PC::make(9), 3));
|
||||
|
||||
R r = m.find(5);
|
||||
assert(r == m.begin());
|
||||
assert(iter_in_range(std::next(m.begin(), 0), std::next(m.begin(), 3), r));
|
||||
r = m.find(6);
|
||||
assert(r == m.end());
|
||||
r = m.find(7);
|
||||
assert(r == std::next(m.begin(), 3));
|
||||
assert(iter_in_range(std::next(m.begin(), 3), std::next(m.begin(), 6), r));
|
||||
r = m.find(8);
|
||||
assert(r == m.end());
|
||||
r = m.find(9);
|
||||
assert(r == std::next(m.begin(), 6));
|
||||
assert(iter_in_range(std::next(m.begin(), 6), std::next(m.begin(), 9), r));
|
||||
r = m.find(10);
|
||||
assert(r == m.end());
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user