mirror-llvm-project/libcxx/include/__tree

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

2471 lines
106 KiB
Text
Raw Permalink Normal View History

2010-05-11 11:42:16 -08:00
// -*- C++ -*-
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
2010-05-11 11:42:16 -08:00
//
//===----------------------------------------------------------------------===//
#ifndef _LIBCPP___TREE
#define _LIBCPP___TREE
#include <__algorithm/min.h>
[libc++] Optimize {std,ranges}::for_each for iterating over __trees (#164405) This patch optimizes how `for_each` iterates over trees by using recursion and storing pointers to the next nodes on the stack. This avoids pointer chasing through the `__parent_` pointer, reducing cache misses. It also makes use of the compiler being able tail-call optimize the recursive function, removing back-tracking the iterators have to do. ``` Benchmark old new Difference % Difference -------------------------------------------------- -------------- -------------- ------------ -------------- rng::for_each(map<int>)/32 35.19 26.67 -8.52 -24.21% rng::for_each(map<int>)/50 64.13 40.68 -23.45 -36.57% rng::for_each(map<int>)/8 5.06 6.49 1.43 28.21% rng::for_each(map<int>)/8192 22893.89 9266.68 -13627.21 -59.52% rng::for_each(map<int>::iterator)/32 35.51 26.88 -8.63 -24.31% rng::for_each(map<int>::iterator)/50 64.39 41.24 -23.15 -35.95% rng::for_each(map<int>::iterator)/8 5.12 5.93 0.81 15.80% rng::for_each(map<int>::iterator)/8192 21283.14 9736.83 -11546.31 -54.25% rng::for_each(multimap<int>)/32 35.22 26.61 -8.61 -24.45% rng::for_each(multimap<int>)/50 64.10 40.07 -24.03 -37.49% rng::for_each(multimap<int>)/8 5.08 6.69 1.61 31.70% rng::for_each(multimap<int>)/8192 23130.44 9026.16 -14104.28 -60.98% rng::for_each(multimap<int>::iterator)/32 35.40 25.08 -10.32 -29.15% rng::for_each(multimap<int>::iterator)/50 64.19 38.15 -26.04 -40.56% rng::for_each(multimap<int>::iterator)/8 5.04 5.25 0.22 4.31% rng::for_each(multimap<int>::iterator)/8192 22875.97 9392.08 -13483.89 -58.94% rng::for_each(multiset<int>)/32 35.82 27.11 -8.72 -24.33% rng::for_each(multiset<int>)/50 62.92 41.59 -21.32 -33.89% rng::for_each(multiset<int>)/8 4.79 6.79 2.00 41.70% rng::for_each(multiset<int>)/8192 22642.68 9280.95 -13361.73 -59.01% rng::for_each(multiset<int>::iterator)/32 35.76 26.71 -9.04 -25.28% rng::for_each(multiset<int>::iterator)/50 63.44 39.00 -24.44 -38.53% rng::for_each(multiset<int>::iterator)/8 4.90 5.21 0.30 6.18% rng::for_each(multiset<int>::iterator)/8192 19930.45 9867.60 -10062.85 -50.49% rng::for_each(set<int>)/32 35.90 27.30 -8.60 -23.96% rng::for_each(set<int>)/50 63.15 40.75 -22.40 -35.47% rng::for_each(set<int>)/8 4.77 6.83 2.06 43.23% rng::for_each(set<int>)/8192 20262.77 9381.57 -10881.20 -53.70% rng::for_each(set<int>::iterator)/32 36.02 26.42 -9.60 -26.64% rng::for_each(set<int>::iterator)/50 63.29 37.97 -25.32 -40.01% rng::for_each(set<int>::iterator)/8 4.72 5.22 0.50 10.50% rng::for_each(set<int>::iterator)/8192 20041.91 9831.91 -10210.00 -50.94% ```
2025-12-11 23:05:51 -09:00
#include <__algorithm/specialized_algorithms.h>
#include <__assert>
2010-05-11 11:42:16 -08:00
#include <__config>
#include <__fwd/pair.h>
#include <__iterator/distance.h>
#include <__iterator/iterator_traits.h>
#include <__iterator/next.h>
#include <__memory/addressof.h>
#include <__memory/allocator_traits.h>
#include <__memory/compressed_pair.h>
#include <__memory/construct_at.h>
#include <__memory/pointer_traits.h>
#include <__memory/swap_allocator.h>
#include <__memory/unique_ptr.h>
#include <__new/launder.h>
#include <__type_traits/copy_cvref.h>
#include <__type_traits/enable_if.h>
#include <__type_traits/invoke.h>
#include <__type_traits/is_constructible.h>
#include <__type_traits/is_nothrow_assignable.h>
#include <__type_traits/is_nothrow_constructible.h>
#include <__type_traits/is_same.h>
#include <__type_traits/is_specialization.h>
#include <__type_traits/is_swappable.h>
#include <__type_traits/make_transparent.h>
#include <__type_traits/remove_const.h>
[libc++] Optimize {std,ranges}::for_each for iterating over __trees (#164405) This patch optimizes how `for_each` iterates over trees by using recursion and storing pointers to the next nodes on the stack. This avoids pointer chasing through the `__parent_` pointer, reducing cache misses. It also makes use of the compiler being able tail-call optimize the recursive function, removing back-tracking the iterators have to do. ``` Benchmark old new Difference % Difference -------------------------------------------------- -------------- -------------- ------------ -------------- rng::for_each(map<int>)/32 35.19 26.67 -8.52 -24.21% rng::for_each(map<int>)/50 64.13 40.68 -23.45 -36.57% rng::for_each(map<int>)/8 5.06 6.49 1.43 28.21% rng::for_each(map<int>)/8192 22893.89 9266.68 -13627.21 -59.52% rng::for_each(map<int>::iterator)/32 35.51 26.88 -8.63 -24.31% rng::for_each(map<int>::iterator)/50 64.39 41.24 -23.15 -35.95% rng::for_each(map<int>::iterator)/8 5.12 5.93 0.81 15.80% rng::for_each(map<int>::iterator)/8192 21283.14 9736.83 -11546.31 -54.25% rng::for_each(multimap<int>)/32 35.22 26.61 -8.61 -24.45% rng::for_each(multimap<int>)/50 64.10 40.07 -24.03 -37.49% rng::for_each(multimap<int>)/8 5.08 6.69 1.61 31.70% rng::for_each(multimap<int>)/8192 23130.44 9026.16 -14104.28 -60.98% rng::for_each(multimap<int>::iterator)/32 35.40 25.08 -10.32 -29.15% rng::for_each(multimap<int>::iterator)/50 64.19 38.15 -26.04 -40.56% rng::for_each(multimap<int>::iterator)/8 5.04 5.25 0.22 4.31% rng::for_each(multimap<int>::iterator)/8192 22875.97 9392.08 -13483.89 -58.94% rng::for_each(multiset<int>)/32 35.82 27.11 -8.72 -24.33% rng::for_each(multiset<int>)/50 62.92 41.59 -21.32 -33.89% rng::for_each(multiset<int>)/8 4.79 6.79 2.00 41.70% rng::for_each(multiset<int>)/8192 22642.68 9280.95 -13361.73 -59.01% rng::for_each(multiset<int>::iterator)/32 35.76 26.71 -9.04 -25.28% rng::for_each(multiset<int>::iterator)/50 63.44 39.00 -24.44 -38.53% rng::for_each(multiset<int>::iterator)/8 4.90 5.21 0.30 6.18% rng::for_each(multiset<int>::iterator)/8192 19930.45 9867.60 -10062.85 -50.49% rng::for_each(set<int>)/32 35.90 27.30 -8.60 -23.96% rng::for_each(set<int>)/50 63.15 40.75 -22.40 -35.47% rng::for_each(set<int>)/8 4.77 6.83 2.06 43.23% rng::for_each(set<int>)/8192 20262.77 9381.57 -10881.20 -53.70% rng::for_each(set<int>::iterator)/32 36.02 26.42 -9.60 -26.64% rng::for_each(set<int>::iterator)/50 63.29 37.97 -25.32 -40.01% rng::for_each(set<int>::iterator)/8 4.72 5.22 0.50 10.50% rng::for_each(set<int>::iterator)/8192 20041.91 9831.91 -10210.00 -50.94% ```
2025-12-11 23:05:51 -09:00
#include <__type_traits/remove_cvref.h>
#include <__utility/forward.h>
[libc++] Optimize most of the __tree search algorithms (#155245) This patch introduces a new comparator, namely `__lazy_synth_three_way`, which tries to provide an efficient three way comparator for known types and falls back to using the provided comparator if it doesn't know how to do that. Currently, an efficient three way comparison is only provided when using one of the `less` comparions object from the standard library and `std::string`. This will be extended in future patches. ``` ------------------------------------------------------------------------------------------------------------------------------ Benchmark old new ------------------------------------------------------------------------------------------------------------------------------ std::map<std::string, int>::ctor(const&)/0 12.6 ns 12.6 ns std::map<std::string, int>::ctor(const&)/32 858 ns 837 ns std::map<std::string, int>::ctor(const&)/1024 46700 ns 46739 ns std::map<std::string, int>::ctor(const&)/8192 458100 ns 449806 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/0 12.8 ns 12.7 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/32 1286 ns 1266 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/1024 93812 ns 84686 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/8192 1480346 ns 1385924 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/0 12.9 ns 12.8 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/32 1044 ns 1055 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/1024 63071 ns 62861 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/8192 595046 ns 590223 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/0 13.6 ns 13.6 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/32 880 ns 911 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/1024 48627 ns 47808 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/8192 458552 ns 454497 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/0 13.8 ns 13.6 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/32 864 ns 851 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/1024 49483 ns 49555 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/8192 456977 ns 457894 ns std::map<std::string, int>::operator=(const&) (into populated Container)/0 1.31 ns 1.31 ns std::map<std::string, int>::operator=(const&) (into populated Container)/32 425 ns 415 ns std::map<std::string, int>::operator=(const&) (into populated Container)/1024 14248 ns 14225 ns std::map<std::string, int>::operator=(const&) (into populated Container)/8192 136684 ns 133696 ns std::map<std::string, int>::insert(value) (already present)/0 21.5 ns 16.2 ns std::map<std::string, int>::insert(value) (already present)/32 22.7 ns 25.1 ns std::map<std::string, int>::insert(value) (already present)/1024 54.5 ns 29.1 ns std::map<std::string, int>::insert(value) (already present)/8192 78.4 ns 30.4 ns std::map<std::string, int>::insert(value) (new value)/0 40.9 ns 39.0 ns std::map<std::string, int>::insert(value) (new value)/32 58.3 ns 47.2 ns std::map<std::string, int>::insert(value) (new value)/1024 120 ns 71.3 ns std::map<std::string, int>::insert(value) (new value)/8192 157 ns 129 ns std::map<std::string, int>::insert(hint, value) (good hint)/0 40.3 ns 40.7 ns std::map<std::string, int>::insert(hint, value) (good hint)/32 48.0 ns 30.0 ns std::map<std::string, int>::insert(hint, value) (good hint)/1024 107 ns 63.2 ns std::map<std::string, int>::insert(hint, value) (good hint)/8192 132 ns 107 ns std::map<std::string, int>::insert(hint, value) (bad hint)/0 27.0 ns 40.9 ns std::map<std::string, int>::insert(hint, value) (bad hint)/32 68.3 ns 58.4 ns std::map<std::string, int>::insert(hint, value) (bad hint)/1024 125 ns 82.0 ns std::map<std::string, int>::insert(hint, value) (bad hint)/8192 155 ns 150 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/0 404 ns 405 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/32 2004 ns 1805 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/1024 102820 ns 76102 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/8192 1144590 ns 949266 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/0 408 ns 404 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/32 1592 ns 1377 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/1024 74847 ns 53921 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/8192 828505 ns 698716 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/0 407 ns 407 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/32 1584 ns 1557 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/1024 47157 ns 47443 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/8192 623887 ns 628385 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/0 405 ns 403 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/32 1478 ns 1510 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/1024 47852 ns 47835 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/8192 605311 ns 606951 ns std::map<std::string, int>::erase(key) (existent)/0 129 ns 94.0 ns std::map<std::string, int>::erase(key) (existent)/32 110 ns 106 ns std::map<std::string, int>::erase(key) (existent)/1024 121 ns 128 ns std::map<std::string, int>::erase(key) (existent)/8192 165 ns 66.9 ns std::map<std::string, int>::erase(key) (non-existent)/0 0.269 ns 0.257 ns std::map<std::string, int>::erase(key) (non-existent)/32 21.9 ns 11.3 ns std::map<std::string, int>::erase(key) (non-existent)/1024 53.5 ns 25.4 ns std::map<std::string, int>::erase(key) (non-existent)/8192 67.3 ns 31.9 ns std::map<std::string, int>::erase(iterator)/0 46.3 ns 46.7 ns std::map<std::string, int>::erase(iterator)/32 44.4 ns 41.8 ns std::map<std::string, int>::erase(iterator)/1024 43.7 ns 46.4 ns std::map<std::string, int>::erase(iterator)/8192 45.2 ns 44.1 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/0 407 ns 407 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/32 876 ns 906 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/1024 20880 ns 20444 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/8192 252881 ns 241583 ns std::map<std::string, int>::clear()/0 407 ns 408 ns std::map<std::string, int>::clear()/32 1252 ns 1323 ns std::map<std::string, int>::clear()/1024 38488 ns 38017 ns std::map<std::string, int>::clear()/8192 416492 ns 428534 ns std::map<std::string, int>::find(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::find(key) (existent)/32 33.9 ns 15.3 ns std::map<std::string, int>::find(key) (existent)/1024 43.0 ns 25.5 ns std::map<std::string, int>::find(key) (existent)/8192 44.6 ns 29.3 ns std::map<std::string, int>::find(key) (non-existent)/0 0.259 ns 0.257 ns std::map<std::string, int>::find(key) (non-existent)/32 22.6 ns 11.4 ns std::map<std::string, int>::find(key) (non-existent)/1024 48.6 ns 25.1 ns std::map<std::string, int>::find(key) (non-existent)/8192 64.1 ns 31.1 ns std::map<std::string, int>::count(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::count(key) (existent)/32 32.2 ns 17.3 ns std::map<std::string, int>::count(key) (existent)/1024 42.4 ns 25.3 ns std::map<std::string, int>::count(key) (existent)/8192 44.4 ns 31.6 ns std::map<std::string, int>::count(key) (non-existent)/0 0.260 ns 0.259 ns std::map<std::string, int>::count(key) (non-existent)/32 22.9 ns 11.3 ns std::map<std::string, int>::count(key) (non-existent)/1024 49.8 ns 25.5 ns std::map<std::string, int>::count(key) (non-existent)/8192 66.3 ns 31.9 ns std::map<std::string, int>::contains(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::contains(key) (existent)/32 31.4 ns 18.0 ns std::map<std::string, int>::contains(key) (existent)/1024 44.3 ns 26.5 ns std::map<std::string, int>::contains(key) (existent)/8192 47.4 ns 30.2 ns std::map<std::string, int>::contains(key) (non-existent)/0 0.452 ns 0.441 ns std::map<std::string, int>::contains(key) (non-existent)/32 23.1 ns 11.5 ns std::map<std::string, int>::contains(key) (non-existent)/1024 46.2 ns 26.3 ns std::map<std::string, int>::contains(key) (non-existent)/8192 63.4 ns 31.4 ns std::map<std::string, int>::lower_bound(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::lower_bound(key) (existent)/32 17.2 ns 19.0 ns std::map<std::string, int>::lower_bound(key) (existent)/1024 27.1 ns 26.2 ns std::map<std::string, int>::lower_bound(key) (existent)/8192 34.0 ns 36.0 ns std::map<std::string, int>::lower_bound(key) (non-existent)/0 0.259 ns 0.257 ns std::map<std::string, int>::lower_bound(key) (non-existent)/32 11.6 ns 11.5 ns std::map<std::string, int>::lower_bound(key) (non-existent)/1024 24.8 ns 25.6 ns std::map<std::string, int>::lower_bound(key) (non-existent)/8192 31.7 ns 31.6 ns std::map<std::string, int>::upper_bound(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::upper_bound(key) (existent)/32 18.8 ns 19.7 ns std::map<std::string, int>::upper_bound(key) (existent)/1024 25.3 ns 27.7 ns std::map<std::string, int>::upper_bound(key) (existent)/8192 30.2 ns 29.9 ns std::map<std::string, int>::upper_bound(key) (non-existent)/0 0.260 ns 0.259 ns std::map<std::string, int>::upper_bound(key) (non-existent)/32 11.3 ns 12.0 ns std::map<std::string, int>::upper_bound(key) (non-existent)/1024 25.6 ns 25.9 ns std::map<std::string, int>::upper_bound(key) (non-existent)/8192 33.1 ns 34.2 ns std::map<std::string, int>::equal_range(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::equal_range(key) (existent)/32 33.5 ns 15.8 ns std::map<std::string, int>::equal_range(key) (existent)/1024 43.0 ns 25.1 ns std::map<std::string, int>::equal_range(key) (existent)/8192 54.1 ns 30.7 ns std::map<std::string, int>::equal_range(key) (non-existent)/0 0.265 ns 0.259 ns std::map<std::string, int>::equal_range(key) (non-existent)/32 22.1 ns 12.1 ns std::map<std::string, int>::equal_range(key) (non-existent)/1024 44.8 ns 24.4 ns std::map<std::string, int>::equal_range(key) (non-existent)/8192 62.2 ns 40.1 ns ``` Fixes #66577
2025-09-07 23:56:14 -08:00
#include <__utility/lazy_synth_three_way_comparator.h>
#include <__utility/move.h>
#include <__utility/pair.h>
#include <__utility/swap.h>
#include <__utility/try_key_extraction.h>
#include <limits>
2010-05-11 11:42:16 -08:00
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
#endif
2010-05-11 11:42:16 -08:00
_LIBCPP_PUSH_MACROS
#include <__undef_macros>
_LIBCPP_DIAGNOSTIC_PUSH
// GCC complains about the backslashes at the end, see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=121528
_LIBCPP_GCC_DIAGNOSTIC_IGNORED("-Wcomment")
// __tree is a red-black-tree implementation used for the associative containers (i.e. (multi)map/set). It stores
// - (1) a pointer to the node with the smallest (i.e. leftmost) element, namely __begin_node_
// - (2) the number of nodes in the tree, namely __size_
// - (3) a pointer to the root of the tree, namely __end_node_
//
// Storing (1) and (2) is required to allow for constant time lookups. A tree looks like this in memory:
//
// __end_node_
// |
// root
// / \
// l1 r1
// / \ / \
// ... ... ... ...
//
// All nodes except __end_node_ have a __left_ and __right_ pointer as well as a __parent_ pointer.
// __end_node_ only contains a __left_ pointer, which points to the root of the tree.
// This layout allows for iteration through the tree without a need for special handling of the end node. See
// __tree_next_iter and __tree_prev_iter for more details.
_LIBCPP_DIAGNOSTIC_POP
2010-05-11 11:42:16 -08:00
_LIBCPP_BEGIN_NAMESPACE_STD
template <class _Pointer>
class __tree_end_node;
template <class _VoidPtr>
class __tree_node_base;
template <class _Tp, class _VoidPtr>
class __tree_node;
template <class _Key, class _Value>
struct __value_type;
2010-05-11 11:42:16 -08:00
/*
_NodePtr algorithms
The algorithms taking _NodePtr are red black tree algorithms. Those
algorithms taking a parameter named __root should assume that __root
points to a proper red black tree (unless otherwise specified).
Each algorithm herein assumes that __root->__parent_ points to a non-null
structure which has a member __left_ which points back to __root. No other
member is read or written to at __root->__parent_.
__root->__parent_ will be referred to below (in comments only) as end_node.
end_node->__left_ is an externably accessible lvalue for __root, and can be
changed by node insertion and removal (without explicit reference to end_node).
All nodes (with the exception of end_node), even the node referred to as
__root, have a non-null __parent_ field.
*/
// Returns: true if __x is a left child of its parent, else false
// Precondition: __x != nullptr.
template <class _NodePtr>
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool __tree_is_left_child(_NodePtr __x) _NOEXCEPT {
2010-05-11 11:42:16 -08:00
return __x == __x->__parent_->__left_;
}
2017-08-18 04:57:36 -08:00
// Determines if the subtree rooted at __x is a proper red black subtree. If
2010-05-11 11:42:16 -08:00
// __x is a proper subtree, returns the black height (null counts as 1). If
// __x is an improper subtree, returns 0.
template <class _NodePtr>
_LIBCPP_CONSTEXPR_SINCE_CXX26 unsigned __tree_sub_invariant(_NodePtr __x) {
2010-05-11 11:42:16 -08:00
if (__x == nullptr)
return 1;
// parent consistency checked by caller
// check __x->__left_ consistency
if (__x->__left_ != nullptr && __x->__left_->__parent_ != __x)
return 0;
// check __x->__right_ consistency
if (__x->__right_ != nullptr && __x->__right_->__parent_ != __x)
return 0;
// check __x->__left_ != __x->__right_ unless both are nullptr
if (__x->__left_ == __x->__right_ && __x->__left_ != nullptr)
return 0;
// If this is red, neither child can be red
if (!__x->__is_black_) {
if (__x->__left_ && !__x->__left_->__is_black_)
return 0;
if (__x->__right_ && !__x->__right_->__is_black_)
return 0;
}
unsigned __h = std::__tree_sub_invariant(__x->__left_);
2010-05-11 11:42:16 -08:00
if (__h == 0)
return 0; // invalid left subtree
if (__h != std::__tree_sub_invariant(__x->__right_))
2010-05-11 11:42:16 -08:00
return 0; // invalid or different height right subtree
return __h + __x->__is_black_; // return black height of this node
}
2017-08-18 04:57:36 -08:00
// Determines if the red black tree rooted at __root is a proper red black tree.
// __root == nullptr is a proper tree. Returns true if __root is a proper
2010-05-11 11:42:16 -08:00
// red black tree, else returns false.
template <class _NodePtr>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool __tree_invariant(_NodePtr __root) {
2010-05-11 11:42:16 -08:00
if (__root == nullptr)
return true;
// check __x->__parent_ consistency
if (__root->__parent_ == nullptr)
return false;
if (!std::__tree_is_left_child(__root))
2010-05-11 11:42:16 -08:00
return false;
// root must be black
if (!__root->__is_black_)
return false;
// do normal node checks
return std::__tree_sub_invariant(__root) != 0;
2010-05-11 11:42:16 -08:00
}
// Returns: pointer to the left-most node under __x.
template <class _NodePtr>
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodePtr __tree_min(_NodePtr __x) _NOEXCEPT {
_LIBCPP_ASSERT_INTERNAL(__x != nullptr, "Root node shouldn't be null");
2010-05-11 11:42:16 -08:00
while (__x->__left_ != nullptr)
__x = __x->__left_;
return __x;
}
// Returns: pointer to the right-most node under __x.
template <class _NodePtr>
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodePtr __tree_max(_NodePtr __x) _NOEXCEPT {
_LIBCPP_ASSERT_INTERNAL(__x != nullptr, "Root node shouldn't be null");
2010-05-11 11:42:16 -08:00
while (__x->__right_ != nullptr)
__x = __x->__right_;
return __x;
}
// Returns: pointer to the next in-order node after __x.
template <class _NodePtr>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodePtr __tree_next(_NodePtr __x) _NOEXCEPT {
_LIBCPP_ASSERT_INTERNAL(__x != nullptr, "node shouldn't be null");
2010-05-11 11:42:16 -08:00
if (__x->__right_ != nullptr)
return std::__tree_min(__x->__right_);
while (!std::__tree_is_left_child(__x))
Fix undefined behavior in __tree Summary: This patch attempts to fix the undefined behavior in __tree by changing the node pointer types used throughout. The pointer types are changed for raw pointers in the current ABI and for fancy pointers in ABI V2 (since the fancy pointer types may not be ABI compatible). The UB in `__tree` arises because tree downcasts the embedded end node and then deferences that pointer. Currently there are 3 node types in __tree. * `__tree_end_node` which contains the `__left_` pointer. This node is embedded within the container. * `__tree_node_base` which contains `__right_`, `__parent_` and `__is_black`. This node is used throughout the tree rebalancing algorithms. * `__tree_node` which contains `__value_`. Currently `__tree` stores the start of the tree, `__begin_node_`, as a pointer to a `__tree_node`. Additionally the iterators store their position as a pointer to a `__tree_node`. In both of these cases the pointee can be the end node. This is fixed by changing them to store `__tree_end_node` pointers instead. To make this change I introduced an `__iter_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node` in the current one. Both `__tree::__begin_node_` and iterator pointers are now stored as `__iter_pointers`. The other situation where `__tree_end_node` is stored as the wrong type is in `__tree_node_base::__parent_`. Currently `__left_`, `__right_`, and `__parent_` are all `__tree_node_base` pointers. Since the end node will only be stored in `__parent_` the fix is to change `__parent_` to be a pointer to `__tree_end_node`. To make this change I introduced a `__parent_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node_base` in the current one. Note that in the new ABI `__iter_pointer` and `__parent_pointer` are the same type (but not in the old one). The confusion between these two types is unfortunate but it was the best solution I could come up with that maintains the ABI. The typedef changes force a ton of explicit type casts to correct pointer types and to make current code compatible with both the old and new pointer typedefs. This is the bulk of the change and it's really messy. Unfortunately I don't know how to avoid it. Please let me know what you think. Reviewers: howard.hinnant, mclow.lists Subscribers: howard.hinnant, bbannier, cfe-commits Differential Revision: https://reviews.llvm.org/D20786 llvm-svn: 276003
2016-07-19 09:56:20 -08:00
__x = __x->__parent_unsafe();
return __x->__parent_unsafe();
}
// __tree_next_iter and __tree_prev_iter implement iteration through the tree. The order is as follows:
// left sub-tree -> node -> right sub-tree. When the right-most node of a sub-tree is reached, we walk up the tree until
// we find a node where we were in the left sub-tree. We are _always_ in a left sub-tree, since the __end_node_ points
// to the actual root of the tree through a __left_ pointer. Incrementing the end() pointer is UB, so we can assume that
// never happens.
Fix undefined behavior in __tree Summary: This patch attempts to fix the undefined behavior in __tree by changing the node pointer types used throughout. The pointer types are changed for raw pointers in the current ABI and for fancy pointers in ABI V2 (since the fancy pointer types may not be ABI compatible). The UB in `__tree` arises because tree downcasts the embedded end node and then deferences that pointer. Currently there are 3 node types in __tree. * `__tree_end_node` which contains the `__left_` pointer. This node is embedded within the container. * `__tree_node_base` which contains `__right_`, `__parent_` and `__is_black`. This node is used throughout the tree rebalancing algorithms. * `__tree_node` which contains `__value_`. Currently `__tree` stores the start of the tree, `__begin_node_`, as a pointer to a `__tree_node`. Additionally the iterators store their position as a pointer to a `__tree_node`. In both of these cases the pointee can be the end node. This is fixed by changing them to store `__tree_end_node` pointers instead. To make this change I introduced an `__iter_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node` in the current one. Both `__tree::__begin_node_` and iterator pointers are now stored as `__iter_pointers`. The other situation where `__tree_end_node` is stored as the wrong type is in `__tree_node_base::__parent_`. Currently `__left_`, `__right_`, and `__parent_` are all `__tree_node_base` pointers. Since the end node will only be stored in `__parent_` the fix is to change `__parent_` to be a pointer to `__tree_end_node`. To make this change I introduced a `__parent_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node_base` in the current one. Note that in the new ABI `__iter_pointer` and `__parent_pointer` are the same type (but not in the old one). The confusion between these two types is unfortunate but it was the best solution I could come up with that maintains the ABI. The typedef changes force a ton of explicit type casts to correct pointer types and to make current code compatible with both the old and new pointer typedefs. This is the bulk of the change and it's really messy. Unfortunately I don't know how to avoid it. Please let me know what you think. Reviewers: howard.hinnant, mclow.lists Subscribers: howard.hinnant, bbannier, cfe-commits Differential Revision: https://reviews.llvm.org/D20786 llvm-svn: 276003
2016-07-19 09:56:20 -08:00
template <class _EndNodePtr, class _NodePtr>
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _EndNodePtr __tree_next_iter(_NodePtr __x) _NOEXCEPT {
_LIBCPP_ASSERT_INTERNAL(__x != nullptr, "node shouldn't be null");
Fix undefined behavior in __tree Summary: This patch attempts to fix the undefined behavior in __tree by changing the node pointer types used throughout. The pointer types are changed for raw pointers in the current ABI and for fancy pointers in ABI V2 (since the fancy pointer types may not be ABI compatible). The UB in `__tree` arises because tree downcasts the embedded end node and then deferences that pointer. Currently there are 3 node types in __tree. * `__tree_end_node` which contains the `__left_` pointer. This node is embedded within the container. * `__tree_node_base` which contains `__right_`, `__parent_` and `__is_black`. This node is used throughout the tree rebalancing algorithms. * `__tree_node` which contains `__value_`. Currently `__tree` stores the start of the tree, `__begin_node_`, as a pointer to a `__tree_node`. Additionally the iterators store their position as a pointer to a `__tree_node`. In both of these cases the pointee can be the end node. This is fixed by changing them to store `__tree_end_node` pointers instead. To make this change I introduced an `__iter_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node` in the current one. Both `__tree::__begin_node_` and iterator pointers are now stored as `__iter_pointers`. The other situation where `__tree_end_node` is stored as the wrong type is in `__tree_node_base::__parent_`. Currently `__left_`, `__right_`, and `__parent_` are all `__tree_node_base` pointers. Since the end node will only be stored in `__parent_` the fix is to change `__parent_` to be a pointer to `__tree_end_node`. To make this change I introduced a `__parent_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node_base` in the current one. Note that in the new ABI `__iter_pointer` and `__parent_pointer` are the same type (but not in the old one). The confusion between these two types is unfortunate but it was the best solution I could come up with that maintains the ABI. The typedef changes force a ton of explicit type casts to correct pointer types and to make current code compatible with both the old and new pointer typedefs. This is the bulk of the change and it's really messy. Unfortunately I don't know how to avoid it. Please let me know what you think. Reviewers: howard.hinnant, mclow.lists Subscribers: howard.hinnant, bbannier, cfe-commits Differential Revision: https://reviews.llvm.org/D20786 llvm-svn: 276003
2016-07-19 09:56:20 -08:00
if (__x->__right_ != nullptr)
return std::__static_fancy_pointer_cast<_EndNodePtr>(std::__tree_min(__x->__right_));
while (!std::__tree_is_left_child(__x))
Fix undefined behavior in __tree Summary: This patch attempts to fix the undefined behavior in __tree by changing the node pointer types used throughout. The pointer types are changed for raw pointers in the current ABI and for fancy pointers in ABI V2 (since the fancy pointer types may not be ABI compatible). The UB in `__tree` arises because tree downcasts the embedded end node and then deferences that pointer. Currently there are 3 node types in __tree. * `__tree_end_node` which contains the `__left_` pointer. This node is embedded within the container. * `__tree_node_base` which contains `__right_`, `__parent_` and `__is_black`. This node is used throughout the tree rebalancing algorithms. * `__tree_node` which contains `__value_`. Currently `__tree` stores the start of the tree, `__begin_node_`, as a pointer to a `__tree_node`. Additionally the iterators store their position as a pointer to a `__tree_node`. In both of these cases the pointee can be the end node. This is fixed by changing them to store `__tree_end_node` pointers instead. To make this change I introduced an `__iter_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node` in the current one. Both `__tree::__begin_node_` and iterator pointers are now stored as `__iter_pointers`. The other situation where `__tree_end_node` is stored as the wrong type is in `__tree_node_base::__parent_`. Currently `__left_`, `__right_`, and `__parent_` are all `__tree_node_base` pointers. Since the end node will only be stored in `__parent_` the fix is to change `__parent_` to be a pointer to `__tree_end_node`. To make this change I introduced a `__parent_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node_base` in the current one. Note that in the new ABI `__iter_pointer` and `__parent_pointer` are the same type (but not in the old one). The confusion between these two types is unfortunate but it was the best solution I could come up with that maintains the ABI. The typedef changes force a ton of explicit type casts to correct pointer types and to make current code compatible with both the old and new pointer typedefs. This is the bulk of the change and it's really messy. Unfortunately I don't know how to avoid it. Please let me know what you think. Reviewers: howard.hinnant, mclow.lists Subscribers: howard.hinnant, bbannier, cfe-commits Differential Revision: https://reviews.llvm.org/D20786 llvm-svn: 276003
2016-07-19 09:56:20 -08:00
__x = __x->__parent_unsafe();
return std::__static_fancy_pointer_cast<_EndNodePtr>(__x->__parent_);
2010-05-11 11:42:16 -08:00
}
// Returns: pointer to the previous in-order node before __x.
Fix undefined behavior in __tree Summary: This patch attempts to fix the undefined behavior in __tree by changing the node pointer types used throughout. The pointer types are changed for raw pointers in the current ABI and for fancy pointers in ABI V2 (since the fancy pointer types may not be ABI compatible). The UB in `__tree` arises because tree downcasts the embedded end node and then deferences that pointer. Currently there are 3 node types in __tree. * `__tree_end_node` which contains the `__left_` pointer. This node is embedded within the container. * `__tree_node_base` which contains `__right_`, `__parent_` and `__is_black`. This node is used throughout the tree rebalancing algorithms. * `__tree_node` which contains `__value_`. Currently `__tree` stores the start of the tree, `__begin_node_`, as a pointer to a `__tree_node`. Additionally the iterators store their position as a pointer to a `__tree_node`. In both of these cases the pointee can be the end node. This is fixed by changing them to store `__tree_end_node` pointers instead. To make this change I introduced an `__iter_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node` in the current one. Both `__tree::__begin_node_` and iterator pointers are now stored as `__iter_pointers`. The other situation where `__tree_end_node` is stored as the wrong type is in `__tree_node_base::__parent_`. Currently `__left_`, `__right_`, and `__parent_` are all `__tree_node_base` pointers. Since the end node will only be stored in `__parent_` the fix is to change `__parent_` to be a pointer to `__tree_end_node`. To make this change I introduced a `__parent_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node_base` in the current one. Note that in the new ABI `__iter_pointer` and `__parent_pointer` are the same type (but not in the old one). The confusion between these two types is unfortunate but it was the best solution I could come up with that maintains the ABI. The typedef changes force a ton of explicit type casts to correct pointer types and to make current code compatible with both the old and new pointer typedefs. This is the bulk of the change and it's really messy. Unfortunately I don't know how to avoid it. Please let me know what you think. Reviewers: howard.hinnant, mclow.lists Subscribers: howard.hinnant, bbannier, cfe-commits Differential Revision: https://reviews.llvm.org/D20786 llvm-svn: 276003
2016-07-19 09:56:20 -08:00
// Note: __x may be the end node.
template <class _NodePtr, class _EndNodePtr>
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodePtr __tree_prev_iter(_EndNodePtr __x) _NOEXCEPT {
_LIBCPP_ASSERT_INTERNAL(__x != nullptr, "node shouldn't be null");
2010-05-11 11:42:16 -08:00
if (__x->__left_ != nullptr)
return std::__tree_max(__x->__left_);
_NodePtr __xx = std::__static_fancy_pointer_cast<_NodePtr>(__x);
while (std::__tree_is_left_child(__xx))
Fix undefined behavior in __tree Summary: This patch attempts to fix the undefined behavior in __tree by changing the node pointer types used throughout. The pointer types are changed for raw pointers in the current ABI and for fancy pointers in ABI V2 (since the fancy pointer types may not be ABI compatible). The UB in `__tree` arises because tree downcasts the embedded end node and then deferences that pointer. Currently there are 3 node types in __tree. * `__tree_end_node` which contains the `__left_` pointer. This node is embedded within the container. * `__tree_node_base` which contains `__right_`, `__parent_` and `__is_black`. This node is used throughout the tree rebalancing algorithms. * `__tree_node` which contains `__value_`. Currently `__tree` stores the start of the tree, `__begin_node_`, as a pointer to a `__tree_node`. Additionally the iterators store their position as a pointer to a `__tree_node`. In both of these cases the pointee can be the end node. This is fixed by changing them to store `__tree_end_node` pointers instead. To make this change I introduced an `__iter_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node` in the current one. Both `__tree::__begin_node_` and iterator pointers are now stored as `__iter_pointers`. The other situation where `__tree_end_node` is stored as the wrong type is in `__tree_node_base::__parent_`. Currently `__left_`, `__right_`, and `__parent_` are all `__tree_node_base` pointers. Since the end node will only be stored in `__parent_` the fix is to change `__parent_` to be a pointer to `__tree_end_node`. To make this change I introduced a `__parent_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node_base` in the current one. Note that in the new ABI `__iter_pointer` and `__parent_pointer` are the same type (but not in the old one). The confusion between these two types is unfortunate but it was the best solution I could come up with that maintains the ABI. The typedef changes force a ton of explicit type casts to correct pointer types and to make current code compatible with both the old and new pointer typedefs. This is the bulk of the change and it's really messy. Unfortunately I don't know how to avoid it. Please let me know what you think. Reviewers: howard.hinnant, mclow.lists Subscribers: howard.hinnant, bbannier, cfe-commits Differential Revision: https://reviews.llvm.org/D20786 llvm-svn: 276003
2016-07-19 09:56:20 -08:00
__xx = __xx->__parent_unsafe();
return __xx->__parent_unsafe();
2010-05-11 11:42:16 -08:00
}
// Returns: pointer to a node which has no children
template <class _NodePtr>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodePtr __tree_leaf(_NodePtr __x) _NOEXCEPT {
_LIBCPP_ASSERT_INTERNAL(__x != nullptr, "node shouldn't be null");
2010-05-11 11:42:16 -08:00
while (true) {
if (__x->__left_ != nullptr) {
__x = __x->__left_;
continue;
}
if (__x->__right_ != nullptr) {
__x = __x->__right_;
continue;
}
break;
}
return __x;
}
// Effects: Makes __x->__right_ the subtree root with __x as its left child
// while preserving in-order order.
template <class _NodePtr>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __tree_left_rotate(_NodePtr __x) _NOEXCEPT {
_LIBCPP_ASSERT_INTERNAL(__x != nullptr, "node shouldn't be null");
_LIBCPP_ASSERT_INTERNAL(__x->__right_ != nullptr, "node should have a right child");
2010-05-11 11:42:16 -08:00
_NodePtr __y = __x->__right_;
__x->__right_ = __y->__left_;
if (__x->__right_ != nullptr)
Fix undefined behavior in __tree Summary: This patch attempts to fix the undefined behavior in __tree by changing the node pointer types used throughout. The pointer types are changed for raw pointers in the current ABI and for fancy pointers in ABI V2 (since the fancy pointer types may not be ABI compatible). The UB in `__tree` arises because tree downcasts the embedded end node and then deferences that pointer. Currently there are 3 node types in __tree. * `__tree_end_node` which contains the `__left_` pointer. This node is embedded within the container. * `__tree_node_base` which contains `__right_`, `__parent_` and `__is_black`. This node is used throughout the tree rebalancing algorithms. * `__tree_node` which contains `__value_`. Currently `__tree` stores the start of the tree, `__begin_node_`, as a pointer to a `__tree_node`. Additionally the iterators store their position as a pointer to a `__tree_node`. In both of these cases the pointee can be the end node. This is fixed by changing them to store `__tree_end_node` pointers instead. To make this change I introduced an `__iter_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node` in the current one. Both `__tree::__begin_node_` and iterator pointers are now stored as `__iter_pointers`. The other situation where `__tree_end_node` is stored as the wrong type is in `__tree_node_base::__parent_`. Currently `__left_`, `__right_`, and `__parent_` are all `__tree_node_base` pointers. Since the end node will only be stored in `__parent_` the fix is to change `__parent_` to be a pointer to `__tree_end_node`. To make this change I introduced a `__parent_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node_base` in the current one. Note that in the new ABI `__iter_pointer` and `__parent_pointer` are the same type (but not in the old one). The confusion between these two types is unfortunate but it was the best solution I could come up with that maintains the ABI. The typedef changes force a ton of explicit type casts to correct pointer types and to make current code compatible with both the old and new pointer typedefs. This is the bulk of the change and it's really messy. Unfortunately I don't know how to avoid it. Please let me know what you think. Reviewers: howard.hinnant, mclow.lists Subscribers: howard.hinnant, bbannier, cfe-commits Differential Revision: https://reviews.llvm.org/D20786 llvm-svn: 276003
2016-07-19 09:56:20 -08:00
__x->__right_->__set_parent(__x);
2010-05-11 11:42:16 -08:00
__y->__parent_ = __x->__parent_;
if (std::__tree_is_left_child(__x))
2010-05-11 11:42:16 -08:00
__x->__parent_->__left_ = __y;
else
Fix undefined behavior in __tree Summary: This patch attempts to fix the undefined behavior in __tree by changing the node pointer types used throughout. The pointer types are changed for raw pointers in the current ABI and for fancy pointers in ABI V2 (since the fancy pointer types may not be ABI compatible). The UB in `__tree` arises because tree downcasts the embedded end node and then deferences that pointer. Currently there are 3 node types in __tree. * `__tree_end_node` which contains the `__left_` pointer. This node is embedded within the container. * `__tree_node_base` which contains `__right_`, `__parent_` and `__is_black`. This node is used throughout the tree rebalancing algorithms. * `__tree_node` which contains `__value_`. Currently `__tree` stores the start of the tree, `__begin_node_`, as a pointer to a `__tree_node`. Additionally the iterators store their position as a pointer to a `__tree_node`. In both of these cases the pointee can be the end node. This is fixed by changing them to store `__tree_end_node` pointers instead. To make this change I introduced an `__iter_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node` in the current one. Both `__tree::__begin_node_` and iterator pointers are now stored as `__iter_pointers`. The other situation where `__tree_end_node` is stored as the wrong type is in `__tree_node_base::__parent_`. Currently `__left_`, `__right_`, and `__parent_` are all `__tree_node_base` pointers. Since the end node will only be stored in `__parent_` the fix is to change `__parent_` to be a pointer to `__tree_end_node`. To make this change I introduced a `__parent_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node_base` in the current one. Note that in the new ABI `__iter_pointer` and `__parent_pointer` are the same type (but not in the old one). The confusion between these two types is unfortunate but it was the best solution I could come up with that maintains the ABI. The typedef changes force a ton of explicit type casts to correct pointer types and to make current code compatible with both the old and new pointer typedefs. This is the bulk of the change and it's really messy. Unfortunately I don't know how to avoid it. Please let me know what you think. Reviewers: howard.hinnant, mclow.lists Subscribers: howard.hinnant, bbannier, cfe-commits Differential Revision: https://reviews.llvm.org/D20786 llvm-svn: 276003
2016-07-19 09:56:20 -08:00
__x->__parent_unsafe()->__right_ = __y;
2010-05-11 11:42:16 -08:00
__y->__left_ = __x;
Fix undefined behavior in __tree Summary: This patch attempts to fix the undefined behavior in __tree by changing the node pointer types used throughout. The pointer types are changed for raw pointers in the current ABI and for fancy pointers in ABI V2 (since the fancy pointer types may not be ABI compatible). The UB in `__tree` arises because tree downcasts the embedded end node and then deferences that pointer. Currently there are 3 node types in __tree. * `__tree_end_node` which contains the `__left_` pointer. This node is embedded within the container. * `__tree_node_base` which contains `__right_`, `__parent_` and `__is_black`. This node is used throughout the tree rebalancing algorithms. * `__tree_node` which contains `__value_`. Currently `__tree` stores the start of the tree, `__begin_node_`, as a pointer to a `__tree_node`. Additionally the iterators store their position as a pointer to a `__tree_node`. In both of these cases the pointee can be the end node. This is fixed by changing them to store `__tree_end_node` pointers instead. To make this change I introduced an `__iter_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node` in the current one. Both `__tree::__begin_node_` and iterator pointers are now stored as `__iter_pointers`. The other situation where `__tree_end_node` is stored as the wrong type is in `__tree_node_base::__parent_`. Currently `__left_`, `__right_`, and `__parent_` are all `__tree_node_base` pointers. Since the end node will only be stored in `__parent_` the fix is to change `__parent_` to be a pointer to `__tree_end_node`. To make this change I introduced a `__parent_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node_base` in the current one. Note that in the new ABI `__iter_pointer` and `__parent_pointer` are the same type (but not in the old one). The confusion between these two types is unfortunate but it was the best solution I could come up with that maintains the ABI. The typedef changes force a ton of explicit type casts to correct pointer types and to make current code compatible with both the old and new pointer typedefs. This is the bulk of the change and it's really messy. Unfortunately I don't know how to avoid it. Please let me know what you think. Reviewers: howard.hinnant, mclow.lists Subscribers: howard.hinnant, bbannier, cfe-commits Differential Revision: https://reviews.llvm.org/D20786 llvm-svn: 276003
2016-07-19 09:56:20 -08:00
__x->__set_parent(__y);
2010-05-11 11:42:16 -08:00
}
// Effects: Makes __x->__left_ the subtree root with __x as its right child
// while preserving in-order order.
template <class _NodePtr>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __tree_right_rotate(_NodePtr __x) _NOEXCEPT {
_LIBCPP_ASSERT_INTERNAL(__x != nullptr, "node shouldn't be null");
_LIBCPP_ASSERT_INTERNAL(__x->__left_ != nullptr, "node should have a left child");
2010-05-11 11:42:16 -08:00
_NodePtr __y = __x->__left_;
__x->__left_ = __y->__right_;
if (__x->__left_ != nullptr)
Fix undefined behavior in __tree Summary: This patch attempts to fix the undefined behavior in __tree by changing the node pointer types used throughout. The pointer types are changed for raw pointers in the current ABI and for fancy pointers in ABI V2 (since the fancy pointer types may not be ABI compatible). The UB in `__tree` arises because tree downcasts the embedded end node and then deferences that pointer. Currently there are 3 node types in __tree. * `__tree_end_node` which contains the `__left_` pointer. This node is embedded within the container. * `__tree_node_base` which contains `__right_`, `__parent_` and `__is_black`. This node is used throughout the tree rebalancing algorithms. * `__tree_node` which contains `__value_`. Currently `__tree` stores the start of the tree, `__begin_node_`, as a pointer to a `__tree_node`. Additionally the iterators store their position as a pointer to a `__tree_node`. In both of these cases the pointee can be the end node. This is fixed by changing them to store `__tree_end_node` pointers instead. To make this change I introduced an `__iter_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node` in the current one. Both `__tree::__begin_node_` and iterator pointers are now stored as `__iter_pointers`. The other situation where `__tree_end_node` is stored as the wrong type is in `__tree_node_base::__parent_`. Currently `__left_`, `__right_`, and `__parent_` are all `__tree_node_base` pointers. Since the end node will only be stored in `__parent_` the fix is to change `__parent_` to be a pointer to `__tree_end_node`. To make this change I introduced a `__parent_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node_base` in the current one. Note that in the new ABI `__iter_pointer` and `__parent_pointer` are the same type (but not in the old one). The confusion between these two types is unfortunate but it was the best solution I could come up with that maintains the ABI. The typedef changes force a ton of explicit type casts to correct pointer types and to make current code compatible with both the old and new pointer typedefs. This is the bulk of the change and it's really messy. Unfortunately I don't know how to avoid it. Please let me know what you think. Reviewers: howard.hinnant, mclow.lists Subscribers: howard.hinnant, bbannier, cfe-commits Differential Revision: https://reviews.llvm.org/D20786 llvm-svn: 276003
2016-07-19 09:56:20 -08:00
__x->__left_->__set_parent(__x);
2010-05-11 11:42:16 -08:00
__y->__parent_ = __x->__parent_;
if (std::__tree_is_left_child(__x))
2010-05-11 11:42:16 -08:00
__x->__parent_->__left_ = __y;
else
Fix undefined behavior in __tree Summary: This patch attempts to fix the undefined behavior in __tree by changing the node pointer types used throughout. The pointer types are changed for raw pointers in the current ABI and for fancy pointers in ABI V2 (since the fancy pointer types may not be ABI compatible). The UB in `__tree` arises because tree downcasts the embedded end node and then deferences that pointer. Currently there are 3 node types in __tree. * `__tree_end_node` which contains the `__left_` pointer. This node is embedded within the container. * `__tree_node_base` which contains `__right_`, `__parent_` and `__is_black`. This node is used throughout the tree rebalancing algorithms. * `__tree_node` which contains `__value_`. Currently `__tree` stores the start of the tree, `__begin_node_`, as a pointer to a `__tree_node`. Additionally the iterators store their position as a pointer to a `__tree_node`. In both of these cases the pointee can be the end node. This is fixed by changing them to store `__tree_end_node` pointers instead. To make this change I introduced an `__iter_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node` in the current one. Both `__tree::__begin_node_` and iterator pointers are now stored as `__iter_pointers`. The other situation where `__tree_end_node` is stored as the wrong type is in `__tree_node_base::__parent_`. Currently `__left_`, `__right_`, and `__parent_` are all `__tree_node_base` pointers. Since the end node will only be stored in `__parent_` the fix is to change `__parent_` to be a pointer to `__tree_end_node`. To make this change I introduced a `__parent_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node_base` in the current one. Note that in the new ABI `__iter_pointer` and `__parent_pointer` are the same type (but not in the old one). The confusion between these two types is unfortunate but it was the best solution I could come up with that maintains the ABI. The typedef changes force a ton of explicit type casts to correct pointer types and to make current code compatible with both the old and new pointer typedefs. This is the bulk of the change and it's really messy. Unfortunately I don't know how to avoid it. Please let me know what you think. Reviewers: howard.hinnant, mclow.lists Subscribers: howard.hinnant, bbannier, cfe-commits Differential Revision: https://reviews.llvm.org/D20786 llvm-svn: 276003
2016-07-19 09:56:20 -08:00
__x->__parent_unsafe()->__right_ = __y;
2010-05-11 11:42:16 -08:00
__y->__right_ = __x;
Fix undefined behavior in __tree Summary: This patch attempts to fix the undefined behavior in __tree by changing the node pointer types used throughout. The pointer types are changed for raw pointers in the current ABI and for fancy pointers in ABI V2 (since the fancy pointer types may not be ABI compatible). The UB in `__tree` arises because tree downcasts the embedded end node and then deferences that pointer. Currently there are 3 node types in __tree. * `__tree_end_node` which contains the `__left_` pointer. This node is embedded within the container. * `__tree_node_base` which contains `__right_`, `__parent_` and `__is_black`. This node is used throughout the tree rebalancing algorithms. * `__tree_node` which contains `__value_`. Currently `__tree` stores the start of the tree, `__begin_node_`, as a pointer to a `__tree_node`. Additionally the iterators store their position as a pointer to a `__tree_node`. In both of these cases the pointee can be the end node. This is fixed by changing them to store `__tree_end_node` pointers instead. To make this change I introduced an `__iter_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node` in the current one. Both `__tree::__begin_node_` and iterator pointers are now stored as `__iter_pointers`. The other situation where `__tree_end_node` is stored as the wrong type is in `__tree_node_base::__parent_`. Currently `__left_`, `__right_`, and `__parent_` are all `__tree_node_base` pointers. Since the end node will only be stored in `__parent_` the fix is to change `__parent_` to be a pointer to `__tree_end_node`. To make this change I introduced a `__parent_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node_base` in the current one. Note that in the new ABI `__iter_pointer` and `__parent_pointer` are the same type (but not in the old one). The confusion between these two types is unfortunate but it was the best solution I could come up with that maintains the ABI. The typedef changes force a ton of explicit type casts to correct pointer types and to make current code compatible with both the old and new pointer typedefs. This is the bulk of the change and it's really messy. Unfortunately I don't know how to avoid it. Please let me know what you think. Reviewers: howard.hinnant, mclow.lists Subscribers: howard.hinnant, bbannier, cfe-commits Differential Revision: https://reviews.llvm.org/D20786 llvm-svn: 276003
2016-07-19 09:56:20 -08:00
__x->__set_parent(__y);
2010-05-11 11:42:16 -08:00
}
// Effects: Rebalances __root after attaching __x to a leaf.
// Precondition: __x has no children.
2010-05-11 11:42:16 -08:00
// __x == __root or == a direct or indirect child of __root.
// If __x were to be unlinked from __root (setting __root to
// nullptr if __root == __x), __tree_invariant(__root) == true.
// Postcondition: __tree_invariant(end_node->__left_) == true. end_node->__left_
// may be different than the value passed in as __root.
template <class _NodePtr>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
__tree_balance_after_insert(_NodePtr __root, _NodePtr __x) _NOEXCEPT {
_LIBCPP_ASSERT_INTERNAL(__root != nullptr, "Root of the tree shouldn't be null");
_LIBCPP_ASSERT_INTERNAL(__x != nullptr, "Can't attach null node to a leaf");
2010-05-11 11:42:16 -08:00
__x->__is_black_ = __x == __root;
Fix undefined behavior in __tree Summary: This patch attempts to fix the undefined behavior in __tree by changing the node pointer types used throughout. The pointer types are changed for raw pointers in the current ABI and for fancy pointers in ABI V2 (since the fancy pointer types may not be ABI compatible). The UB in `__tree` arises because tree downcasts the embedded end node and then deferences that pointer. Currently there are 3 node types in __tree. * `__tree_end_node` which contains the `__left_` pointer. This node is embedded within the container. * `__tree_node_base` which contains `__right_`, `__parent_` and `__is_black`. This node is used throughout the tree rebalancing algorithms. * `__tree_node` which contains `__value_`. Currently `__tree` stores the start of the tree, `__begin_node_`, as a pointer to a `__tree_node`. Additionally the iterators store their position as a pointer to a `__tree_node`. In both of these cases the pointee can be the end node. This is fixed by changing them to store `__tree_end_node` pointers instead. To make this change I introduced an `__iter_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node` in the current one. Both `__tree::__begin_node_` and iterator pointers are now stored as `__iter_pointers`. The other situation where `__tree_end_node` is stored as the wrong type is in `__tree_node_base::__parent_`. Currently `__left_`, `__right_`, and `__parent_` are all `__tree_node_base` pointers. Since the end node will only be stored in `__parent_` the fix is to change `__parent_` to be a pointer to `__tree_end_node`. To make this change I introduced a `__parent_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node_base` in the current one. Note that in the new ABI `__iter_pointer` and `__parent_pointer` are the same type (but not in the old one). The confusion between these two types is unfortunate but it was the best solution I could come up with that maintains the ABI. The typedef changes force a ton of explicit type casts to correct pointer types and to make current code compatible with both the old and new pointer typedefs. This is the bulk of the change and it's really messy. Unfortunately I don't know how to avoid it. Please let me know what you think. Reviewers: howard.hinnant, mclow.lists Subscribers: howard.hinnant, bbannier, cfe-commits Differential Revision: https://reviews.llvm.org/D20786 llvm-svn: 276003
2016-07-19 09:56:20 -08:00
while (__x != __root && !__x->__parent_unsafe()->__is_black_) {
2010-05-11 11:42:16 -08:00
// __x->__parent_ != __root because __x->__parent_->__is_black == false
if (std::__tree_is_left_child(__x->__parent_unsafe())) {
Fix undefined behavior in __tree Summary: This patch attempts to fix the undefined behavior in __tree by changing the node pointer types used throughout. The pointer types are changed for raw pointers in the current ABI and for fancy pointers in ABI V2 (since the fancy pointer types may not be ABI compatible). The UB in `__tree` arises because tree downcasts the embedded end node and then deferences that pointer. Currently there are 3 node types in __tree. * `__tree_end_node` which contains the `__left_` pointer. This node is embedded within the container. * `__tree_node_base` which contains `__right_`, `__parent_` and `__is_black`. This node is used throughout the tree rebalancing algorithms. * `__tree_node` which contains `__value_`. Currently `__tree` stores the start of the tree, `__begin_node_`, as a pointer to a `__tree_node`. Additionally the iterators store their position as a pointer to a `__tree_node`. In both of these cases the pointee can be the end node. This is fixed by changing them to store `__tree_end_node` pointers instead. To make this change I introduced an `__iter_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node` in the current one. Both `__tree::__begin_node_` and iterator pointers are now stored as `__iter_pointers`. The other situation where `__tree_end_node` is stored as the wrong type is in `__tree_node_base::__parent_`. Currently `__left_`, `__right_`, and `__parent_` are all `__tree_node_base` pointers. Since the end node will only be stored in `__parent_` the fix is to change `__parent_` to be a pointer to `__tree_end_node`. To make this change I introduced a `__parent_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node_base` in the current one. Note that in the new ABI `__iter_pointer` and `__parent_pointer` are the same type (but not in the old one). The confusion between these two types is unfortunate but it was the best solution I could come up with that maintains the ABI. The typedef changes force a ton of explicit type casts to correct pointer types and to make current code compatible with both the old and new pointer typedefs. This is the bulk of the change and it's really messy. Unfortunately I don't know how to avoid it. Please let me know what you think. Reviewers: howard.hinnant, mclow.lists Subscribers: howard.hinnant, bbannier, cfe-commits Differential Revision: https://reviews.llvm.org/D20786 llvm-svn: 276003
2016-07-19 09:56:20 -08:00
_NodePtr __y = __x->__parent_unsafe()->__parent_unsafe()->__right_;
2010-05-11 11:42:16 -08:00
if (__y != nullptr && !__y->__is_black_) {
Fix undefined behavior in __tree Summary: This patch attempts to fix the undefined behavior in __tree by changing the node pointer types used throughout. The pointer types are changed for raw pointers in the current ABI and for fancy pointers in ABI V2 (since the fancy pointer types may not be ABI compatible). The UB in `__tree` arises because tree downcasts the embedded end node and then deferences that pointer. Currently there are 3 node types in __tree. * `__tree_end_node` which contains the `__left_` pointer. This node is embedded within the container. * `__tree_node_base` which contains `__right_`, `__parent_` and `__is_black`. This node is used throughout the tree rebalancing algorithms. * `__tree_node` which contains `__value_`. Currently `__tree` stores the start of the tree, `__begin_node_`, as a pointer to a `__tree_node`. Additionally the iterators store their position as a pointer to a `__tree_node`. In both of these cases the pointee can be the end node. This is fixed by changing them to store `__tree_end_node` pointers instead. To make this change I introduced an `__iter_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node` in the current one. Both `__tree::__begin_node_` and iterator pointers are now stored as `__iter_pointers`. The other situation where `__tree_end_node` is stored as the wrong type is in `__tree_node_base::__parent_`. Currently `__left_`, `__right_`, and `__parent_` are all `__tree_node_base` pointers. Since the end node will only be stored in `__parent_` the fix is to change `__parent_` to be a pointer to `__tree_end_node`. To make this change I introduced a `__parent_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node_base` in the current one. Note that in the new ABI `__iter_pointer` and `__parent_pointer` are the same type (but not in the old one). The confusion between these two types is unfortunate but it was the best solution I could come up with that maintains the ABI. The typedef changes force a ton of explicit type casts to correct pointer types and to make current code compatible with both the old and new pointer typedefs. This is the bulk of the change and it's really messy. Unfortunately I don't know how to avoid it. Please let me know what you think. Reviewers: howard.hinnant, mclow.lists Subscribers: howard.hinnant, bbannier, cfe-commits Differential Revision: https://reviews.llvm.org/D20786 llvm-svn: 276003
2016-07-19 09:56:20 -08:00
__x = __x->__parent_unsafe();
2010-05-11 11:42:16 -08:00
__x->__is_black_ = true;
Fix undefined behavior in __tree Summary: This patch attempts to fix the undefined behavior in __tree by changing the node pointer types used throughout. The pointer types are changed for raw pointers in the current ABI and for fancy pointers in ABI V2 (since the fancy pointer types may not be ABI compatible). The UB in `__tree` arises because tree downcasts the embedded end node and then deferences that pointer. Currently there are 3 node types in __tree. * `__tree_end_node` which contains the `__left_` pointer. This node is embedded within the container. * `__tree_node_base` which contains `__right_`, `__parent_` and `__is_black`. This node is used throughout the tree rebalancing algorithms. * `__tree_node` which contains `__value_`. Currently `__tree` stores the start of the tree, `__begin_node_`, as a pointer to a `__tree_node`. Additionally the iterators store their position as a pointer to a `__tree_node`. In both of these cases the pointee can be the end node. This is fixed by changing them to store `__tree_end_node` pointers instead. To make this change I introduced an `__iter_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node` in the current one. Both `__tree::__begin_node_` and iterator pointers are now stored as `__iter_pointers`. The other situation where `__tree_end_node` is stored as the wrong type is in `__tree_node_base::__parent_`. Currently `__left_`, `__right_`, and `__parent_` are all `__tree_node_base` pointers. Since the end node will only be stored in `__parent_` the fix is to change `__parent_` to be a pointer to `__tree_end_node`. To make this change I introduced a `__parent_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node_base` in the current one. Note that in the new ABI `__iter_pointer` and `__parent_pointer` are the same type (but not in the old one). The confusion between these two types is unfortunate but it was the best solution I could come up with that maintains the ABI. The typedef changes force a ton of explicit type casts to correct pointer types and to make current code compatible with both the old and new pointer typedefs. This is the bulk of the change and it's really messy. Unfortunately I don't know how to avoid it. Please let me know what you think. Reviewers: howard.hinnant, mclow.lists Subscribers: howard.hinnant, bbannier, cfe-commits Differential Revision: https://reviews.llvm.org/D20786 llvm-svn: 276003
2016-07-19 09:56:20 -08:00
__x = __x->__parent_unsafe();
2010-05-11 11:42:16 -08:00
__x->__is_black_ = __x == __root;
__y->__is_black_ = true;
} else {
if (!std::__tree_is_left_child(__x)) {
Fix undefined behavior in __tree Summary: This patch attempts to fix the undefined behavior in __tree by changing the node pointer types used throughout. The pointer types are changed for raw pointers in the current ABI and for fancy pointers in ABI V2 (since the fancy pointer types may not be ABI compatible). The UB in `__tree` arises because tree downcasts the embedded end node and then deferences that pointer. Currently there are 3 node types in __tree. * `__tree_end_node` which contains the `__left_` pointer. This node is embedded within the container. * `__tree_node_base` which contains `__right_`, `__parent_` and `__is_black`. This node is used throughout the tree rebalancing algorithms. * `__tree_node` which contains `__value_`. Currently `__tree` stores the start of the tree, `__begin_node_`, as a pointer to a `__tree_node`. Additionally the iterators store their position as a pointer to a `__tree_node`. In both of these cases the pointee can be the end node. This is fixed by changing them to store `__tree_end_node` pointers instead. To make this change I introduced an `__iter_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node` in the current one. Both `__tree::__begin_node_` and iterator pointers are now stored as `__iter_pointers`. The other situation where `__tree_end_node` is stored as the wrong type is in `__tree_node_base::__parent_`. Currently `__left_`, `__right_`, and `__parent_` are all `__tree_node_base` pointers. Since the end node will only be stored in `__parent_` the fix is to change `__parent_` to be a pointer to `__tree_end_node`. To make this change I introduced a `__parent_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node_base` in the current one. Note that in the new ABI `__iter_pointer` and `__parent_pointer` are the same type (but not in the old one). The confusion between these two types is unfortunate but it was the best solution I could come up with that maintains the ABI. The typedef changes force a ton of explicit type casts to correct pointer types and to make current code compatible with both the old and new pointer typedefs. This is the bulk of the change and it's really messy. Unfortunately I don't know how to avoid it. Please let me know what you think. Reviewers: howard.hinnant, mclow.lists Subscribers: howard.hinnant, bbannier, cfe-commits Differential Revision: https://reviews.llvm.org/D20786 llvm-svn: 276003
2016-07-19 09:56:20 -08:00
__x = __x->__parent_unsafe();
std::__tree_left_rotate(__x);
2010-05-11 11:42:16 -08:00
}
Fix undefined behavior in __tree Summary: This patch attempts to fix the undefined behavior in __tree by changing the node pointer types used throughout. The pointer types are changed for raw pointers in the current ABI and for fancy pointers in ABI V2 (since the fancy pointer types may not be ABI compatible). The UB in `__tree` arises because tree downcasts the embedded end node and then deferences that pointer. Currently there are 3 node types in __tree. * `__tree_end_node` which contains the `__left_` pointer. This node is embedded within the container. * `__tree_node_base` which contains `__right_`, `__parent_` and `__is_black`. This node is used throughout the tree rebalancing algorithms. * `__tree_node` which contains `__value_`. Currently `__tree` stores the start of the tree, `__begin_node_`, as a pointer to a `__tree_node`. Additionally the iterators store their position as a pointer to a `__tree_node`. In both of these cases the pointee can be the end node. This is fixed by changing them to store `__tree_end_node` pointers instead. To make this change I introduced an `__iter_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node` in the current one. Both `__tree::__begin_node_` and iterator pointers are now stored as `__iter_pointers`. The other situation where `__tree_end_node` is stored as the wrong type is in `__tree_node_base::__parent_`. Currently `__left_`, `__right_`, and `__parent_` are all `__tree_node_base` pointers. Since the end node will only be stored in `__parent_` the fix is to change `__parent_` to be a pointer to `__tree_end_node`. To make this change I introduced a `__parent_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node_base` in the current one. Note that in the new ABI `__iter_pointer` and `__parent_pointer` are the same type (but not in the old one). The confusion between these two types is unfortunate but it was the best solution I could come up with that maintains the ABI. The typedef changes force a ton of explicit type casts to correct pointer types and to make current code compatible with both the old and new pointer typedefs. This is the bulk of the change and it's really messy. Unfortunately I don't know how to avoid it. Please let me know what you think. Reviewers: howard.hinnant, mclow.lists Subscribers: howard.hinnant, bbannier, cfe-commits Differential Revision: https://reviews.llvm.org/D20786 llvm-svn: 276003
2016-07-19 09:56:20 -08:00
__x = __x->__parent_unsafe();
2010-05-11 11:42:16 -08:00
__x->__is_black_ = true;
Fix undefined behavior in __tree Summary: This patch attempts to fix the undefined behavior in __tree by changing the node pointer types used throughout. The pointer types are changed for raw pointers in the current ABI and for fancy pointers in ABI V2 (since the fancy pointer types may not be ABI compatible). The UB in `__tree` arises because tree downcasts the embedded end node and then deferences that pointer. Currently there are 3 node types in __tree. * `__tree_end_node` which contains the `__left_` pointer. This node is embedded within the container. * `__tree_node_base` which contains `__right_`, `__parent_` and `__is_black`. This node is used throughout the tree rebalancing algorithms. * `__tree_node` which contains `__value_`. Currently `__tree` stores the start of the tree, `__begin_node_`, as a pointer to a `__tree_node`. Additionally the iterators store their position as a pointer to a `__tree_node`. In both of these cases the pointee can be the end node. This is fixed by changing them to store `__tree_end_node` pointers instead. To make this change I introduced an `__iter_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node` in the current one. Both `__tree::__begin_node_` and iterator pointers are now stored as `__iter_pointers`. The other situation where `__tree_end_node` is stored as the wrong type is in `__tree_node_base::__parent_`. Currently `__left_`, `__right_`, and `__parent_` are all `__tree_node_base` pointers. Since the end node will only be stored in `__parent_` the fix is to change `__parent_` to be a pointer to `__tree_end_node`. To make this change I introduced a `__parent_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node_base` in the current one. Note that in the new ABI `__iter_pointer` and `__parent_pointer` are the same type (but not in the old one). The confusion between these two types is unfortunate but it was the best solution I could come up with that maintains the ABI. The typedef changes force a ton of explicit type casts to correct pointer types and to make current code compatible with both the old and new pointer typedefs. This is the bulk of the change and it's really messy. Unfortunately I don't know how to avoid it. Please let me know what you think. Reviewers: howard.hinnant, mclow.lists Subscribers: howard.hinnant, bbannier, cfe-commits Differential Revision: https://reviews.llvm.org/D20786 llvm-svn: 276003
2016-07-19 09:56:20 -08:00
__x = __x->__parent_unsafe();
2010-05-11 11:42:16 -08:00
__x->__is_black_ = false;
std::__tree_right_rotate(__x);
break;
}
2010-05-11 11:42:16 -08:00
} else {
Fix undefined behavior in __tree Summary: This patch attempts to fix the undefined behavior in __tree by changing the node pointer types used throughout. The pointer types are changed for raw pointers in the current ABI and for fancy pointers in ABI V2 (since the fancy pointer types may not be ABI compatible). The UB in `__tree` arises because tree downcasts the embedded end node and then deferences that pointer. Currently there are 3 node types in __tree. * `__tree_end_node` which contains the `__left_` pointer. This node is embedded within the container. * `__tree_node_base` which contains `__right_`, `__parent_` and `__is_black`. This node is used throughout the tree rebalancing algorithms. * `__tree_node` which contains `__value_`. Currently `__tree` stores the start of the tree, `__begin_node_`, as a pointer to a `__tree_node`. Additionally the iterators store their position as a pointer to a `__tree_node`. In both of these cases the pointee can be the end node. This is fixed by changing them to store `__tree_end_node` pointers instead. To make this change I introduced an `__iter_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node` in the current one. Both `__tree::__begin_node_` and iterator pointers are now stored as `__iter_pointers`. The other situation where `__tree_end_node` is stored as the wrong type is in `__tree_node_base::__parent_`. Currently `__left_`, `__right_`, and `__parent_` are all `__tree_node_base` pointers. Since the end node will only be stored in `__parent_` the fix is to change `__parent_` to be a pointer to `__tree_end_node`. To make this change I introduced a `__parent_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node_base` in the current one. Note that in the new ABI `__iter_pointer` and `__parent_pointer` are the same type (but not in the old one). The confusion between these two types is unfortunate but it was the best solution I could come up with that maintains the ABI. The typedef changes force a ton of explicit type casts to correct pointer types and to make current code compatible with both the old and new pointer typedefs. This is the bulk of the change and it's really messy. Unfortunately I don't know how to avoid it. Please let me know what you think. Reviewers: howard.hinnant, mclow.lists Subscribers: howard.hinnant, bbannier, cfe-commits Differential Revision: https://reviews.llvm.org/D20786 llvm-svn: 276003
2016-07-19 09:56:20 -08:00
_NodePtr __y = __x->__parent_unsafe()->__parent_->__left_;
2010-05-11 11:42:16 -08:00
if (__y != nullptr && !__y->__is_black_) {
Fix undefined behavior in __tree Summary: This patch attempts to fix the undefined behavior in __tree by changing the node pointer types used throughout. The pointer types are changed for raw pointers in the current ABI and for fancy pointers in ABI V2 (since the fancy pointer types may not be ABI compatible). The UB in `__tree` arises because tree downcasts the embedded end node and then deferences that pointer. Currently there are 3 node types in __tree. * `__tree_end_node` which contains the `__left_` pointer. This node is embedded within the container. * `__tree_node_base` which contains `__right_`, `__parent_` and `__is_black`. This node is used throughout the tree rebalancing algorithms. * `__tree_node` which contains `__value_`. Currently `__tree` stores the start of the tree, `__begin_node_`, as a pointer to a `__tree_node`. Additionally the iterators store their position as a pointer to a `__tree_node`. In both of these cases the pointee can be the end node. This is fixed by changing them to store `__tree_end_node` pointers instead. To make this change I introduced an `__iter_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node` in the current one. Both `__tree::__begin_node_` and iterator pointers are now stored as `__iter_pointers`. The other situation where `__tree_end_node` is stored as the wrong type is in `__tree_node_base::__parent_`. Currently `__left_`, `__right_`, and `__parent_` are all `__tree_node_base` pointers. Since the end node will only be stored in `__parent_` the fix is to change `__parent_` to be a pointer to `__tree_end_node`. To make this change I introduced a `__parent_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node_base` in the current one. Note that in the new ABI `__iter_pointer` and `__parent_pointer` are the same type (but not in the old one). The confusion between these two types is unfortunate but it was the best solution I could come up with that maintains the ABI. The typedef changes force a ton of explicit type casts to correct pointer types and to make current code compatible with both the old and new pointer typedefs. This is the bulk of the change and it's really messy. Unfortunately I don't know how to avoid it. Please let me know what you think. Reviewers: howard.hinnant, mclow.lists Subscribers: howard.hinnant, bbannier, cfe-commits Differential Revision: https://reviews.llvm.org/D20786 llvm-svn: 276003
2016-07-19 09:56:20 -08:00
__x = __x->__parent_unsafe();
2010-05-11 11:42:16 -08:00
__x->__is_black_ = true;
Fix undefined behavior in __tree Summary: This patch attempts to fix the undefined behavior in __tree by changing the node pointer types used throughout. The pointer types are changed for raw pointers in the current ABI and for fancy pointers in ABI V2 (since the fancy pointer types may not be ABI compatible). The UB in `__tree` arises because tree downcasts the embedded end node and then deferences that pointer. Currently there are 3 node types in __tree. * `__tree_end_node` which contains the `__left_` pointer. This node is embedded within the container. * `__tree_node_base` which contains `__right_`, `__parent_` and `__is_black`. This node is used throughout the tree rebalancing algorithms. * `__tree_node` which contains `__value_`. Currently `__tree` stores the start of the tree, `__begin_node_`, as a pointer to a `__tree_node`. Additionally the iterators store their position as a pointer to a `__tree_node`. In both of these cases the pointee can be the end node. This is fixed by changing them to store `__tree_end_node` pointers instead. To make this change I introduced an `__iter_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node` in the current one. Both `__tree::__begin_node_` and iterator pointers are now stored as `__iter_pointers`. The other situation where `__tree_end_node` is stored as the wrong type is in `__tree_node_base::__parent_`. Currently `__left_`, `__right_`, and `__parent_` are all `__tree_node_base` pointers. Since the end node will only be stored in `__parent_` the fix is to change `__parent_` to be a pointer to `__tree_end_node`. To make this change I introduced a `__parent_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node_base` in the current one. Note that in the new ABI `__iter_pointer` and `__parent_pointer` are the same type (but not in the old one). The confusion between these two types is unfortunate but it was the best solution I could come up with that maintains the ABI. The typedef changes force a ton of explicit type casts to correct pointer types and to make current code compatible with both the old and new pointer typedefs. This is the bulk of the change and it's really messy. Unfortunately I don't know how to avoid it. Please let me know what you think. Reviewers: howard.hinnant, mclow.lists Subscribers: howard.hinnant, bbannier, cfe-commits Differential Revision: https://reviews.llvm.org/D20786 llvm-svn: 276003
2016-07-19 09:56:20 -08:00
__x = __x->__parent_unsafe();
2010-05-11 11:42:16 -08:00
__x->__is_black_ = __x == __root;
__y->__is_black_ = true;
} else {
if (std::__tree_is_left_child(__x)) {
Fix undefined behavior in __tree Summary: This patch attempts to fix the undefined behavior in __tree by changing the node pointer types used throughout. The pointer types are changed for raw pointers in the current ABI and for fancy pointers in ABI V2 (since the fancy pointer types may not be ABI compatible). The UB in `__tree` arises because tree downcasts the embedded end node and then deferences that pointer. Currently there are 3 node types in __tree. * `__tree_end_node` which contains the `__left_` pointer. This node is embedded within the container. * `__tree_node_base` which contains `__right_`, `__parent_` and `__is_black`. This node is used throughout the tree rebalancing algorithms. * `__tree_node` which contains `__value_`. Currently `__tree` stores the start of the tree, `__begin_node_`, as a pointer to a `__tree_node`. Additionally the iterators store their position as a pointer to a `__tree_node`. In both of these cases the pointee can be the end node. This is fixed by changing them to store `__tree_end_node` pointers instead. To make this change I introduced an `__iter_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node` in the current one. Both `__tree::__begin_node_` and iterator pointers are now stored as `__iter_pointers`. The other situation where `__tree_end_node` is stored as the wrong type is in `__tree_node_base::__parent_`. Currently `__left_`, `__right_`, and `__parent_` are all `__tree_node_base` pointers. Since the end node will only be stored in `__parent_` the fix is to change `__parent_` to be a pointer to `__tree_end_node`. To make this change I introduced a `__parent_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node_base` in the current one. Note that in the new ABI `__iter_pointer` and `__parent_pointer` are the same type (but not in the old one). The confusion between these two types is unfortunate but it was the best solution I could come up with that maintains the ABI. The typedef changes force a ton of explicit type casts to correct pointer types and to make current code compatible with both the old and new pointer typedefs. This is the bulk of the change and it's really messy. Unfortunately I don't know how to avoid it. Please let me know what you think. Reviewers: howard.hinnant, mclow.lists Subscribers: howard.hinnant, bbannier, cfe-commits Differential Revision: https://reviews.llvm.org/D20786 llvm-svn: 276003
2016-07-19 09:56:20 -08:00
__x = __x->__parent_unsafe();
std::__tree_right_rotate(__x);
2010-05-11 11:42:16 -08:00
}
Fix undefined behavior in __tree Summary: This patch attempts to fix the undefined behavior in __tree by changing the node pointer types used throughout. The pointer types are changed for raw pointers in the current ABI and for fancy pointers in ABI V2 (since the fancy pointer types may not be ABI compatible). The UB in `__tree` arises because tree downcasts the embedded end node and then deferences that pointer. Currently there are 3 node types in __tree. * `__tree_end_node` which contains the `__left_` pointer. This node is embedded within the container. * `__tree_node_base` which contains `__right_`, `__parent_` and `__is_black`. This node is used throughout the tree rebalancing algorithms. * `__tree_node` which contains `__value_`. Currently `__tree` stores the start of the tree, `__begin_node_`, as a pointer to a `__tree_node`. Additionally the iterators store their position as a pointer to a `__tree_node`. In both of these cases the pointee can be the end node. This is fixed by changing them to store `__tree_end_node` pointers instead. To make this change I introduced an `__iter_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node` in the current one. Both `__tree::__begin_node_` and iterator pointers are now stored as `__iter_pointers`. The other situation where `__tree_end_node` is stored as the wrong type is in `__tree_node_base::__parent_`. Currently `__left_`, `__right_`, and `__parent_` are all `__tree_node_base` pointers. Since the end node will only be stored in `__parent_` the fix is to change `__parent_` to be a pointer to `__tree_end_node`. To make this change I introduced a `__parent_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node_base` in the current one. Note that in the new ABI `__iter_pointer` and `__parent_pointer` are the same type (but not in the old one). The confusion between these two types is unfortunate but it was the best solution I could come up with that maintains the ABI. The typedef changes force a ton of explicit type casts to correct pointer types and to make current code compatible with both the old and new pointer typedefs. This is the bulk of the change and it's really messy. Unfortunately I don't know how to avoid it. Please let me know what you think. Reviewers: howard.hinnant, mclow.lists Subscribers: howard.hinnant, bbannier, cfe-commits Differential Revision: https://reviews.llvm.org/D20786 llvm-svn: 276003
2016-07-19 09:56:20 -08:00
__x = __x->__parent_unsafe();
2010-05-11 11:42:16 -08:00
__x->__is_black_ = true;
Fix undefined behavior in __tree Summary: This patch attempts to fix the undefined behavior in __tree by changing the node pointer types used throughout. The pointer types are changed for raw pointers in the current ABI and for fancy pointers in ABI V2 (since the fancy pointer types may not be ABI compatible). The UB in `__tree` arises because tree downcasts the embedded end node and then deferences that pointer. Currently there are 3 node types in __tree. * `__tree_end_node` which contains the `__left_` pointer. This node is embedded within the container. * `__tree_node_base` which contains `__right_`, `__parent_` and `__is_black`. This node is used throughout the tree rebalancing algorithms. * `__tree_node` which contains `__value_`. Currently `__tree` stores the start of the tree, `__begin_node_`, as a pointer to a `__tree_node`. Additionally the iterators store their position as a pointer to a `__tree_node`. In both of these cases the pointee can be the end node. This is fixed by changing them to store `__tree_end_node` pointers instead. To make this change I introduced an `__iter_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node` in the current one. Both `__tree::__begin_node_` and iterator pointers are now stored as `__iter_pointers`. The other situation where `__tree_end_node` is stored as the wrong type is in `__tree_node_base::__parent_`. Currently `__left_`, `__right_`, and `__parent_` are all `__tree_node_base` pointers. Since the end node will only be stored in `__parent_` the fix is to change `__parent_` to be a pointer to `__tree_end_node`. To make this change I introduced a `__parent_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node_base` in the current one. Note that in the new ABI `__iter_pointer` and `__parent_pointer` are the same type (but not in the old one). The confusion between these two types is unfortunate but it was the best solution I could come up with that maintains the ABI. The typedef changes force a ton of explicit type casts to correct pointer types and to make current code compatible with both the old and new pointer typedefs. This is the bulk of the change and it's really messy. Unfortunately I don't know how to avoid it. Please let me know what you think. Reviewers: howard.hinnant, mclow.lists Subscribers: howard.hinnant, bbannier, cfe-commits Differential Revision: https://reviews.llvm.org/D20786 llvm-svn: 276003
2016-07-19 09:56:20 -08:00
__x = __x->__parent_unsafe();
2010-05-11 11:42:16 -08:00
__x->__is_black_ = false;
std::__tree_left_rotate(__x);
break;
}
2010-05-11 11:42:16 -08:00
}
}
2010-05-11 11:42:16 -08:00
}
// Precondition: __z == __root or == a direct or indirect child of __root.
2010-05-11 11:42:16 -08:00
// Effects: unlinks __z from the tree rooted at __root, rebalancing as needed.
// Postcondition: __tree_invariant(end_node->__left_) == true && end_node->__left_
// nor any of its children refer to __z. end_node->__left_
// may be different than the value passed in as __root.
template <class _NodePtr>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __tree_remove(_NodePtr __root, _NodePtr __z) _NOEXCEPT {
_LIBCPP_ASSERT_INTERNAL(__root != nullptr, "Root node should not be null");
_LIBCPP_ASSERT_INTERNAL(__z != nullptr, "The node to remove should not be null");
_LIBCPP_ASSERT_INTERNAL(std::__tree_invariant(__root), "The tree invariants should hold");
2010-05-11 11:42:16 -08:00
// __z will be removed from the tree. Client still needs to destruct/deallocate it
// __y is either __z, or if __z has two children, __tree_next(__z).
// __y will have at most one child.
// __y will be the initial hole in the tree (make the hole at a leaf)
_NodePtr __y = (__z->__left_ == nullptr || __z->__right_ == nullptr) ? __z : std::__tree_next(__z);
// __x is __y's possibly null single child
_NodePtr __x = __y->__left_ != nullptr ? __y->__left_ : __y->__right_;
// __w is __x's possibly null uncle (will become __x's sibling)
_NodePtr __w = nullptr;
// link __x to __y's parent, and find __w
if (__x != nullptr)
__x->__parent_ = __y->__parent_;
if (std::__tree_is_left_child(__y)) {
2010-05-11 11:42:16 -08:00
__y->__parent_->__left_ = __x;
if (__y != __root)
Fix undefined behavior in __tree Summary: This patch attempts to fix the undefined behavior in __tree by changing the node pointer types used throughout. The pointer types are changed for raw pointers in the current ABI and for fancy pointers in ABI V2 (since the fancy pointer types may not be ABI compatible). The UB in `__tree` arises because tree downcasts the embedded end node and then deferences that pointer. Currently there are 3 node types in __tree. * `__tree_end_node` which contains the `__left_` pointer. This node is embedded within the container. * `__tree_node_base` which contains `__right_`, `__parent_` and `__is_black`. This node is used throughout the tree rebalancing algorithms. * `__tree_node` which contains `__value_`. Currently `__tree` stores the start of the tree, `__begin_node_`, as a pointer to a `__tree_node`. Additionally the iterators store their position as a pointer to a `__tree_node`. In both of these cases the pointee can be the end node. This is fixed by changing them to store `__tree_end_node` pointers instead. To make this change I introduced an `__iter_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node` in the current one. Both `__tree::__begin_node_` and iterator pointers are now stored as `__iter_pointers`. The other situation where `__tree_end_node` is stored as the wrong type is in `__tree_node_base::__parent_`. Currently `__left_`, `__right_`, and `__parent_` are all `__tree_node_base` pointers. Since the end node will only be stored in `__parent_` the fix is to change `__parent_` to be a pointer to `__tree_end_node`. To make this change I introduced a `__parent_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node_base` in the current one. Note that in the new ABI `__iter_pointer` and `__parent_pointer` are the same type (but not in the old one). The confusion between these two types is unfortunate but it was the best solution I could come up with that maintains the ABI. The typedef changes force a ton of explicit type casts to correct pointer types and to make current code compatible with both the old and new pointer typedefs. This is the bulk of the change and it's really messy. Unfortunately I don't know how to avoid it. Please let me know what you think. Reviewers: howard.hinnant, mclow.lists Subscribers: howard.hinnant, bbannier, cfe-commits Differential Revision: https://reviews.llvm.org/D20786 llvm-svn: 276003
2016-07-19 09:56:20 -08:00
__w = __y->__parent_unsafe()->__right_;
2010-05-11 11:42:16 -08:00
else
__root = __x; // __w == nullptr
} else {
Fix undefined behavior in __tree Summary: This patch attempts to fix the undefined behavior in __tree by changing the node pointer types used throughout. The pointer types are changed for raw pointers in the current ABI and for fancy pointers in ABI V2 (since the fancy pointer types may not be ABI compatible). The UB in `__tree` arises because tree downcasts the embedded end node and then deferences that pointer. Currently there are 3 node types in __tree. * `__tree_end_node` which contains the `__left_` pointer. This node is embedded within the container. * `__tree_node_base` which contains `__right_`, `__parent_` and `__is_black`. This node is used throughout the tree rebalancing algorithms. * `__tree_node` which contains `__value_`. Currently `__tree` stores the start of the tree, `__begin_node_`, as a pointer to a `__tree_node`. Additionally the iterators store their position as a pointer to a `__tree_node`. In both of these cases the pointee can be the end node. This is fixed by changing them to store `__tree_end_node` pointers instead. To make this change I introduced an `__iter_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node` in the current one. Both `__tree::__begin_node_` and iterator pointers are now stored as `__iter_pointers`. The other situation where `__tree_end_node` is stored as the wrong type is in `__tree_node_base::__parent_`. Currently `__left_`, `__right_`, and `__parent_` are all `__tree_node_base` pointers. Since the end node will only be stored in `__parent_` the fix is to change `__parent_` to be a pointer to `__tree_end_node`. To make this change I introduced a `__parent_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node_base` in the current one. Note that in the new ABI `__iter_pointer` and `__parent_pointer` are the same type (but not in the old one). The confusion between these two types is unfortunate but it was the best solution I could come up with that maintains the ABI. The typedef changes force a ton of explicit type casts to correct pointer types and to make current code compatible with both the old and new pointer typedefs. This is the bulk of the change and it's really messy. Unfortunately I don't know how to avoid it. Please let me know what you think. Reviewers: howard.hinnant, mclow.lists Subscribers: howard.hinnant, bbannier, cfe-commits Differential Revision: https://reviews.llvm.org/D20786 llvm-svn: 276003
2016-07-19 09:56:20 -08:00
__y->__parent_unsafe()->__right_ = __x;
2010-05-11 11:42:16 -08:00
// __y can't be root if it is a right child
__w = __y->__parent_->__left_;
}
bool __removed_black = __y->__is_black_;
// If we didn't remove __z, do so now by splicing in __y for __z,
// but copy __z's color. This does not impact __x or __w.
if (__y != __z) {
// __z->__left_ != nulptr but __z->__right_ might == __x == nullptr
2010-05-11 11:42:16 -08:00
__y->__parent_ = __z->__parent_;
if (std::__tree_is_left_child(__z))
2010-05-11 11:42:16 -08:00
__y->__parent_->__left_ = __y;
else
Fix undefined behavior in __tree Summary: This patch attempts to fix the undefined behavior in __tree by changing the node pointer types used throughout. The pointer types are changed for raw pointers in the current ABI and for fancy pointers in ABI V2 (since the fancy pointer types may not be ABI compatible). The UB in `__tree` arises because tree downcasts the embedded end node and then deferences that pointer. Currently there are 3 node types in __tree. * `__tree_end_node` which contains the `__left_` pointer. This node is embedded within the container. * `__tree_node_base` which contains `__right_`, `__parent_` and `__is_black`. This node is used throughout the tree rebalancing algorithms. * `__tree_node` which contains `__value_`. Currently `__tree` stores the start of the tree, `__begin_node_`, as a pointer to a `__tree_node`. Additionally the iterators store their position as a pointer to a `__tree_node`. In both of these cases the pointee can be the end node. This is fixed by changing them to store `__tree_end_node` pointers instead. To make this change I introduced an `__iter_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node` in the current one. Both `__tree::__begin_node_` and iterator pointers are now stored as `__iter_pointers`. The other situation where `__tree_end_node` is stored as the wrong type is in `__tree_node_base::__parent_`. Currently `__left_`, `__right_`, and `__parent_` are all `__tree_node_base` pointers. Since the end node will only be stored in `__parent_` the fix is to change `__parent_` to be a pointer to `__tree_end_node`. To make this change I introduced a `__parent_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node_base` in the current one. Note that in the new ABI `__iter_pointer` and `__parent_pointer` are the same type (but not in the old one). The confusion between these two types is unfortunate but it was the best solution I could come up with that maintains the ABI. The typedef changes force a ton of explicit type casts to correct pointer types and to make current code compatible with both the old and new pointer typedefs. This is the bulk of the change and it's really messy. Unfortunately I don't know how to avoid it. Please let me know what you think. Reviewers: howard.hinnant, mclow.lists Subscribers: howard.hinnant, bbannier, cfe-commits Differential Revision: https://reviews.llvm.org/D20786 llvm-svn: 276003
2016-07-19 09:56:20 -08:00
__y->__parent_unsafe()->__right_ = __y;
2010-05-11 11:42:16 -08:00
__y->__left_ = __z->__left_;
Fix undefined behavior in __tree Summary: This patch attempts to fix the undefined behavior in __tree by changing the node pointer types used throughout. The pointer types are changed for raw pointers in the current ABI and for fancy pointers in ABI V2 (since the fancy pointer types may not be ABI compatible). The UB in `__tree` arises because tree downcasts the embedded end node and then deferences that pointer. Currently there are 3 node types in __tree. * `__tree_end_node` which contains the `__left_` pointer. This node is embedded within the container. * `__tree_node_base` which contains `__right_`, `__parent_` and `__is_black`. This node is used throughout the tree rebalancing algorithms. * `__tree_node` which contains `__value_`. Currently `__tree` stores the start of the tree, `__begin_node_`, as a pointer to a `__tree_node`. Additionally the iterators store their position as a pointer to a `__tree_node`. In both of these cases the pointee can be the end node. This is fixed by changing them to store `__tree_end_node` pointers instead. To make this change I introduced an `__iter_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node` in the current one. Both `__tree::__begin_node_` and iterator pointers are now stored as `__iter_pointers`. The other situation where `__tree_end_node` is stored as the wrong type is in `__tree_node_base::__parent_`. Currently `__left_`, `__right_`, and `__parent_` are all `__tree_node_base` pointers. Since the end node will only be stored in `__parent_` the fix is to change `__parent_` to be a pointer to `__tree_end_node`. To make this change I introduced a `__parent_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node_base` in the current one. Note that in the new ABI `__iter_pointer` and `__parent_pointer` are the same type (but not in the old one). The confusion between these two types is unfortunate but it was the best solution I could come up with that maintains the ABI. The typedef changes force a ton of explicit type casts to correct pointer types and to make current code compatible with both the old and new pointer typedefs. This is the bulk of the change and it's really messy. Unfortunately I don't know how to avoid it. Please let me know what you think. Reviewers: howard.hinnant, mclow.lists Subscribers: howard.hinnant, bbannier, cfe-commits Differential Revision: https://reviews.llvm.org/D20786 llvm-svn: 276003
2016-07-19 09:56:20 -08:00
__y->__left_->__set_parent(__y);
2010-05-11 11:42:16 -08:00
__y->__right_ = __z->__right_;
if (__y->__right_ != nullptr)
Fix undefined behavior in __tree Summary: This patch attempts to fix the undefined behavior in __tree by changing the node pointer types used throughout. The pointer types are changed for raw pointers in the current ABI and for fancy pointers in ABI V2 (since the fancy pointer types may not be ABI compatible). The UB in `__tree` arises because tree downcasts the embedded end node and then deferences that pointer. Currently there are 3 node types in __tree. * `__tree_end_node` which contains the `__left_` pointer. This node is embedded within the container. * `__tree_node_base` which contains `__right_`, `__parent_` and `__is_black`. This node is used throughout the tree rebalancing algorithms. * `__tree_node` which contains `__value_`. Currently `__tree` stores the start of the tree, `__begin_node_`, as a pointer to a `__tree_node`. Additionally the iterators store their position as a pointer to a `__tree_node`. In both of these cases the pointee can be the end node. This is fixed by changing them to store `__tree_end_node` pointers instead. To make this change I introduced an `__iter_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node` in the current one. Both `__tree::__begin_node_` and iterator pointers are now stored as `__iter_pointers`. The other situation where `__tree_end_node` is stored as the wrong type is in `__tree_node_base::__parent_`. Currently `__left_`, `__right_`, and `__parent_` are all `__tree_node_base` pointers. Since the end node will only be stored in `__parent_` the fix is to change `__parent_` to be a pointer to `__tree_end_node`. To make this change I introduced a `__parent_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node_base` in the current one. Note that in the new ABI `__iter_pointer` and `__parent_pointer` are the same type (but not in the old one). The confusion between these two types is unfortunate but it was the best solution I could come up with that maintains the ABI. The typedef changes force a ton of explicit type casts to correct pointer types and to make current code compatible with both the old and new pointer typedefs. This is the bulk of the change and it's really messy. Unfortunately I don't know how to avoid it. Please let me know what you think. Reviewers: howard.hinnant, mclow.lists Subscribers: howard.hinnant, bbannier, cfe-commits Differential Revision: https://reviews.llvm.org/D20786 llvm-svn: 276003
2016-07-19 09:56:20 -08:00
__y->__right_->__set_parent(__y);
2010-05-11 11:42:16 -08:00
__y->__is_black_ = __z->__is_black_;
if (__root == __z)
__root = __y;
}
// There is no need to rebalance if we removed a red, or if we removed
// the last node.
if (__removed_black && __root != nullptr) {
// Rebalance:
// __x has an implicit black color (transferred from the removed __y)
// associated with it, no matter what its color is.
// If __x is __root (in which case it can't be null), it is supposed
// to be black anyway, and if it is doubly black, then the double
// can just be ignored.
// If __x is red (in which case it can't be null), then it can absorb
// the implicit black just by setting its color to black.
// Since __y was black and only had one child (which __x points to), __x
// is either red with no children, else null, otherwise __y would have
// different black heights under left and right pointers.
// if (__x == __root || __x != nullptr && !__x->__is_black_)
if (__x != nullptr)
__x->__is_black_ = true;
else {
2010-05-11 11:42:16 -08:00
// Else __x isn't root, and is "doubly black", even though it may
// be null. __w can not be null here, else the parent would
// see a black height >= 2 on the __x side and a black height
// of 1 on the __w side (__w must be a non-null black or a red
// with a non-null black child).
while (true) {
if (!std::__tree_is_left_child(__w)) // if x is left child
2010-05-11 11:42:16 -08:00
{
if (!__w->__is_black_) {
__w->__is_black_ = true;
Fix undefined behavior in __tree Summary: This patch attempts to fix the undefined behavior in __tree by changing the node pointer types used throughout. The pointer types are changed for raw pointers in the current ABI and for fancy pointers in ABI V2 (since the fancy pointer types may not be ABI compatible). The UB in `__tree` arises because tree downcasts the embedded end node and then deferences that pointer. Currently there are 3 node types in __tree. * `__tree_end_node` which contains the `__left_` pointer. This node is embedded within the container. * `__tree_node_base` which contains `__right_`, `__parent_` and `__is_black`. This node is used throughout the tree rebalancing algorithms. * `__tree_node` which contains `__value_`. Currently `__tree` stores the start of the tree, `__begin_node_`, as a pointer to a `__tree_node`. Additionally the iterators store their position as a pointer to a `__tree_node`. In both of these cases the pointee can be the end node. This is fixed by changing them to store `__tree_end_node` pointers instead. To make this change I introduced an `__iter_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node` in the current one. Both `__tree::__begin_node_` and iterator pointers are now stored as `__iter_pointers`. The other situation where `__tree_end_node` is stored as the wrong type is in `__tree_node_base::__parent_`. Currently `__left_`, `__right_`, and `__parent_` are all `__tree_node_base` pointers. Since the end node will only be stored in `__parent_` the fix is to change `__parent_` to be a pointer to `__tree_end_node`. To make this change I introduced a `__parent_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node_base` in the current one. Note that in the new ABI `__iter_pointer` and `__parent_pointer` are the same type (but not in the old one). The confusion between these two types is unfortunate but it was the best solution I could come up with that maintains the ABI. The typedef changes force a ton of explicit type casts to correct pointer types and to make current code compatible with both the old and new pointer typedefs. This is the bulk of the change and it's really messy. Unfortunately I don't know how to avoid it. Please let me know what you think. Reviewers: howard.hinnant, mclow.lists Subscribers: howard.hinnant, bbannier, cfe-commits Differential Revision: https://reviews.llvm.org/D20786 llvm-svn: 276003
2016-07-19 09:56:20 -08:00
__w->__parent_unsafe()->__is_black_ = false;
std::__tree_left_rotate(__w->__parent_unsafe());
2010-05-11 11:42:16 -08:00
// __x is still valid
// reset __root only if necessary
if (__root == __w->__left_)
__root = __w;
// reset sibling, and it still can't be null
__w = __w->__left_->__right_;
}
// __w->__is_black_ is now true, __w may have null children
if ((__w->__left_ == nullptr || __w->__left_->__is_black_) &&
(__w->__right_ == nullptr || __w->__right_->__is_black_)) {
__w->__is_black_ = false;
Fix undefined behavior in __tree Summary: This patch attempts to fix the undefined behavior in __tree by changing the node pointer types used throughout. The pointer types are changed for raw pointers in the current ABI and for fancy pointers in ABI V2 (since the fancy pointer types may not be ABI compatible). The UB in `__tree` arises because tree downcasts the embedded end node and then deferences that pointer. Currently there are 3 node types in __tree. * `__tree_end_node` which contains the `__left_` pointer. This node is embedded within the container. * `__tree_node_base` which contains `__right_`, `__parent_` and `__is_black`. This node is used throughout the tree rebalancing algorithms. * `__tree_node` which contains `__value_`. Currently `__tree` stores the start of the tree, `__begin_node_`, as a pointer to a `__tree_node`. Additionally the iterators store their position as a pointer to a `__tree_node`. In both of these cases the pointee can be the end node. This is fixed by changing them to store `__tree_end_node` pointers instead. To make this change I introduced an `__iter_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node` in the current one. Both `__tree::__begin_node_` and iterator pointers are now stored as `__iter_pointers`. The other situation where `__tree_end_node` is stored as the wrong type is in `__tree_node_base::__parent_`. Currently `__left_`, `__right_`, and `__parent_` are all `__tree_node_base` pointers. Since the end node will only be stored in `__parent_` the fix is to change `__parent_` to be a pointer to `__tree_end_node`. To make this change I introduced a `__parent_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node_base` in the current one. Note that in the new ABI `__iter_pointer` and `__parent_pointer` are the same type (but not in the old one). The confusion between these two types is unfortunate but it was the best solution I could come up with that maintains the ABI. The typedef changes force a ton of explicit type casts to correct pointer types and to make current code compatible with both the old and new pointer typedefs. This is the bulk of the change and it's really messy. Unfortunately I don't know how to avoid it. Please let me know what you think. Reviewers: howard.hinnant, mclow.lists Subscribers: howard.hinnant, bbannier, cfe-commits Differential Revision: https://reviews.llvm.org/D20786 llvm-svn: 276003
2016-07-19 09:56:20 -08:00
__x = __w->__parent_unsafe();
2010-05-11 11:42:16 -08:00
// __x can no longer be null
if (__x == __root || !__x->__is_black_) {
Fix undefined behavior in __tree Summary: This patch attempts to fix the undefined behavior in __tree by changing the node pointer types used throughout. The pointer types are changed for raw pointers in the current ABI and for fancy pointers in ABI V2 (since the fancy pointer types may not be ABI compatible). The UB in `__tree` arises because tree downcasts the embedded end node and then deferences that pointer. Currently there are 3 node types in __tree. * `__tree_end_node` which contains the `__left_` pointer. This node is embedded within the container. * `__tree_node_base` which contains `__right_`, `__parent_` and `__is_black`. This node is used throughout the tree rebalancing algorithms. * `__tree_node` which contains `__value_`. Currently `__tree` stores the start of the tree, `__begin_node_`, as a pointer to a `__tree_node`. Additionally the iterators store their position as a pointer to a `__tree_node`. In both of these cases the pointee can be the end node. This is fixed by changing them to store `__tree_end_node` pointers instead. To make this change I introduced an `__iter_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node` in the current one. Both `__tree::__begin_node_` and iterator pointers are now stored as `__iter_pointers`. The other situation where `__tree_end_node` is stored as the wrong type is in `__tree_node_base::__parent_`. Currently `__left_`, `__right_`, and `__parent_` are all `__tree_node_base` pointers. Since the end node will only be stored in `__parent_` the fix is to change `__parent_` to be a pointer to `__tree_end_node`. To make this change I introduced a `__parent_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node_base` in the current one. Note that in the new ABI `__iter_pointer` and `__parent_pointer` are the same type (but not in the old one). The confusion between these two types is unfortunate but it was the best solution I could come up with that maintains the ABI. The typedef changes force a ton of explicit type casts to correct pointer types and to make current code compatible with both the old and new pointer typedefs. This is the bulk of the change and it's really messy. Unfortunately I don't know how to avoid it. Please let me know what you think. Reviewers: howard.hinnant, mclow.lists Subscribers: howard.hinnant, bbannier, cfe-commits Differential Revision: https://reviews.llvm.org/D20786 llvm-svn: 276003
2016-07-19 09:56:20 -08:00
__x->__is_black_ = true;
2010-05-11 11:42:16 -08:00
break;
}
// reset sibling, and it still can't be null
Fix undefined behavior in __tree Summary: This patch attempts to fix the undefined behavior in __tree by changing the node pointer types used throughout. The pointer types are changed for raw pointers in the current ABI and for fancy pointers in ABI V2 (since the fancy pointer types may not be ABI compatible). The UB in `__tree` arises because tree downcasts the embedded end node and then deferences that pointer. Currently there are 3 node types in __tree. * `__tree_end_node` which contains the `__left_` pointer. This node is embedded within the container. * `__tree_node_base` which contains `__right_`, `__parent_` and `__is_black`. This node is used throughout the tree rebalancing algorithms. * `__tree_node` which contains `__value_`. Currently `__tree` stores the start of the tree, `__begin_node_`, as a pointer to a `__tree_node`. Additionally the iterators store their position as a pointer to a `__tree_node`. In both of these cases the pointee can be the end node. This is fixed by changing them to store `__tree_end_node` pointers instead. To make this change I introduced an `__iter_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node` in the current one. Both `__tree::__begin_node_` and iterator pointers are now stored as `__iter_pointers`. The other situation where `__tree_end_node` is stored as the wrong type is in `__tree_node_base::__parent_`. Currently `__left_`, `__right_`, and `__parent_` are all `__tree_node_base` pointers. Since the end node will only be stored in `__parent_` the fix is to change `__parent_` to be a pointer to `__tree_end_node`. To make this change I introduced a `__parent_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node_base` in the current one. Note that in the new ABI `__iter_pointer` and `__parent_pointer` are the same type (but not in the old one). The confusion between these two types is unfortunate but it was the best solution I could come up with that maintains the ABI. The typedef changes force a ton of explicit type casts to correct pointer types and to make current code compatible with both the old and new pointer typedefs. This is the bulk of the change and it's really messy. Unfortunately I don't know how to avoid it. Please let me know what you think. Reviewers: howard.hinnant, mclow.lists Subscribers: howard.hinnant, bbannier, cfe-commits Differential Revision: https://reviews.llvm.org/D20786 llvm-svn: 276003
2016-07-19 09:56:20 -08:00
__w = std::__tree_is_left_child(__x) ? __x->__parent_unsafe()->__right_ : __x->__parent_->__left_;
2010-05-11 11:42:16 -08:00
// continue;
} else // __w has a red child
{
2010-05-11 11:42:16 -08:00
if (__w->__right_ == nullptr || __w->__right_->__is_black_) {
// __w left child is non-null and red
__w->__left_->__is_black_ = true;
__w->__is_black_ = false;
std::__tree_right_rotate(__w);
2010-05-11 11:42:16 -08:00
// __w is known not to be root, so root hasn't changed
// reset sibling, and it still can't be null
Fix undefined behavior in __tree Summary: This patch attempts to fix the undefined behavior in __tree by changing the node pointer types used throughout. The pointer types are changed for raw pointers in the current ABI and for fancy pointers in ABI V2 (since the fancy pointer types may not be ABI compatible). The UB in `__tree` arises because tree downcasts the embedded end node and then deferences that pointer. Currently there are 3 node types in __tree. * `__tree_end_node` which contains the `__left_` pointer. This node is embedded within the container. * `__tree_node_base` which contains `__right_`, `__parent_` and `__is_black`. This node is used throughout the tree rebalancing algorithms. * `__tree_node` which contains `__value_`. Currently `__tree` stores the start of the tree, `__begin_node_`, as a pointer to a `__tree_node`. Additionally the iterators store their position as a pointer to a `__tree_node`. In both of these cases the pointee can be the end node. This is fixed by changing them to store `__tree_end_node` pointers instead. To make this change I introduced an `__iter_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node` in the current one. Both `__tree::__begin_node_` and iterator pointers are now stored as `__iter_pointers`. The other situation where `__tree_end_node` is stored as the wrong type is in `__tree_node_base::__parent_`. Currently `__left_`, `__right_`, and `__parent_` are all `__tree_node_base` pointers. Since the end node will only be stored in `__parent_` the fix is to change `__parent_` to be a pointer to `__tree_end_node`. To make this change I introduced a `__parent_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node_base` in the current one. Note that in the new ABI `__iter_pointer` and `__parent_pointer` are the same type (but not in the old one). The confusion between these two types is unfortunate but it was the best solution I could come up with that maintains the ABI. The typedef changes force a ton of explicit type casts to correct pointer types and to make current code compatible with both the old and new pointer typedefs. This is the bulk of the change and it's really messy. Unfortunately I don't know how to avoid it. Please let me know what you think. Reviewers: howard.hinnant, mclow.lists Subscribers: howard.hinnant, bbannier, cfe-commits Differential Revision: https://reviews.llvm.org/D20786 llvm-svn: 276003
2016-07-19 09:56:20 -08:00
__w = __w->__parent_unsafe();
}
2010-05-11 11:42:16 -08:00
// __w has a right red child, left child may be null
Fix undefined behavior in __tree Summary: This patch attempts to fix the undefined behavior in __tree by changing the node pointer types used throughout. The pointer types are changed for raw pointers in the current ABI and for fancy pointers in ABI V2 (since the fancy pointer types may not be ABI compatible). The UB in `__tree` arises because tree downcasts the embedded end node and then deferences that pointer. Currently there are 3 node types in __tree. * `__tree_end_node` which contains the `__left_` pointer. This node is embedded within the container. * `__tree_node_base` which contains `__right_`, `__parent_` and `__is_black`. This node is used throughout the tree rebalancing algorithms. * `__tree_node` which contains `__value_`. Currently `__tree` stores the start of the tree, `__begin_node_`, as a pointer to a `__tree_node`. Additionally the iterators store their position as a pointer to a `__tree_node`. In both of these cases the pointee can be the end node. This is fixed by changing them to store `__tree_end_node` pointers instead. To make this change I introduced an `__iter_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node` in the current one. Both `__tree::__begin_node_` and iterator pointers are now stored as `__iter_pointers`. The other situation where `__tree_end_node` is stored as the wrong type is in `__tree_node_base::__parent_`. Currently `__left_`, `__right_`, and `__parent_` are all `__tree_node_base` pointers. Since the end node will only be stored in `__parent_` the fix is to change `__parent_` to be a pointer to `__tree_end_node`. To make this change I introduced a `__parent_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node_base` in the current one. Note that in the new ABI `__iter_pointer` and `__parent_pointer` are the same type (but not in the old one). The confusion between these two types is unfortunate but it was the best solution I could come up with that maintains the ABI. The typedef changes force a ton of explicit type casts to correct pointer types and to make current code compatible with both the old and new pointer typedefs. This is the bulk of the change and it's really messy. Unfortunately I don't know how to avoid it. Please let me know what you think. Reviewers: howard.hinnant, mclow.lists Subscribers: howard.hinnant, bbannier, cfe-commits Differential Revision: https://reviews.llvm.org/D20786 llvm-svn: 276003
2016-07-19 09:56:20 -08:00
__w->__is_black_ = __w->__parent_unsafe()->__is_black_;
__w->__parent_unsafe()->__is_black_ = true;
2010-05-11 11:42:16 -08:00
__w->__right_->__is_black_ = true;
std::__tree_left_rotate(__w->__parent_unsafe());
break;
}
} else {
2010-05-11 11:42:16 -08:00
if (!__w->__is_black_) {
__w->__is_black_ = true;
Fix undefined behavior in __tree Summary: This patch attempts to fix the undefined behavior in __tree by changing the node pointer types used throughout. The pointer types are changed for raw pointers in the current ABI and for fancy pointers in ABI V2 (since the fancy pointer types may not be ABI compatible). The UB in `__tree` arises because tree downcasts the embedded end node and then deferences that pointer. Currently there are 3 node types in __tree. * `__tree_end_node` which contains the `__left_` pointer. This node is embedded within the container. * `__tree_node_base` which contains `__right_`, `__parent_` and `__is_black`. This node is used throughout the tree rebalancing algorithms. * `__tree_node` which contains `__value_`. Currently `__tree` stores the start of the tree, `__begin_node_`, as a pointer to a `__tree_node`. Additionally the iterators store their position as a pointer to a `__tree_node`. In both of these cases the pointee can be the end node. This is fixed by changing them to store `__tree_end_node` pointers instead. To make this change I introduced an `__iter_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node` in the current one. Both `__tree::__begin_node_` and iterator pointers are now stored as `__iter_pointers`. The other situation where `__tree_end_node` is stored as the wrong type is in `__tree_node_base::__parent_`. Currently `__left_`, `__right_`, and `__parent_` are all `__tree_node_base` pointers. Since the end node will only be stored in `__parent_` the fix is to change `__parent_` to be a pointer to `__tree_end_node`. To make this change I introduced a `__parent_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node_base` in the current one. Note that in the new ABI `__iter_pointer` and `__parent_pointer` are the same type (but not in the old one). The confusion between these two types is unfortunate but it was the best solution I could come up with that maintains the ABI. The typedef changes force a ton of explicit type casts to correct pointer types and to make current code compatible with both the old and new pointer typedefs. This is the bulk of the change and it's really messy. Unfortunately I don't know how to avoid it. Please let me know what you think. Reviewers: howard.hinnant, mclow.lists Subscribers: howard.hinnant, bbannier, cfe-commits Differential Revision: https://reviews.llvm.org/D20786 llvm-svn: 276003
2016-07-19 09:56:20 -08:00
__w->__parent_unsafe()->__is_black_ = false;
std::__tree_right_rotate(__w->__parent_unsafe());
2010-05-11 11:42:16 -08:00
// __x is still valid
// reset __root only if necessary
if (__root == __w->__right_)
__root = __w;
// reset sibling, and it still can't be null
__w = __w->__right_->__left_;
}
2010-05-11 11:42:16 -08:00
// __w->__is_black_ is now true, __w may have null children
if ((__w->__left_ == nullptr || __w->__left_->__is_black_) &&
(__w->__right_ == nullptr || __w->__right_->__is_black_)) {
__w->__is_black_ = false;
Fix undefined behavior in __tree Summary: This patch attempts to fix the undefined behavior in __tree by changing the node pointer types used throughout. The pointer types are changed for raw pointers in the current ABI and for fancy pointers in ABI V2 (since the fancy pointer types may not be ABI compatible). The UB in `__tree` arises because tree downcasts the embedded end node and then deferences that pointer. Currently there are 3 node types in __tree. * `__tree_end_node` which contains the `__left_` pointer. This node is embedded within the container. * `__tree_node_base` which contains `__right_`, `__parent_` and `__is_black`. This node is used throughout the tree rebalancing algorithms. * `__tree_node` which contains `__value_`. Currently `__tree` stores the start of the tree, `__begin_node_`, as a pointer to a `__tree_node`. Additionally the iterators store their position as a pointer to a `__tree_node`. In both of these cases the pointee can be the end node. This is fixed by changing them to store `__tree_end_node` pointers instead. To make this change I introduced an `__iter_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node` in the current one. Both `__tree::__begin_node_` and iterator pointers are now stored as `__iter_pointers`. The other situation where `__tree_end_node` is stored as the wrong type is in `__tree_node_base::__parent_`. Currently `__left_`, `__right_`, and `__parent_` are all `__tree_node_base` pointers. Since the end node will only be stored in `__parent_` the fix is to change `__parent_` to be a pointer to `__tree_end_node`. To make this change I introduced a `__parent_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node_base` in the current one. Note that in the new ABI `__iter_pointer` and `__parent_pointer` are the same type (but not in the old one). The confusion between these two types is unfortunate but it was the best solution I could come up with that maintains the ABI. The typedef changes force a ton of explicit type casts to correct pointer types and to make current code compatible with both the old and new pointer typedefs. This is the bulk of the change and it's really messy. Unfortunately I don't know how to avoid it. Please let me know what you think. Reviewers: howard.hinnant, mclow.lists Subscribers: howard.hinnant, bbannier, cfe-commits Differential Revision: https://reviews.llvm.org/D20786 llvm-svn: 276003
2016-07-19 09:56:20 -08:00
__x = __w->__parent_unsafe();
2010-05-11 11:42:16 -08:00
// __x can no longer be null
if (!__x->__is_black_ || __x == __root) {
__x->__is_black_ = true;
break;
}
2010-05-11 11:42:16 -08:00
// reset sibling, and it still can't be null
Fix undefined behavior in __tree Summary: This patch attempts to fix the undefined behavior in __tree by changing the node pointer types used throughout. The pointer types are changed for raw pointers in the current ABI and for fancy pointers in ABI V2 (since the fancy pointer types may not be ABI compatible). The UB in `__tree` arises because tree downcasts the embedded end node and then deferences that pointer. Currently there are 3 node types in __tree. * `__tree_end_node` which contains the `__left_` pointer. This node is embedded within the container. * `__tree_node_base` which contains `__right_`, `__parent_` and `__is_black`. This node is used throughout the tree rebalancing algorithms. * `__tree_node` which contains `__value_`. Currently `__tree` stores the start of the tree, `__begin_node_`, as a pointer to a `__tree_node`. Additionally the iterators store their position as a pointer to a `__tree_node`. In both of these cases the pointee can be the end node. This is fixed by changing them to store `__tree_end_node` pointers instead. To make this change I introduced an `__iter_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node` in the current one. Both `__tree::__begin_node_` and iterator pointers are now stored as `__iter_pointers`. The other situation where `__tree_end_node` is stored as the wrong type is in `__tree_node_base::__parent_`. Currently `__left_`, `__right_`, and `__parent_` are all `__tree_node_base` pointers. Since the end node will only be stored in `__parent_` the fix is to change `__parent_` to be a pointer to `__tree_end_node`. To make this change I introduced a `__parent_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node_base` in the current one. Note that in the new ABI `__iter_pointer` and `__parent_pointer` are the same type (but not in the old one). The confusion between these two types is unfortunate but it was the best solution I could come up with that maintains the ABI. The typedef changes force a ton of explicit type casts to correct pointer types and to make current code compatible with both the old and new pointer typedefs. This is the bulk of the change and it's really messy. Unfortunately I don't know how to avoid it. Please let me know what you think. Reviewers: howard.hinnant, mclow.lists Subscribers: howard.hinnant, bbannier, cfe-commits Differential Revision: https://reviews.llvm.org/D20786 llvm-svn: 276003
2016-07-19 09:56:20 -08:00
__w = std::__tree_is_left_child(__x) ? __x->__parent_unsafe()->__right_ : __x->__parent_->__left_;
2010-05-11 11:42:16 -08:00
// continue;
} else // __w has a red child
{
2010-05-11 11:42:16 -08:00
if (__w->__left_ == nullptr || __w->__left_->__is_black_) {
// __w right child is non-null and red
__w->__right_->__is_black_ = true;
__w->__is_black_ = false;
std::__tree_left_rotate(__w);
2010-05-11 11:42:16 -08:00
// __w is known not to be root, so root hasn't changed
// reset sibling, and it still can't be null
Fix undefined behavior in __tree Summary: This patch attempts to fix the undefined behavior in __tree by changing the node pointer types used throughout. The pointer types are changed for raw pointers in the current ABI and for fancy pointers in ABI V2 (since the fancy pointer types may not be ABI compatible). The UB in `__tree` arises because tree downcasts the embedded end node and then deferences that pointer. Currently there are 3 node types in __tree. * `__tree_end_node` which contains the `__left_` pointer. This node is embedded within the container. * `__tree_node_base` which contains `__right_`, `__parent_` and `__is_black`. This node is used throughout the tree rebalancing algorithms. * `__tree_node` which contains `__value_`. Currently `__tree` stores the start of the tree, `__begin_node_`, as a pointer to a `__tree_node`. Additionally the iterators store their position as a pointer to a `__tree_node`. In both of these cases the pointee can be the end node. This is fixed by changing them to store `__tree_end_node` pointers instead. To make this change I introduced an `__iter_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node` in the current one. Both `__tree::__begin_node_` and iterator pointers are now stored as `__iter_pointers`. The other situation where `__tree_end_node` is stored as the wrong type is in `__tree_node_base::__parent_`. Currently `__left_`, `__right_`, and `__parent_` are all `__tree_node_base` pointers. Since the end node will only be stored in `__parent_` the fix is to change `__parent_` to be a pointer to `__tree_end_node`. To make this change I introduced a `__parent_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node_base` in the current one. Note that in the new ABI `__iter_pointer` and `__parent_pointer` are the same type (but not in the old one). The confusion between these two types is unfortunate but it was the best solution I could come up with that maintains the ABI. The typedef changes force a ton of explicit type casts to correct pointer types and to make current code compatible with both the old and new pointer typedefs. This is the bulk of the change and it's really messy. Unfortunately I don't know how to avoid it. Please let me know what you think. Reviewers: howard.hinnant, mclow.lists Subscribers: howard.hinnant, bbannier, cfe-commits Differential Revision: https://reviews.llvm.org/D20786 llvm-svn: 276003
2016-07-19 09:56:20 -08:00
__w = __w->__parent_unsafe();
}
2010-05-11 11:42:16 -08:00
// __w has a left red child, right child may be null
Fix undefined behavior in __tree Summary: This patch attempts to fix the undefined behavior in __tree by changing the node pointer types used throughout. The pointer types are changed for raw pointers in the current ABI and for fancy pointers in ABI V2 (since the fancy pointer types may not be ABI compatible). The UB in `__tree` arises because tree downcasts the embedded end node and then deferences that pointer. Currently there are 3 node types in __tree. * `__tree_end_node` which contains the `__left_` pointer. This node is embedded within the container. * `__tree_node_base` which contains `__right_`, `__parent_` and `__is_black`. This node is used throughout the tree rebalancing algorithms. * `__tree_node` which contains `__value_`. Currently `__tree` stores the start of the tree, `__begin_node_`, as a pointer to a `__tree_node`. Additionally the iterators store their position as a pointer to a `__tree_node`. In both of these cases the pointee can be the end node. This is fixed by changing them to store `__tree_end_node` pointers instead. To make this change I introduced an `__iter_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node` in the current one. Both `__tree::__begin_node_` and iterator pointers are now stored as `__iter_pointers`. The other situation where `__tree_end_node` is stored as the wrong type is in `__tree_node_base::__parent_`. Currently `__left_`, `__right_`, and `__parent_` are all `__tree_node_base` pointers. Since the end node will only be stored in `__parent_` the fix is to change `__parent_` to be a pointer to `__tree_end_node`. To make this change I introduced a `__parent_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node_base` in the current one. Note that in the new ABI `__iter_pointer` and `__parent_pointer` are the same type (but not in the old one). The confusion between these two types is unfortunate but it was the best solution I could come up with that maintains the ABI. The typedef changes force a ton of explicit type casts to correct pointer types and to make current code compatible with both the old and new pointer typedefs. This is the bulk of the change and it's really messy. Unfortunately I don't know how to avoid it. Please let me know what you think. Reviewers: howard.hinnant, mclow.lists Subscribers: howard.hinnant, bbannier, cfe-commits Differential Revision: https://reviews.llvm.org/D20786 llvm-svn: 276003
2016-07-19 09:56:20 -08:00
__w->__is_black_ = __w->__parent_unsafe()->__is_black_;
__w->__parent_unsafe()->__is_black_ = true;
2010-05-11 11:42:16 -08:00
__w->__left_->__is_black_ = true;
std::__tree_right_rotate(__w->__parent_unsafe());
break;
}
2010-05-11 11:42:16 -08:00
}
}
2010-05-11 11:42:16 -08:00
}
}
2010-05-11 11:42:16 -08:00
}
Teach __tree how to handle map's __value_type This patch is fairly large and contains a number of changes. The changes all work towards allowing __tree to properly handle __value_type esspecially when inserting into the __tree. I chose not to break this change into smaller patches because it wouldn't be possible to write meaningful standard-compliant tests for each patch. It is very similar to r260513 "[libcxx] Teach __hash_table how to handle unordered_map's __hash_value_type". Changes in <map> * Remove __value_type's constructors because it should never be constructed directly. * Make map::emplace and multimap::emplace forward to __tree and remove the old definitions * Remove "__construct_node" map and multimap member functions. Almost all of the construction is done within __tree. * Fix map's move constructor to access "__value_type.__nc" directly and pass this object to __tree::insert. Changes in <__tree> * Add traits to detect, handle, and unwrap, map's "__value_type". * Convert methods taking "value_type" to take "__container_value_type" instead. Previously these methods caused unwanted implicit conversions from "std::pair<Key, Value>" to "__value_type<Key, Value>". * Delete __tree_node and __tree_node_base's constructors and assignment operators. The node types should never be constructed because the "__value_" member of __tree_node must be constructed directly by the allocator. * Make the __tree_node_destructor class and "__construct_node" methods unwrap "__node_value_type" into "__container_value_type" before invoking the allocator. The user's allocator can only be used to construct and destroy the container's value_type. Passing it map's "__value_type" was incorrect. * Cleanup the "__insert" and "__emplace" methods. Have __insert forward to an __emplace function wherever possible to reduce code duplication. __insert_unique(value_type const&) and __insert_unique(value_type&&) forward to __emplace_unique_key_args. These functions will not allocate a new node if the value is already in the tree. * Change the __find* functions to take the "key_type" directly instead of passing in "value_type" and unwrapping the key later. This change allows the find functions to be used without having to construct a "value_type" first. This allows for a number of optimizations. * Teach __move_assign and __assign_multi methods to unwrap map's __value_type. llvm-svn: 264986
2016-03-30 18:15:15 -08:00
// node traits
2010-05-11 11:42:16 -08:00
Teach __tree how to handle map's __value_type This patch is fairly large and contains a number of changes. The changes all work towards allowing __tree to properly handle __value_type esspecially when inserting into the __tree. I chose not to break this change into smaller patches because it wouldn't be possible to write meaningful standard-compliant tests for each patch. It is very similar to r260513 "[libcxx] Teach __hash_table how to handle unordered_map's __hash_value_type". Changes in <map> * Remove __value_type's constructors because it should never be constructed directly. * Make map::emplace and multimap::emplace forward to __tree and remove the old definitions * Remove "__construct_node" map and multimap member functions. Almost all of the construction is done within __tree. * Fix map's move constructor to access "__value_type.__nc" directly and pass this object to __tree::insert. Changes in <__tree> * Add traits to detect, handle, and unwrap, map's "__value_type". * Convert methods taking "value_type" to take "__container_value_type" instead. Previously these methods caused unwanted implicit conversions from "std::pair<Key, Value>" to "__value_type<Key, Value>". * Delete __tree_node and __tree_node_base's constructors and assignment operators. The node types should never be constructed because the "__value_" member of __tree_node must be constructed directly by the allocator. * Make the __tree_node_destructor class and "__construct_node" methods unwrap "__node_value_type" into "__container_value_type" before invoking the allocator. The user's allocator can only be used to construct and destroy the container's value_type. Passing it map's "__value_type" was incorrect. * Cleanup the "__insert" and "__emplace" methods. Have __insert forward to an __emplace function wherever possible to reduce code duplication. __insert_unique(value_type const&) and __insert_unique(value_type&&) forward to __emplace_unique_key_args. These functions will not allocate a new node if the value is already in the tree. * Change the __find* functions to take the "key_type" directly instead of passing in "value_type" and unwrapping the key later. This change allows the find functions to be used without having to construct a "value_type" first. This allows for a number of optimizations. * Teach __move_assign and __assign_multi methods to unwrap map's __value_type. llvm-svn: 264986
2016-03-30 18:15:15 -08:00
template <class _Tp>
inline const bool __is_tree_value_type_v = __is_specialization_v<_Tp, __value_type>;
template <class _Tp>
struct __get_tree_key_type {
using type _LIBCPP_NODEBUG = _Tp;
};
template <class _Key, class _ValueT>
struct __get_tree_key_type<__value_type<_Key, _ValueT> > {
using type _LIBCPP_NODEBUG = _Key;
};
template <class _Tp>
using __get_tree_key_type_t _LIBCPP_NODEBUG = typename __get_tree_key_type<_Tp>::type;
template <class _Tp>
struct __get_node_value_type {
using type _LIBCPP_NODEBUG = _Tp;
};
template <class _Key, class _ValueT>
struct __get_node_value_type<__value_type<_Key, _ValueT> > {
using type _LIBCPP_NODEBUG = pair<const _Key, _ValueT>;
};
template <class _Tp>
using __get_node_value_type_t _LIBCPP_NODEBUG = typename __get_node_value_type<_Tp>::type;
template <class _NodePtr, class _NodeT = typename pointer_traits<_NodePtr>::element_type>
struct __tree_node_types;
template <class _NodePtr, class _Tp, class _VoidPtr>
struct __tree_node_types<_NodePtr, __tree_node<_Tp, _VoidPtr> > {
using __node_base_pointer _LIBCPP_NODEBUG = __rebind_pointer_t<_VoidPtr, __tree_node_base<_VoidPtr> >;
using __end_node_pointer _LIBCPP_NODEBUG = __rebind_pointer_t<_VoidPtr, __tree_end_node<__node_base_pointer> >;
private:
static_assert(is_same<typename pointer_traits<_VoidPtr>::element_type, void>::value,
"_VoidPtr does not point to unqualified void type");
};
2010-05-11 11:42:16 -08:00
// node
template <class _Pointer>
class __tree_end_node {
public:
using pointer = _Pointer;
2010-05-11 11:42:16 -08:00
pointer __left_;
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree_end_node() _NOEXCEPT : __left_() {}
2010-05-11 11:42:16 -08:00
};
template <class _VoidPtr>
class __tree_node_base : public __tree_end_node<__rebind_pointer_t<_VoidPtr, __tree_node_base<_VoidPtr> > > {
2010-05-11 11:42:16 -08:00
public:
using pointer = __rebind_pointer_t<_VoidPtr, __tree_node_base>;
using __end_node_pointer _LIBCPP_NODEBUG = __rebind_pointer_t<_VoidPtr, __tree_end_node<pointer> >;
2010-05-11 11:42:16 -08:00
Fix undefined behavior in __tree Summary: This patch attempts to fix the undefined behavior in __tree by changing the node pointer types used throughout. The pointer types are changed for raw pointers in the current ABI and for fancy pointers in ABI V2 (since the fancy pointer types may not be ABI compatible). The UB in `__tree` arises because tree downcasts the embedded end node and then deferences that pointer. Currently there are 3 node types in __tree. * `__tree_end_node` which contains the `__left_` pointer. This node is embedded within the container. * `__tree_node_base` which contains `__right_`, `__parent_` and `__is_black`. This node is used throughout the tree rebalancing algorithms. * `__tree_node` which contains `__value_`. Currently `__tree` stores the start of the tree, `__begin_node_`, as a pointer to a `__tree_node`. Additionally the iterators store their position as a pointer to a `__tree_node`. In both of these cases the pointee can be the end node. This is fixed by changing them to store `__tree_end_node` pointers instead. To make this change I introduced an `__iter_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node` in the current one. Both `__tree::__begin_node_` and iterator pointers are now stored as `__iter_pointers`. The other situation where `__tree_end_node` is stored as the wrong type is in `__tree_node_base::__parent_`. Currently `__left_`, `__right_`, and `__parent_` are all `__tree_node_base` pointers. Since the end node will only be stored in `__parent_` the fix is to change `__parent_` to be a pointer to `__tree_end_node`. To make this change I introduced a `__parent_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node_base` in the current one. Note that in the new ABI `__iter_pointer` and `__parent_pointer` are the same type (but not in the old one). The confusion between these two types is unfortunate but it was the best solution I could come up with that maintains the ABI. The typedef changes force a ton of explicit type casts to correct pointer types and to make current code compatible with both the old and new pointer typedefs. This is the bulk of the change and it's really messy. Unfortunately I don't know how to avoid it. Please let me know what you think. Reviewers: howard.hinnant, mclow.lists Subscribers: howard.hinnant, bbannier, cfe-commits Differential Revision: https://reviews.llvm.org/D20786 llvm-svn: 276003
2016-07-19 09:56:20 -08:00
pointer __right_;
__end_node_pointer __parent_;
2010-05-11 11:42:16 -08:00
bool __is_black_;
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pointer __parent_unsafe() const {
return std::__static_fancy_pointer_cast<pointer>(__parent_);
}
Fix undefined behavior in __tree Summary: This patch attempts to fix the undefined behavior in __tree by changing the node pointer types used throughout. The pointer types are changed for raw pointers in the current ABI and for fancy pointers in ABI V2 (since the fancy pointer types may not be ABI compatible). The UB in `__tree` arises because tree downcasts the embedded end node and then deferences that pointer. Currently there are 3 node types in __tree. * `__tree_end_node` which contains the `__left_` pointer. This node is embedded within the container. * `__tree_node_base` which contains `__right_`, `__parent_` and `__is_black`. This node is used throughout the tree rebalancing algorithms. * `__tree_node` which contains `__value_`. Currently `__tree` stores the start of the tree, `__begin_node_`, as a pointer to a `__tree_node`. Additionally the iterators store their position as a pointer to a `__tree_node`. In both of these cases the pointee can be the end node. This is fixed by changing them to store `__tree_end_node` pointers instead. To make this change I introduced an `__iter_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node` in the current one. Both `__tree::__begin_node_` and iterator pointers are now stored as `__iter_pointers`. The other situation where `__tree_end_node` is stored as the wrong type is in `__tree_node_base::__parent_`. Currently `__left_`, `__right_`, and `__parent_` are all `__tree_node_base` pointers. Since the end node will only be stored in `__parent_` the fix is to change `__parent_` to be a pointer to `__tree_end_node`. To make this change I introduced a `__parent_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node_base` in the current one. Note that in the new ABI `__iter_pointer` and `__parent_pointer` are the same type (but not in the old one). The confusion between these two types is unfortunate but it was the best solution I could come up with that maintains the ABI. The typedef changes force a ton of explicit type casts to correct pointer types and to make current code compatible with both the old and new pointer typedefs. This is the bulk of the change and it's really messy. Unfortunately I don't know how to avoid it. Please let me know what you think. Reviewers: howard.hinnant, mclow.lists Subscribers: howard.hinnant, bbannier, cfe-commits Differential Revision: https://reviews.llvm.org/D20786 llvm-svn: 276003
2016-07-19 09:56:20 -08:00
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __set_parent(pointer __p) {
__parent_ = std::__static_fancy_pointer_cast<__end_node_pointer>(__p);
}
Fix undefined behavior in __tree Summary: This patch attempts to fix the undefined behavior in __tree by changing the node pointer types used throughout. The pointer types are changed for raw pointers in the current ABI and for fancy pointers in ABI V2 (since the fancy pointer types may not be ABI compatible). The UB in `__tree` arises because tree downcasts the embedded end node and then deferences that pointer. Currently there are 3 node types in __tree. * `__tree_end_node` which contains the `__left_` pointer. This node is embedded within the container. * `__tree_node_base` which contains `__right_`, `__parent_` and `__is_black`. This node is used throughout the tree rebalancing algorithms. * `__tree_node` which contains `__value_`. Currently `__tree` stores the start of the tree, `__begin_node_`, as a pointer to a `__tree_node`. Additionally the iterators store their position as a pointer to a `__tree_node`. In both of these cases the pointee can be the end node. This is fixed by changing them to store `__tree_end_node` pointers instead. To make this change I introduced an `__iter_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node` in the current one. Both `__tree::__begin_node_` and iterator pointers are now stored as `__iter_pointers`. The other situation where `__tree_end_node` is stored as the wrong type is in `__tree_node_base::__parent_`. Currently `__left_`, `__right_`, and `__parent_` are all `__tree_node_base` pointers. Since the end node will only be stored in `__parent_` the fix is to change `__parent_` to be a pointer to `__tree_end_node`. To make this change I introduced a `__parent_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node_base` in the current one. Note that in the new ABI `__iter_pointer` and `__parent_pointer` are the same type (but not in the old one). The confusion between these two types is unfortunate but it was the best solution I could come up with that maintains the ABI. The typedef changes force a ton of explicit type casts to correct pointer types and to make current code compatible with both the old and new pointer typedefs. This is the bulk of the change and it's really messy. Unfortunately I don't know how to avoid it. Please let me know what you think. Reviewers: howard.hinnant, mclow.lists Subscribers: howard.hinnant, bbannier, cfe-commits Differential Revision: https://reviews.llvm.org/D20786 llvm-svn: 276003
2016-07-19 09:56:20 -08:00
_LIBCPP_HIDE_FROM_ABI __tree_node_base() = default;
__tree_node_base(__tree_node_base const&) = delete;
__tree_node_base& operator=(__tree_node_base const&) = delete;
2010-05-11 11:42:16 -08:00
};
template <class _Tp, class _VoidPtr>
class __tree_node : public __tree_node_base<_VoidPtr> {
2010-05-11 11:42:16 -08:00
public:
using __node_value_type _LIBCPP_NODEBUG = __get_node_value_type_t<_Tp>;
2010-05-11 11:42:16 -08:00
// We use a union to avoid initialization during member initialization, which allows us
// to use the allocator from the container to construct the `__node_value_type` in the
// memory provided by the union member
#ifndef _LIBCPP_CXX03_LANG
private:
union {
__node_value_type __value_;
};
2010-05-11 11:42:16 -08:00
public:
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __node_value_type& __get_value() { return __value_; }
#else
private:
_ALIGNAS_TYPE(__node_value_type) unsigned char __buffer_[sizeof(__node_value_type)];
public:
_LIBCPP_HIDE_FROM_ABI __node_value_type& __get_value() { return *reinterpret_cast<__node_value_type*>(__buffer_); }
#endif
template <class _Alloc, class... _Args>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 explicit __tree_node(_Alloc& __na, _Args&&... __args) {
allocator_traits<_Alloc>::construct(__na, std::addressof(__get_value()), std::forward<_Args>(__args)...);
}
~__tree_node() = delete;
__tree_node(__tree_node const&) = delete;
__tree_node& operator=(__tree_node const&) = delete;
Teach __tree how to handle map's __value_type This patch is fairly large and contains a number of changes. The changes all work towards allowing __tree to properly handle __value_type esspecially when inserting into the __tree. I chose not to break this change into smaller patches because it wouldn't be possible to write meaningful standard-compliant tests for each patch. It is very similar to r260513 "[libcxx] Teach __hash_table how to handle unordered_map's __hash_value_type". Changes in <map> * Remove __value_type's constructors because it should never be constructed directly. * Make map::emplace and multimap::emplace forward to __tree and remove the old definitions * Remove "__construct_node" map and multimap member functions. Almost all of the construction is done within __tree. * Fix map's move constructor to access "__value_type.__nc" directly and pass this object to __tree::insert. Changes in <__tree> * Add traits to detect, handle, and unwrap, map's "__value_type". * Convert methods taking "value_type" to take "__container_value_type" instead. Previously these methods caused unwanted implicit conversions from "std::pair<Key, Value>" to "__value_type<Key, Value>". * Delete __tree_node and __tree_node_base's constructors and assignment operators. The node types should never be constructed because the "__value_" member of __tree_node must be constructed directly by the allocator. * Make the __tree_node_destructor class and "__construct_node" methods unwrap "__node_value_type" into "__container_value_type" before invoking the allocator. The user's allocator can only be used to construct and destroy the container's value_type. Passing it map's "__value_type" was incorrect. * Cleanup the "__insert" and "__emplace" methods. Have __insert forward to an __emplace function wherever possible to reduce code duplication. __insert_unique(value_type const&) and __insert_unique(value_type&&) forward to __emplace_unique_key_args. These functions will not allocate a new node if the value is already in the tree. * Change the __find* functions to take the "key_type" directly instead of passing in "value_type" and unwrapping the key later. This change allows the find functions to be used without having to construct a "value_type" first. This allows for a number of optimizations. * Teach __move_assign and __assign_multi methods to unwrap map's __value_type. llvm-svn: 264986
2016-03-30 18:15:15 -08:00
};
template <class _Allocator>
class __tree_node_destructor {
using allocator_type = _Allocator;
using __alloc_traits _LIBCPP_NODEBUG = allocator_traits<allocator_type>;
Teach __tree how to handle map's __value_type This patch is fairly large and contains a number of changes. The changes all work towards allowing __tree to properly handle __value_type esspecially when inserting into the __tree. I chose not to break this change into smaller patches because it wouldn't be possible to write meaningful standard-compliant tests for each patch. It is very similar to r260513 "[libcxx] Teach __hash_table how to handle unordered_map's __hash_value_type". Changes in <map> * Remove __value_type's constructors because it should never be constructed directly. * Make map::emplace and multimap::emplace forward to __tree and remove the old definitions * Remove "__construct_node" map and multimap member functions. Almost all of the construction is done within __tree. * Fix map's move constructor to access "__value_type.__nc" directly and pass this object to __tree::insert. Changes in <__tree> * Add traits to detect, handle, and unwrap, map's "__value_type". * Convert methods taking "value_type" to take "__container_value_type" instead. Previously these methods caused unwanted implicit conversions from "std::pair<Key, Value>" to "__value_type<Key, Value>". * Delete __tree_node and __tree_node_base's constructors and assignment operators. The node types should never be constructed because the "__value_" member of __tree_node must be constructed directly by the allocator. * Make the __tree_node_destructor class and "__construct_node" methods unwrap "__node_value_type" into "__container_value_type" before invoking the allocator. The user's allocator can only be used to construct and destroy the container's value_type. Passing it map's "__value_type" was incorrect. * Cleanup the "__insert" and "__emplace" methods. Have __insert forward to an __emplace function wherever possible to reduce code duplication. __insert_unique(value_type const&) and __insert_unique(value_type&&) forward to __emplace_unique_key_args. These functions will not allocate a new node if the value is already in the tree. * Change the __find* functions to take the "key_type" directly instead of passing in "value_type" and unwrapping the key later. This change allows the find functions to be used without having to construct a "value_type" first. This allows for a number of optimizations. * Teach __move_assign and __assign_multi methods to unwrap map's __value_type. llvm-svn: 264986
2016-03-30 18:15:15 -08:00
public:
using pointer = typename __alloc_traits::pointer;
Teach __tree how to handle map's __value_type This patch is fairly large and contains a number of changes. The changes all work towards allowing __tree to properly handle __value_type esspecially when inserting into the __tree. I chose not to break this change into smaller patches because it wouldn't be possible to write meaningful standard-compliant tests for each patch. It is very similar to r260513 "[libcxx] Teach __hash_table how to handle unordered_map's __hash_value_type". Changes in <map> * Remove __value_type's constructors because it should never be constructed directly. * Make map::emplace and multimap::emplace forward to __tree and remove the old definitions * Remove "__construct_node" map and multimap member functions. Almost all of the construction is done within __tree. * Fix map's move constructor to access "__value_type.__nc" directly and pass this object to __tree::insert. Changes in <__tree> * Add traits to detect, handle, and unwrap, map's "__value_type". * Convert methods taking "value_type" to take "__container_value_type" instead. Previously these methods caused unwanted implicit conversions from "std::pair<Key, Value>" to "__value_type<Key, Value>". * Delete __tree_node and __tree_node_base's constructors and assignment operators. The node types should never be constructed because the "__value_" member of __tree_node must be constructed directly by the allocator. * Make the __tree_node_destructor class and "__construct_node" methods unwrap "__node_value_type" into "__container_value_type" before invoking the allocator. The user's allocator can only be used to construct and destroy the container's value_type. Passing it map's "__value_type" was incorrect. * Cleanup the "__insert" and "__emplace" methods. Have __insert forward to an __emplace function wherever possible to reduce code duplication. __insert_unique(value_type const&) and __insert_unique(value_type&&) forward to __emplace_unique_key_args. These functions will not allocate a new node if the value is already in the tree. * Change the __find* functions to take the "key_type" directly instead of passing in "value_type" and unwrapping the key later. This change allows the find functions to be used without having to construct a "value_type" first. This allows for a number of optimizations. * Teach __move_assign and __assign_multi methods to unwrap map's __value_type. llvm-svn: 264986
2016-03-30 18:15:15 -08:00
private:
Teach __tree how to handle map's __value_type This patch is fairly large and contains a number of changes. The changes all work towards allowing __tree to properly handle __value_type esspecially when inserting into the __tree. I chose not to break this change into smaller patches because it wouldn't be possible to write meaningful standard-compliant tests for each patch. It is very similar to r260513 "[libcxx] Teach __hash_table how to handle unordered_map's __hash_value_type". Changes in <map> * Remove __value_type's constructors because it should never be constructed directly. * Make map::emplace and multimap::emplace forward to __tree and remove the old definitions * Remove "__construct_node" map and multimap member functions. Almost all of the construction is done within __tree. * Fix map's move constructor to access "__value_type.__nc" directly and pass this object to __tree::insert. Changes in <__tree> * Add traits to detect, handle, and unwrap, map's "__value_type". * Convert methods taking "value_type" to take "__container_value_type" instead. Previously these methods caused unwanted implicit conversions from "std::pair<Key, Value>" to "__value_type<Key, Value>". * Delete __tree_node and __tree_node_base's constructors and assignment operators. The node types should never be constructed because the "__value_" member of __tree_node must be constructed directly by the allocator. * Make the __tree_node_destructor class and "__construct_node" methods unwrap "__node_value_type" into "__container_value_type" before invoking the allocator. The user's allocator can only be used to construct and destroy the container's value_type. Passing it map's "__value_type" was incorrect. * Cleanup the "__insert" and "__emplace" methods. Have __insert forward to an __emplace function wherever possible to reduce code duplication. __insert_unique(value_type const&) and __insert_unique(value_type&&) forward to __emplace_unique_key_args. These functions will not allocate a new node if the value is already in the tree. * Change the __find* functions to take the "key_type" directly instead of passing in "value_type" and unwrapping the key later. This change allows the find functions to be used without having to construct a "value_type" first. This allows for a number of optimizations. * Teach __move_assign and __assign_multi methods to unwrap map's __value_type. llvm-svn: 264986
2016-03-30 18:15:15 -08:00
allocator_type& __na_;
public:
bool __value_constructed;
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree_node_destructor(const __tree_node_destructor&) = default;
__tree_node_destructor& operator=(const __tree_node_destructor&) = delete;
_LIBCPP_HIDE_FROM_ABI
_LIBCPP_CONSTEXPR_SINCE_CXX26 explicit __tree_node_destructor(allocator_type& __na, bool __val = false) _NOEXCEPT
: __na_(__na),
__value_constructed(__val) {}
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void operator()(pointer __p) _NOEXCEPT {
Teach __tree how to handle map's __value_type This patch is fairly large and contains a number of changes. The changes all work towards allowing __tree to properly handle __value_type esspecially when inserting into the __tree. I chose not to break this change into smaller patches because it wouldn't be possible to write meaningful standard-compliant tests for each patch. It is very similar to r260513 "[libcxx] Teach __hash_table how to handle unordered_map's __hash_value_type". Changes in <map> * Remove __value_type's constructors because it should never be constructed directly. * Make map::emplace and multimap::emplace forward to __tree and remove the old definitions * Remove "__construct_node" map and multimap member functions. Almost all of the construction is done within __tree. * Fix map's move constructor to access "__value_type.__nc" directly and pass this object to __tree::insert. Changes in <__tree> * Add traits to detect, handle, and unwrap, map's "__value_type". * Convert methods taking "value_type" to take "__container_value_type" instead. Previously these methods caused unwanted implicit conversions from "std::pair<Key, Value>" to "__value_type<Key, Value>". * Delete __tree_node and __tree_node_base's constructors and assignment operators. The node types should never be constructed because the "__value_" member of __tree_node must be constructed directly by the allocator. * Make the __tree_node_destructor class and "__construct_node" methods unwrap "__node_value_type" into "__container_value_type" before invoking the allocator. The user's allocator can only be used to construct and destroy the container's value_type. Passing it map's "__value_type" was incorrect. * Cleanup the "__insert" and "__emplace" methods. Have __insert forward to an __emplace function wherever possible to reduce code duplication. __insert_unique(value_type const&) and __insert_unique(value_type&&) forward to __emplace_unique_key_args. These functions will not allocate a new node if the value is already in the tree. * Change the __find* functions to take the "key_type" directly instead of passing in "value_type" and unwrapping the key later. This change allows the find functions to be used without having to construct a "value_type" first. This allows for a number of optimizations. * Teach __move_assign and __assign_multi methods to unwrap map's __value_type. llvm-svn: 264986
2016-03-30 18:15:15 -08:00
if (__value_constructed)
__alloc_traits::destroy(__na_, std::addressof(__p->__get_value()));
Teach __tree how to handle map's __value_type This patch is fairly large and contains a number of changes. The changes all work towards allowing __tree to properly handle __value_type esspecially when inserting into the __tree. I chose not to break this change into smaller patches because it wouldn't be possible to write meaningful standard-compliant tests for each patch. It is very similar to r260513 "[libcxx] Teach __hash_table how to handle unordered_map's __hash_value_type". Changes in <map> * Remove __value_type's constructors because it should never be constructed directly. * Make map::emplace and multimap::emplace forward to __tree and remove the old definitions * Remove "__construct_node" map and multimap member functions. Almost all of the construction is done within __tree. * Fix map's move constructor to access "__value_type.__nc" directly and pass this object to __tree::insert. Changes in <__tree> * Add traits to detect, handle, and unwrap, map's "__value_type". * Convert methods taking "value_type" to take "__container_value_type" instead. Previously these methods caused unwanted implicit conversions from "std::pair<Key, Value>" to "__value_type<Key, Value>". * Delete __tree_node and __tree_node_base's constructors and assignment operators. The node types should never be constructed because the "__value_" member of __tree_node must be constructed directly by the allocator. * Make the __tree_node_destructor class and "__construct_node" methods unwrap "__node_value_type" into "__container_value_type" before invoking the allocator. The user's allocator can only be used to construct and destroy the container's value_type. Passing it map's "__value_type" was incorrect. * Cleanup the "__insert" and "__emplace" methods. Have __insert forward to an __emplace function wherever possible to reduce code duplication. __insert_unique(value_type const&) and __insert_unique(value_type&&) forward to __emplace_unique_key_args. These functions will not allocate a new node if the value is already in the tree. * Change the __find* functions to take the "key_type" directly instead of passing in "value_type" and unwrapping the key later. This change allows the find functions to be used without having to construct a "value_type" first. This allows for a number of optimizations. * Teach __move_assign and __assign_multi methods to unwrap map's __value_type. llvm-svn: 264986
2016-03-30 18:15:15 -08:00
if (__p)
__alloc_traits::deallocate(__na_, __p, 1);
}
template <class>
friend class __map_node_destructor;
2010-05-11 11:42:16 -08:00
};
#if _LIBCPP_STD_VER >= 17
template <class _NodeType, class _Alloc>
struct __generic_container_node_destructor;
template <class _Tp, class _VoidPtr, class _Alloc>
struct __generic_container_node_destructor<__tree_node<_Tp, _VoidPtr>, _Alloc> : __tree_node_destructor<_Alloc> {
using __tree_node_destructor<_Alloc>::__tree_node_destructor;
};
#endif
Teach __tree how to handle map's __value_type This patch is fairly large and contains a number of changes. The changes all work towards allowing __tree to properly handle __value_type esspecially when inserting into the __tree. I chose not to break this change into smaller patches because it wouldn't be possible to write meaningful standard-compliant tests for each patch. It is very similar to r260513 "[libcxx] Teach __hash_table how to handle unordered_map's __hash_value_type". Changes in <map> * Remove __value_type's constructors because it should never be constructed directly. * Make map::emplace and multimap::emplace forward to __tree and remove the old definitions * Remove "__construct_node" map and multimap member functions. Almost all of the construction is done within __tree. * Fix map's move constructor to access "__value_type.__nc" directly and pass this object to __tree::insert. Changes in <__tree> * Add traits to detect, handle, and unwrap, map's "__value_type". * Convert methods taking "value_type" to take "__container_value_type" instead. Previously these methods caused unwanted implicit conversions from "std::pair<Key, Value>" to "__value_type<Key, Value>". * Delete __tree_node and __tree_node_base's constructors and assignment operators. The node types should never be constructed because the "__value_" member of __tree_node must be constructed directly by the allocator. * Make the __tree_node_destructor class and "__construct_node" methods unwrap "__node_value_type" into "__container_value_type" before invoking the allocator. The user's allocator can only be used to construct and destroy the container's value_type. Passing it map's "__value_type" was incorrect. * Cleanup the "__insert" and "__emplace" methods. Have __insert forward to an __emplace function wherever possible to reduce code duplication. __insert_unique(value_type const&) and __insert_unique(value_type&&) forward to __emplace_unique_key_args. These functions will not allocate a new node if the value is already in the tree. * Change the __find* functions to take the "key_type" directly instead of passing in "value_type" and unwrapping the key later. This change allows the find functions to be used without having to construct a "value_type" first. This allows for a number of optimizations. * Teach __move_assign and __assign_multi methods to unwrap map's __value_type. llvm-svn: 264986
2016-03-30 18:15:15 -08:00
[libc++] Optimize {std,ranges}::for_each for iterating over __trees (#164405) This patch optimizes how `for_each` iterates over trees by using recursion and storing pointers to the next nodes on the stack. This avoids pointer chasing through the `__parent_` pointer, reducing cache misses. It also makes use of the compiler being able tail-call optimize the recursive function, removing back-tracking the iterators have to do. ``` Benchmark old new Difference % Difference -------------------------------------------------- -------------- -------------- ------------ -------------- rng::for_each(map<int>)/32 35.19 26.67 -8.52 -24.21% rng::for_each(map<int>)/50 64.13 40.68 -23.45 -36.57% rng::for_each(map<int>)/8 5.06 6.49 1.43 28.21% rng::for_each(map<int>)/8192 22893.89 9266.68 -13627.21 -59.52% rng::for_each(map<int>::iterator)/32 35.51 26.88 -8.63 -24.31% rng::for_each(map<int>::iterator)/50 64.39 41.24 -23.15 -35.95% rng::for_each(map<int>::iterator)/8 5.12 5.93 0.81 15.80% rng::for_each(map<int>::iterator)/8192 21283.14 9736.83 -11546.31 -54.25% rng::for_each(multimap<int>)/32 35.22 26.61 -8.61 -24.45% rng::for_each(multimap<int>)/50 64.10 40.07 -24.03 -37.49% rng::for_each(multimap<int>)/8 5.08 6.69 1.61 31.70% rng::for_each(multimap<int>)/8192 23130.44 9026.16 -14104.28 -60.98% rng::for_each(multimap<int>::iterator)/32 35.40 25.08 -10.32 -29.15% rng::for_each(multimap<int>::iterator)/50 64.19 38.15 -26.04 -40.56% rng::for_each(multimap<int>::iterator)/8 5.04 5.25 0.22 4.31% rng::for_each(multimap<int>::iterator)/8192 22875.97 9392.08 -13483.89 -58.94% rng::for_each(multiset<int>)/32 35.82 27.11 -8.72 -24.33% rng::for_each(multiset<int>)/50 62.92 41.59 -21.32 -33.89% rng::for_each(multiset<int>)/8 4.79 6.79 2.00 41.70% rng::for_each(multiset<int>)/8192 22642.68 9280.95 -13361.73 -59.01% rng::for_each(multiset<int>::iterator)/32 35.76 26.71 -9.04 -25.28% rng::for_each(multiset<int>::iterator)/50 63.44 39.00 -24.44 -38.53% rng::for_each(multiset<int>::iterator)/8 4.90 5.21 0.30 6.18% rng::for_each(multiset<int>::iterator)/8192 19930.45 9867.60 -10062.85 -50.49% rng::for_each(set<int>)/32 35.90 27.30 -8.60 -23.96% rng::for_each(set<int>)/50 63.15 40.75 -22.40 -35.47% rng::for_each(set<int>)/8 4.77 6.83 2.06 43.23% rng::for_each(set<int>)/8192 20262.77 9381.57 -10881.20 -53.70% rng::for_each(set<int>::iterator)/32 36.02 26.42 -9.60 -26.64% rng::for_each(set<int>::iterator)/50 63.29 37.97 -25.32 -40.01% rng::for_each(set<int>::iterator)/8 4.72 5.22 0.50 10.50% rng::for_each(set<int>::iterator)/8192 20041.91 9831.91 -10210.00 -50.94% ```
2025-12-11 23:05:51 -09:00
// Do an in-order traversal of the tree until `__break` returns true. Takes the root node of the tree.
template <class _Reference, class _Break, class _NodePtr, class _Func, class _Proj>
#ifndef _LIBCPP_COMPILER_GCC // This function is recursive, so GCC complains about always_inline.
_LIBCPP_HIDE_FROM_ABI
#endif
_LIBCPP_CONSTEXPR_SINCE_CXX26 bool
__tree_iterate_from_root(_Break __break, _NodePtr __root, _Func& __func, _Proj& __proj) {
[libc++] Optimize {std,ranges}::for_each for iterating over __trees (#164405) This patch optimizes how `for_each` iterates over trees by using recursion and storing pointers to the next nodes on the stack. This avoids pointer chasing through the `__parent_` pointer, reducing cache misses. It also makes use of the compiler being able tail-call optimize the recursive function, removing back-tracking the iterators have to do. ``` Benchmark old new Difference % Difference -------------------------------------------------- -------------- -------------- ------------ -------------- rng::for_each(map<int>)/32 35.19 26.67 -8.52 -24.21% rng::for_each(map<int>)/50 64.13 40.68 -23.45 -36.57% rng::for_each(map<int>)/8 5.06 6.49 1.43 28.21% rng::for_each(map<int>)/8192 22893.89 9266.68 -13627.21 -59.52% rng::for_each(map<int>::iterator)/32 35.51 26.88 -8.63 -24.31% rng::for_each(map<int>::iterator)/50 64.39 41.24 -23.15 -35.95% rng::for_each(map<int>::iterator)/8 5.12 5.93 0.81 15.80% rng::for_each(map<int>::iterator)/8192 21283.14 9736.83 -11546.31 -54.25% rng::for_each(multimap<int>)/32 35.22 26.61 -8.61 -24.45% rng::for_each(multimap<int>)/50 64.10 40.07 -24.03 -37.49% rng::for_each(multimap<int>)/8 5.08 6.69 1.61 31.70% rng::for_each(multimap<int>)/8192 23130.44 9026.16 -14104.28 -60.98% rng::for_each(multimap<int>::iterator)/32 35.40 25.08 -10.32 -29.15% rng::for_each(multimap<int>::iterator)/50 64.19 38.15 -26.04 -40.56% rng::for_each(multimap<int>::iterator)/8 5.04 5.25 0.22 4.31% rng::for_each(multimap<int>::iterator)/8192 22875.97 9392.08 -13483.89 -58.94% rng::for_each(multiset<int>)/32 35.82 27.11 -8.72 -24.33% rng::for_each(multiset<int>)/50 62.92 41.59 -21.32 -33.89% rng::for_each(multiset<int>)/8 4.79 6.79 2.00 41.70% rng::for_each(multiset<int>)/8192 22642.68 9280.95 -13361.73 -59.01% rng::for_each(multiset<int>::iterator)/32 35.76 26.71 -9.04 -25.28% rng::for_each(multiset<int>::iterator)/50 63.44 39.00 -24.44 -38.53% rng::for_each(multiset<int>::iterator)/8 4.90 5.21 0.30 6.18% rng::for_each(multiset<int>::iterator)/8192 19930.45 9867.60 -10062.85 -50.49% rng::for_each(set<int>)/32 35.90 27.30 -8.60 -23.96% rng::for_each(set<int>)/50 63.15 40.75 -22.40 -35.47% rng::for_each(set<int>)/8 4.77 6.83 2.06 43.23% rng::for_each(set<int>)/8192 20262.77 9381.57 -10881.20 -53.70% rng::for_each(set<int>::iterator)/32 36.02 26.42 -9.60 -26.64% rng::for_each(set<int>::iterator)/50 63.29 37.97 -25.32 -40.01% rng::for_each(set<int>::iterator)/8 4.72 5.22 0.50 10.50% rng::for_each(set<int>::iterator)/8192 20041.91 9831.91 -10210.00 -50.94% ```
2025-12-11 23:05:51 -09:00
if (__root->__left_) {
if (std::__tree_iterate_from_root<_Reference>(__break, static_cast<_NodePtr>(__root->__left_), __func, __proj))
return true;
}
if (__break(__root))
return true;
std::__invoke(__func, std::__invoke(__proj, static_cast<_Reference>(__root->__get_value())));
[libc++] Optimize {std,ranges}::for_each for iterating over __trees (#164405) This patch optimizes how `for_each` iterates over trees by using recursion and storing pointers to the next nodes on the stack. This avoids pointer chasing through the `__parent_` pointer, reducing cache misses. It also makes use of the compiler being able tail-call optimize the recursive function, removing back-tracking the iterators have to do. ``` Benchmark old new Difference % Difference -------------------------------------------------- -------------- -------------- ------------ -------------- rng::for_each(map<int>)/32 35.19 26.67 -8.52 -24.21% rng::for_each(map<int>)/50 64.13 40.68 -23.45 -36.57% rng::for_each(map<int>)/8 5.06 6.49 1.43 28.21% rng::for_each(map<int>)/8192 22893.89 9266.68 -13627.21 -59.52% rng::for_each(map<int>::iterator)/32 35.51 26.88 -8.63 -24.31% rng::for_each(map<int>::iterator)/50 64.39 41.24 -23.15 -35.95% rng::for_each(map<int>::iterator)/8 5.12 5.93 0.81 15.80% rng::for_each(map<int>::iterator)/8192 21283.14 9736.83 -11546.31 -54.25% rng::for_each(multimap<int>)/32 35.22 26.61 -8.61 -24.45% rng::for_each(multimap<int>)/50 64.10 40.07 -24.03 -37.49% rng::for_each(multimap<int>)/8 5.08 6.69 1.61 31.70% rng::for_each(multimap<int>)/8192 23130.44 9026.16 -14104.28 -60.98% rng::for_each(multimap<int>::iterator)/32 35.40 25.08 -10.32 -29.15% rng::for_each(multimap<int>::iterator)/50 64.19 38.15 -26.04 -40.56% rng::for_each(multimap<int>::iterator)/8 5.04 5.25 0.22 4.31% rng::for_each(multimap<int>::iterator)/8192 22875.97 9392.08 -13483.89 -58.94% rng::for_each(multiset<int>)/32 35.82 27.11 -8.72 -24.33% rng::for_each(multiset<int>)/50 62.92 41.59 -21.32 -33.89% rng::for_each(multiset<int>)/8 4.79 6.79 2.00 41.70% rng::for_each(multiset<int>)/8192 22642.68 9280.95 -13361.73 -59.01% rng::for_each(multiset<int>::iterator)/32 35.76 26.71 -9.04 -25.28% rng::for_each(multiset<int>::iterator)/50 63.44 39.00 -24.44 -38.53% rng::for_each(multiset<int>::iterator)/8 4.90 5.21 0.30 6.18% rng::for_each(multiset<int>::iterator)/8192 19930.45 9867.60 -10062.85 -50.49% rng::for_each(set<int>)/32 35.90 27.30 -8.60 -23.96% rng::for_each(set<int>)/50 63.15 40.75 -22.40 -35.47% rng::for_each(set<int>)/8 4.77 6.83 2.06 43.23% rng::for_each(set<int>)/8192 20262.77 9381.57 -10881.20 -53.70% rng::for_each(set<int>::iterator)/32 36.02 26.42 -9.60 -26.64% rng::for_each(set<int>::iterator)/50 63.29 37.97 -25.32 -40.01% rng::for_each(set<int>::iterator)/8 4.72 5.22 0.50 10.50% rng::for_each(set<int>::iterator)/8192 20041.91 9831.91 -10210.00 -50.94% ```
2025-12-11 23:05:51 -09:00
if (__root->__right_)
return std::__tree_iterate_from_root<_Reference>(__break, static_cast<_NodePtr>(__root->__right_), __func, __proj);
return false;
}
// Do an in-order traversal of the tree from __first to __last.
template <class _NodeIter, class _Func, class _Proj>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
[libc++] Optimize {std,ranges}::for_each for iterating over __trees (#164405) This patch optimizes how `for_each` iterates over trees by using recursion and storing pointers to the next nodes on the stack. This avoids pointer chasing through the `__parent_` pointer, reducing cache misses. It also makes use of the compiler being able tail-call optimize the recursive function, removing back-tracking the iterators have to do. ``` Benchmark old new Difference % Difference -------------------------------------------------- -------------- -------------- ------------ -------------- rng::for_each(map<int>)/32 35.19 26.67 -8.52 -24.21% rng::for_each(map<int>)/50 64.13 40.68 -23.45 -36.57% rng::for_each(map<int>)/8 5.06 6.49 1.43 28.21% rng::for_each(map<int>)/8192 22893.89 9266.68 -13627.21 -59.52% rng::for_each(map<int>::iterator)/32 35.51 26.88 -8.63 -24.31% rng::for_each(map<int>::iterator)/50 64.39 41.24 -23.15 -35.95% rng::for_each(map<int>::iterator)/8 5.12 5.93 0.81 15.80% rng::for_each(map<int>::iterator)/8192 21283.14 9736.83 -11546.31 -54.25% rng::for_each(multimap<int>)/32 35.22 26.61 -8.61 -24.45% rng::for_each(multimap<int>)/50 64.10 40.07 -24.03 -37.49% rng::for_each(multimap<int>)/8 5.08 6.69 1.61 31.70% rng::for_each(multimap<int>)/8192 23130.44 9026.16 -14104.28 -60.98% rng::for_each(multimap<int>::iterator)/32 35.40 25.08 -10.32 -29.15% rng::for_each(multimap<int>::iterator)/50 64.19 38.15 -26.04 -40.56% rng::for_each(multimap<int>::iterator)/8 5.04 5.25 0.22 4.31% rng::for_each(multimap<int>::iterator)/8192 22875.97 9392.08 -13483.89 -58.94% rng::for_each(multiset<int>)/32 35.82 27.11 -8.72 -24.33% rng::for_each(multiset<int>)/50 62.92 41.59 -21.32 -33.89% rng::for_each(multiset<int>)/8 4.79 6.79 2.00 41.70% rng::for_each(multiset<int>)/8192 22642.68 9280.95 -13361.73 -59.01% rng::for_each(multiset<int>::iterator)/32 35.76 26.71 -9.04 -25.28% rng::for_each(multiset<int>::iterator)/50 63.44 39.00 -24.44 -38.53% rng::for_each(multiset<int>::iterator)/8 4.90 5.21 0.30 6.18% rng::for_each(multiset<int>::iterator)/8192 19930.45 9867.60 -10062.85 -50.49% rng::for_each(set<int>)/32 35.90 27.30 -8.60 -23.96% rng::for_each(set<int>)/50 63.15 40.75 -22.40 -35.47% rng::for_each(set<int>)/8 4.77 6.83 2.06 43.23% rng::for_each(set<int>)/8192 20262.77 9381.57 -10881.20 -53.70% rng::for_each(set<int>::iterator)/32 36.02 26.42 -9.60 -26.64% rng::for_each(set<int>::iterator)/50 63.29 37.97 -25.32 -40.01% rng::for_each(set<int>::iterator)/8 4.72 5.22 0.50 10.50% rng::for_each(set<int>::iterator)/8192 20041.91 9831.91 -10210.00 -50.94% ```
2025-12-11 23:05:51 -09:00
__tree_iterate_subrange(_NodeIter __first_it, _NodeIter __last_it, _Func& __func, _Proj& __proj) {
using _NodePtr = typename _NodeIter::__node_pointer;
using _Reference = typename _NodeIter::reference;
auto __first = __first_it.__ptr_;
auto __last = __last_it.__ptr_;
while (true) {
if (__first == __last)
return;
const auto __nfirst = static_cast<_NodePtr>(__first);
std::__invoke(__func, std::__invoke(__proj, static_cast<_Reference>(__nfirst->__get_value())));
[libc++] Optimize {std,ranges}::for_each for iterating over __trees (#164405) This patch optimizes how `for_each` iterates over trees by using recursion and storing pointers to the next nodes on the stack. This avoids pointer chasing through the `__parent_` pointer, reducing cache misses. It also makes use of the compiler being able tail-call optimize the recursive function, removing back-tracking the iterators have to do. ``` Benchmark old new Difference % Difference -------------------------------------------------- -------------- -------------- ------------ -------------- rng::for_each(map<int>)/32 35.19 26.67 -8.52 -24.21% rng::for_each(map<int>)/50 64.13 40.68 -23.45 -36.57% rng::for_each(map<int>)/8 5.06 6.49 1.43 28.21% rng::for_each(map<int>)/8192 22893.89 9266.68 -13627.21 -59.52% rng::for_each(map<int>::iterator)/32 35.51 26.88 -8.63 -24.31% rng::for_each(map<int>::iterator)/50 64.39 41.24 -23.15 -35.95% rng::for_each(map<int>::iterator)/8 5.12 5.93 0.81 15.80% rng::for_each(map<int>::iterator)/8192 21283.14 9736.83 -11546.31 -54.25% rng::for_each(multimap<int>)/32 35.22 26.61 -8.61 -24.45% rng::for_each(multimap<int>)/50 64.10 40.07 -24.03 -37.49% rng::for_each(multimap<int>)/8 5.08 6.69 1.61 31.70% rng::for_each(multimap<int>)/8192 23130.44 9026.16 -14104.28 -60.98% rng::for_each(multimap<int>::iterator)/32 35.40 25.08 -10.32 -29.15% rng::for_each(multimap<int>::iterator)/50 64.19 38.15 -26.04 -40.56% rng::for_each(multimap<int>::iterator)/8 5.04 5.25 0.22 4.31% rng::for_each(multimap<int>::iterator)/8192 22875.97 9392.08 -13483.89 -58.94% rng::for_each(multiset<int>)/32 35.82 27.11 -8.72 -24.33% rng::for_each(multiset<int>)/50 62.92 41.59 -21.32 -33.89% rng::for_each(multiset<int>)/8 4.79 6.79 2.00 41.70% rng::for_each(multiset<int>)/8192 22642.68 9280.95 -13361.73 -59.01% rng::for_each(multiset<int>::iterator)/32 35.76 26.71 -9.04 -25.28% rng::for_each(multiset<int>::iterator)/50 63.44 39.00 -24.44 -38.53% rng::for_each(multiset<int>::iterator)/8 4.90 5.21 0.30 6.18% rng::for_each(multiset<int>::iterator)/8192 19930.45 9867.60 -10062.85 -50.49% rng::for_each(set<int>)/32 35.90 27.30 -8.60 -23.96% rng::for_each(set<int>)/50 63.15 40.75 -22.40 -35.47% rng::for_each(set<int>)/8 4.77 6.83 2.06 43.23% rng::for_each(set<int>)/8192 20262.77 9381.57 -10881.20 -53.70% rng::for_each(set<int>::iterator)/32 36.02 26.42 -9.60 -26.64% rng::for_each(set<int>::iterator)/50 63.29 37.97 -25.32 -40.01% rng::for_each(set<int>::iterator)/8 4.72 5.22 0.50 10.50% rng::for_each(set<int>::iterator)/8192 20041.91 9831.91 -10210.00 -50.94% ```
2025-12-11 23:05:51 -09:00
if (__nfirst->__right_) {
if (std::__tree_iterate_from_root<_Reference>(
[&](_NodePtr __node) -> bool { return __node == __last; },
static_cast<_NodePtr>(__nfirst->__right_),
__func,
__proj))
return;
}
while (!std::__tree_is_left_child(static_cast<_NodePtr>(__first)))
__first = static_cast<_NodePtr>(__first)->__parent_;
__first = static_cast<_NodePtr>(__first)->__parent_;
}
}
2010-05-11 11:42:16 -08:00
template <class _Tp, class _NodePtr, class _DiffType>
class __tree_iterator {
using _NodeTypes _LIBCPP_NODEBUG = __tree_node_types<_NodePtr>;
// NOLINTNEXTLINE(libcpp-nodebug-on-aliases) lldb relies on this alias for pretty printing
using __node_pointer = _NodePtr;
using __node_base_pointer _LIBCPP_NODEBUG = typename _NodeTypes::__node_base_pointer;
using __end_node_pointer _LIBCPP_NODEBUG = typename _NodeTypes::__end_node_pointer;
2010-05-11 11:42:16 -08:00
__end_node_pointer __ptr_;
2010-05-11 11:42:16 -08:00
public:
using iterator_category = bidirectional_iterator_tag;
using value_type = __get_node_value_type_t<_Tp>;
using difference_type = _DiffType;
using reference = value_type&;
using pointer = __rebind_pointer_t<_NodePtr, value_type>;
2010-05-11 11:42:16 -08:00
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree_iterator() _NOEXCEPT : __ptr_(nullptr) {}
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 reference operator*() const { return __get_np()->__get_value(); }
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pointer operator->() const {
return pointer_traits<pointer>::pointer_to(__get_np()->__get_value());
}
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree_iterator& operator++() {
__ptr_ = std::__tree_next_iter<__end_node_pointer>(std::__static_fancy_pointer_cast<__node_base_pointer>(__ptr_));
return *this;
}
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree_iterator operator++(int) {
2010-05-11 11:42:16 -08:00
__tree_iterator __t(*this);
++(*this);
return __t;
}
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree_iterator& operator--() {
__ptr_ = std::__static_fancy_pointer_cast<__end_node_pointer>(std::__tree_prev_iter<__node_base_pointer>(__ptr_));
return *this;
}
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree_iterator operator--(int) {
2010-05-11 11:42:16 -08:00
__tree_iterator __t(*this);
--(*this);
Teach __tree how to handle map's __value_type This patch is fairly large and contains a number of changes. The changes all work towards allowing __tree to properly handle __value_type esspecially when inserting into the __tree. I chose not to break this change into smaller patches because it wouldn't be possible to write meaningful standard-compliant tests for each patch. It is very similar to r260513 "[libcxx] Teach __hash_table how to handle unordered_map's __hash_value_type". Changes in <map> * Remove __value_type's constructors because it should never be constructed directly. * Make map::emplace and multimap::emplace forward to __tree and remove the old definitions * Remove "__construct_node" map and multimap member functions. Almost all of the construction is done within __tree. * Fix map's move constructor to access "__value_type.__nc" directly and pass this object to __tree::insert. Changes in <__tree> * Add traits to detect, handle, and unwrap, map's "__value_type". * Convert methods taking "value_type" to take "__container_value_type" instead. Previously these methods caused unwanted implicit conversions from "std::pair<Key, Value>" to "__value_type<Key, Value>". * Delete __tree_node and __tree_node_base's constructors and assignment operators. The node types should never be constructed because the "__value_" member of __tree_node must be constructed directly by the allocator. * Make the __tree_node_destructor class and "__construct_node" methods unwrap "__node_value_type" into "__container_value_type" before invoking the allocator. The user's allocator can only be used to construct and destroy the container's value_type. Passing it map's "__value_type" was incorrect. * Cleanup the "__insert" and "__emplace" methods. Have __insert forward to an __emplace function wherever possible to reduce code duplication. __insert_unique(value_type const&) and __insert_unique(value_type&&) forward to __emplace_unique_key_args. These functions will not allocate a new node if the value is already in the tree. * Change the __find* functions to take the "key_type" directly instead of passing in "value_type" and unwrapping the key later. This change allows the find functions to be used without having to construct a "value_type" first. This allows for a number of optimizations. * Teach __move_assign and __assign_multi methods to unwrap map's __value_type. llvm-svn: 264986
2016-03-30 18:15:15 -08:00
return __t;
}
friend _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool
operator==(const __tree_iterator& __x, const __tree_iterator& __y) {
2010-05-11 11:42:16 -08:00
return __x.__ptr_ == __y.__ptr_;
}
friend _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool
operator!=(const __tree_iterator& __x, const __tree_iterator& __y) {
2010-05-11 11:42:16 -08:00
return !(__x == __y);
}
private:
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 explicit __tree_iterator(__node_pointer __p) _NOEXCEPT
: __ptr_(std::__static_fancy_pointer_cast<__end_node_pointer>(__p)) {}
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 explicit __tree_iterator(__end_node_pointer __p) _NOEXCEPT
: __ptr_(__p) {}
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __node_pointer __get_np() const {
return std::__static_fancy_pointer_cast<__node_pointer>(__ptr_);
}
2010-05-11 11:42:16 -08:00
template <class, class, class>
friend class __tree;
template <class, class, class>
friend class __tree_const_iterator;
[libc++] Optimize {std,ranges}::for_each for iterating over __trees (#164405) This patch optimizes how `for_each` iterates over trees by using recursion and storing pointers to the next nodes on the stack. This avoids pointer chasing through the `__parent_` pointer, reducing cache misses. It also makes use of the compiler being able tail-call optimize the recursive function, removing back-tracking the iterators have to do. ``` Benchmark old new Difference % Difference -------------------------------------------------- -------------- -------------- ------------ -------------- rng::for_each(map<int>)/32 35.19 26.67 -8.52 -24.21% rng::for_each(map<int>)/50 64.13 40.68 -23.45 -36.57% rng::for_each(map<int>)/8 5.06 6.49 1.43 28.21% rng::for_each(map<int>)/8192 22893.89 9266.68 -13627.21 -59.52% rng::for_each(map<int>::iterator)/32 35.51 26.88 -8.63 -24.31% rng::for_each(map<int>::iterator)/50 64.39 41.24 -23.15 -35.95% rng::for_each(map<int>::iterator)/8 5.12 5.93 0.81 15.80% rng::for_each(map<int>::iterator)/8192 21283.14 9736.83 -11546.31 -54.25% rng::for_each(multimap<int>)/32 35.22 26.61 -8.61 -24.45% rng::for_each(multimap<int>)/50 64.10 40.07 -24.03 -37.49% rng::for_each(multimap<int>)/8 5.08 6.69 1.61 31.70% rng::for_each(multimap<int>)/8192 23130.44 9026.16 -14104.28 -60.98% rng::for_each(multimap<int>::iterator)/32 35.40 25.08 -10.32 -29.15% rng::for_each(multimap<int>::iterator)/50 64.19 38.15 -26.04 -40.56% rng::for_each(multimap<int>::iterator)/8 5.04 5.25 0.22 4.31% rng::for_each(multimap<int>::iterator)/8192 22875.97 9392.08 -13483.89 -58.94% rng::for_each(multiset<int>)/32 35.82 27.11 -8.72 -24.33% rng::for_each(multiset<int>)/50 62.92 41.59 -21.32 -33.89% rng::for_each(multiset<int>)/8 4.79 6.79 2.00 41.70% rng::for_each(multiset<int>)/8192 22642.68 9280.95 -13361.73 -59.01% rng::for_each(multiset<int>::iterator)/32 35.76 26.71 -9.04 -25.28% rng::for_each(multiset<int>::iterator)/50 63.44 39.00 -24.44 -38.53% rng::for_each(multiset<int>::iterator)/8 4.90 5.21 0.30 6.18% rng::for_each(multiset<int>::iterator)/8192 19930.45 9867.60 -10062.85 -50.49% rng::for_each(set<int>)/32 35.90 27.30 -8.60 -23.96% rng::for_each(set<int>)/50 63.15 40.75 -22.40 -35.47% rng::for_each(set<int>)/8 4.77 6.83 2.06 43.23% rng::for_each(set<int>)/8192 20262.77 9381.57 -10881.20 -53.70% rng::for_each(set<int>::iterator)/32 36.02 26.42 -9.60 -26.64% rng::for_each(set<int>::iterator)/50 63.29 37.97 -25.32 -40.01% rng::for_each(set<int>::iterator)/8 4.72 5.22 0.50 10.50% rng::for_each(set<int>::iterator)/8192 20041.91 9831.91 -10210.00 -50.94% ```
2025-12-11 23:05:51 -09:00
template <class _NodeIter, class _Func, class _Proj>
_LIBCPP_CONSTEXPR_SINCE_CXX26 friend void __tree_iterate_subrange(_NodeIter, _NodeIter, _Func&, _Proj&);
2010-05-11 11:42:16 -08:00
};
[libc++] Optimize {std,ranges}::for_each for iterating over __trees (#164405) This patch optimizes how `for_each` iterates over trees by using recursion and storing pointers to the next nodes on the stack. This avoids pointer chasing through the `__parent_` pointer, reducing cache misses. It also makes use of the compiler being able tail-call optimize the recursive function, removing back-tracking the iterators have to do. ``` Benchmark old new Difference % Difference -------------------------------------------------- -------------- -------------- ------------ -------------- rng::for_each(map<int>)/32 35.19 26.67 -8.52 -24.21% rng::for_each(map<int>)/50 64.13 40.68 -23.45 -36.57% rng::for_each(map<int>)/8 5.06 6.49 1.43 28.21% rng::for_each(map<int>)/8192 22893.89 9266.68 -13627.21 -59.52% rng::for_each(map<int>::iterator)/32 35.51 26.88 -8.63 -24.31% rng::for_each(map<int>::iterator)/50 64.39 41.24 -23.15 -35.95% rng::for_each(map<int>::iterator)/8 5.12 5.93 0.81 15.80% rng::for_each(map<int>::iterator)/8192 21283.14 9736.83 -11546.31 -54.25% rng::for_each(multimap<int>)/32 35.22 26.61 -8.61 -24.45% rng::for_each(multimap<int>)/50 64.10 40.07 -24.03 -37.49% rng::for_each(multimap<int>)/8 5.08 6.69 1.61 31.70% rng::for_each(multimap<int>)/8192 23130.44 9026.16 -14104.28 -60.98% rng::for_each(multimap<int>::iterator)/32 35.40 25.08 -10.32 -29.15% rng::for_each(multimap<int>::iterator)/50 64.19 38.15 -26.04 -40.56% rng::for_each(multimap<int>::iterator)/8 5.04 5.25 0.22 4.31% rng::for_each(multimap<int>::iterator)/8192 22875.97 9392.08 -13483.89 -58.94% rng::for_each(multiset<int>)/32 35.82 27.11 -8.72 -24.33% rng::for_each(multiset<int>)/50 62.92 41.59 -21.32 -33.89% rng::for_each(multiset<int>)/8 4.79 6.79 2.00 41.70% rng::for_each(multiset<int>)/8192 22642.68 9280.95 -13361.73 -59.01% rng::for_each(multiset<int>::iterator)/32 35.76 26.71 -9.04 -25.28% rng::for_each(multiset<int>::iterator)/50 63.44 39.00 -24.44 -38.53% rng::for_each(multiset<int>::iterator)/8 4.90 5.21 0.30 6.18% rng::for_each(multiset<int>::iterator)/8192 19930.45 9867.60 -10062.85 -50.49% rng::for_each(set<int>)/32 35.90 27.30 -8.60 -23.96% rng::for_each(set<int>)/50 63.15 40.75 -22.40 -35.47% rng::for_each(set<int>)/8 4.77 6.83 2.06 43.23% rng::for_each(set<int>)/8192 20262.77 9381.57 -10881.20 -53.70% rng::for_each(set<int>::iterator)/32 36.02 26.42 -9.60 -26.64% rng::for_each(set<int>::iterator)/50 63.29 37.97 -25.32 -40.01% rng::for_each(set<int>::iterator)/8 4.72 5.22 0.50 10.50% rng::for_each(set<int>::iterator)/8192 20041.91 9831.91 -10210.00 -50.94% ```
2025-12-11 23:05:51 -09:00
#ifndef _LIBCPP_CXX03_LANG
// This also handles {multi,}set::iterator, since they're just aliases to __tree::iterator
template <class _Tp, class _NodePtr, class _DiffType>
struct __specialized_algorithm<
_Algorithm::__for_each,
__iterator_pair<__tree_iterator<_Tp, _NodePtr, _DiffType>, __tree_iterator<_Tp, _NodePtr, _DiffType>>> {
static const bool __has_algorithm = true;
using __iterator _LIBCPP_NODEBUG = __tree_iterator<_Tp, _NodePtr, _DiffType>;
template <class _Func, class _Proj>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 static void
operator()(__iterator __first, __iterator __last, _Func& __func, _Proj& __proj) {
[libc++] Optimize {std,ranges}::for_each for iterating over __trees (#164405) This patch optimizes how `for_each` iterates over trees by using recursion and storing pointers to the next nodes on the stack. This avoids pointer chasing through the `__parent_` pointer, reducing cache misses. It also makes use of the compiler being able tail-call optimize the recursive function, removing back-tracking the iterators have to do. ``` Benchmark old new Difference % Difference -------------------------------------------------- -------------- -------------- ------------ -------------- rng::for_each(map<int>)/32 35.19 26.67 -8.52 -24.21% rng::for_each(map<int>)/50 64.13 40.68 -23.45 -36.57% rng::for_each(map<int>)/8 5.06 6.49 1.43 28.21% rng::for_each(map<int>)/8192 22893.89 9266.68 -13627.21 -59.52% rng::for_each(map<int>::iterator)/32 35.51 26.88 -8.63 -24.31% rng::for_each(map<int>::iterator)/50 64.39 41.24 -23.15 -35.95% rng::for_each(map<int>::iterator)/8 5.12 5.93 0.81 15.80% rng::for_each(map<int>::iterator)/8192 21283.14 9736.83 -11546.31 -54.25% rng::for_each(multimap<int>)/32 35.22 26.61 -8.61 -24.45% rng::for_each(multimap<int>)/50 64.10 40.07 -24.03 -37.49% rng::for_each(multimap<int>)/8 5.08 6.69 1.61 31.70% rng::for_each(multimap<int>)/8192 23130.44 9026.16 -14104.28 -60.98% rng::for_each(multimap<int>::iterator)/32 35.40 25.08 -10.32 -29.15% rng::for_each(multimap<int>::iterator)/50 64.19 38.15 -26.04 -40.56% rng::for_each(multimap<int>::iterator)/8 5.04 5.25 0.22 4.31% rng::for_each(multimap<int>::iterator)/8192 22875.97 9392.08 -13483.89 -58.94% rng::for_each(multiset<int>)/32 35.82 27.11 -8.72 -24.33% rng::for_each(multiset<int>)/50 62.92 41.59 -21.32 -33.89% rng::for_each(multiset<int>)/8 4.79 6.79 2.00 41.70% rng::for_each(multiset<int>)/8192 22642.68 9280.95 -13361.73 -59.01% rng::for_each(multiset<int>::iterator)/32 35.76 26.71 -9.04 -25.28% rng::for_each(multiset<int>::iterator)/50 63.44 39.00 -24.44 -38.53% rng::for_each(multiset<int>::iterator)/8 4.90 5.21 0.30 6.18% rng::for_each(multiset<int>::iterator)/8192 19930.45 9867.60 -10062.85 -50.49% rng::for_each(set<int>)/32 35.90 27.30 -8.60 -23.96% rng::for_each(set<int>)/50 63.15 40.75 -22.40 -35.47% rng::for_each(set<int>)/8 4.77 6.83 2.06 43.23% rng::for_each(set<int>)/8192 20262.77 9381.57 -10881.20 -53.70% rng::for_each(set<int>::iterator)/32 36.02 26.42 -9.60 -26.64% rng::for_each(set<int>::iterator)/50 63.29 37.97 -25.32 -40.01% rng::for_each(set<int>::iterator)/8 4.72 5.22 0.50 10.50% rng::for_each(set<int>::iterator)/8192 20041.91 9831.91 -10210.00 -50.94% ```
2025-12-11 23:05:51 -09:00
std::__tree_iterate_subrange(__first, __last, __func, __proj);
}
};
#endif
template <class _Tp, class _NodePtr, class _DiffType>
class __tree_const_iterator {
using _NodeTypes _LIBCPP_NODEBUG = __tree_node_types<_NodePtr>;
// NOLINTNEXTLINE(libcpp-nodebug-on-aliases) lldb relies on this alias for pretty printing
using __node_pointer = _NodePtr;
using __node_base_pointer _LIBCPP_NODEBUG = typename _NodeTypes::__node_base_pointer;
using __end_node_pointer _LIBCPP_NODEBUG = typename _NodeTypes::__end_node_pointer;
2010-05-11 11:42:16 -08:00
__end_node_pointer __ptr_;
2010-05-11 11:42:16 -08:00
public:
using iterator_category = bidirectional_iterator_tag;
using value_type = __get_node_value_type_t<_Tp>;
using difference_type = _DiffType;
using reference = const value_type&;
using pointer = __rebind_pointer_t<_NodePtr, const value_type>;
using __non_const_iterator _LIBCPP_NODEBUG = __tree_iterator<_Tp, __node_pointer, difference_type>;
2010-05-11 11:42:16 -08:00
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree_const_iterator() _NOEXCEPT : __ptr_(nullptr) {}
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree_const_iterator(__non_const_iterator __p) _NOEXCEPT
: __ptr_(__p.__ptr_) {}
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 reference operator*() const { return __get_np()->__get_value(); }
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pointer operator->() const {
return pointer_traits<pointer>::pointer_to(__get_np()->__get_value());
}
2010-05-11 11:42:16 -08:00
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree_const_iterator& operator++() {
__ptr_ = std::__tree_next_iter<__end_node_pointer>(std::__static_fancy_pointer_cast<__node_base_pointer>(__ptr_));
return *this;
}
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree_const_iterator operator++(int) {
2010-05-11 11:42:16 -08:00
__tree_const_iterator __t(*this);
++(*this);
return __t;
}
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree_const_iterator& operator--() {
__ptr_ = std::__static_fancy_pointer_cast<__end_node_pointer>(std::__tree_prev_iter<__node_base_pointer>(__ptr_));
2010-05-11 11:42:16 -08:00
return *this;
}
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree_const_iterator operator--(int) {
2011-06-04 06:31:57 -08:00
__tree_const_iterator __t(*this);
2010-05-11 11:42:16 -08:00
--(*this);
2011-06-04 06:31:57 -08:00
return __t;
}
friend _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool
operator==(const __tree_const_iterator& __x, const __tree_const_iterator& __y) {
Fix undefined behavior in __tree Summary: This patch attempts to fix the undefined behavior in __tree by changing the node pointer types used throughout. The pointer types are changed for raw pointers in the current ABI and for fancy pointers in ABI V2 (since the fancy pointer types may not be ABI compatible). The UB in `__tree` arises because tree downcasts the embedded end node and then deferences that pointer. Currently there are 3 node types in __tree. * `__tree_end_node` which contains the `__left_` pointer. This node is embedded within the container. * `__tree_node_base` which contains `__right_`, `__parent_` and `__is_black`. This node is used throughout the tree rebalancing algorithms. * `__tree_node` which contains `__value_`. Currently `__tree` stores the start of the tree, `__begin_node_`, as a pointer to a `__tree_node`. Additionally the iterators store their position as a pointer to a `__tree_node`. In both of these cases the pointee can be the end node. This is fixed by changing them to store `__tree_end_node` pointers instead. To make this change I introduced an `__iter_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node` in the current one. Both `__tree::__begin_node_` and iterator pointers are now stored as `__iter_pointers`. The other situation where `__tree_end_node` is stored as the wrong type is in `__tree_node_base::__parent_`. Currently `__left_`, `__right_`, and `__parent_` are all `__tree_node_base` pointers. Since the end node will only be stored in `__parent_` the fix is to change `__parent_` to be a pointer to `__tree_end_node`. To make this change I introduced a `__parent_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node_base` in the current one. Note that in the new ABI `__iter_pointer` and `__parent_pointer` are the same type (but not in the old one). The confusion between these two types is unfortunate but it was the best solution I could come up with that maintains the ABI. The typedef changes force a ton of explicit type casts to correct pointer types and to make current code compatible with both the old and new pointer typedefs. This is the bulk of the change and it's really messy. Unfortunately I don't know how to avoid it. Please let me know what you think. Reviewers: howard.hinnant, mclow.lists Subscribers: howard.hinnant, bbannier, cfe-commits Differential Revision: https://reviews.llvm.org/D20786 llvm-svn: 276003
2016-07-19 09:56:20 -08:00
return __x.__ptr_ == __y.__ptr_;
}
friend _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool
operator!=(const __tree_const_iterator& __x, const __tree_const_iterator& __y) {
return !(__x == __y);
}
Fix undefined behavior in __tree Summary: This patch attempts to fix the undefined behavior in __tree by changing the node pointer types used throughout. The pointer types are changed for raw pointers in the current ABI and for fancy pointers in ABI V2 (since the fancy pointer types may not be ABI compatible). The UB in `__tree` arises because tree downcasts the embedded end node and then deferences that pointer. Currently there are 3 node types in __tree. * `__tree_end_node` which contains the `__left_` pointer. This node is embedded within the container. * `__tree_node_base` which contains `__right_`, `__parent_` and `__is_black`. This node is used throughout the tree rebalancing algorithms. * `__tree_node` which contains `__value_`. Currently `__tree` stores the start of the tree, `__begin_node_`, as a pointer to a `__tree_node`. Additionally the iterators store their position as a pointer to a `__tree_node`. In both of these cases the pointee can be the end node. This is fixed by changing them to store `__tree_end_node` pointers instead. To make this change I introduced an `__iter_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node` in the current one. Both `__tree::__begin_node_` and iterator pointers are now stored as `__iter_pointers`. The other situation where `__tree_end_node` is stored as the wrong type is in `__tree_node_base::__parent_`. Currently `__left_`, `__right_`, and `__parent_` are all `__tree_node_base` pointers. Since the end node will only be stored in `__parent_` the fix is to change `__parent_` to be a pointer to `__tree_end_node`. To make this change I introduced a `__parent_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node_base` in the current one. Note that in the new ABI `__iter_pointer` and `__parent_pointer` are the same type (but not in the old one). The confusion between these two types is unfortunate but it was the best solution I could come up with that maintains the ABI. The typedef changes force a ton of explicit type casts to correct pointer types and to make current code compatible with both the old and new pointer typedefs. This is the bulk of the change and it's really messy. Unfortunately I don't know how to avoid it. Please let me know what you think. Reviewers: howard.hinnant, mclow.lists Subscribers: howard.hinnant, bbannier, cfe-commits Differential Revision: https://reviews.llvm.org/D20786 llvm-svn: 276003
2016-07-19 09:56:20 -08:00
private:
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 explicit __tree_const_iterator(__node_pointer __p) _NOEXCEPT
: __ptr_(std::__static_fancy_pointer_cast<__end_node_pointer>(__p)) {}
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 explicit __tree_const_iterator(__end_node_pointer __p) _NOEXCEPT
: __ptr_(std::__static_fancy_pointer_cast<__end_node_pointer>(__p)) {}
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __node_pointer __get_np() const {
return std::__static_fancy_pointer_cast<__node_pointer>(__ptr_);
}
2010-05-11 11:42:16 -08:00
template <class, class, class>
friend class __tree;
[libc++] Optimize {std,ranges}::for_each for iterating over __trees (#164405) This patch optimizes how `for_each` iterates over trees by using recursion and storing pointers to the next nodes on the stack. This avoids pointer chasing through the `__parent_` pointer, reducing cache misses. It also makes use of the compiler being able tail-call optimize the recursive function, removing back-tracking the iterators have to do. ``` Benchmark old new Difference % Difference -------------------------------------------------- -------------- -------------- ------------ -------------- rng::for_each(map<int>)/32 35.19 26.67 -8.52 -24.21% rng::for_each(map<int>)/50 64.13 40.68 -23.45 -36.57% rng::for_each(map<int>)/8 5.06 6.49 1.43 28.21% rng::for_each(map<int>)/8192 22893.89 9266.68 -13627.21 -59.52% rng::for_each(map<int>::iterator)/32 35.51 26.88 -8.63 -24.31% rng::for_each(map<int>::iterator)/50 64.39 41.24 -23.15 -35.95% rng::for_each(map<int>::iterator)/8 5.12 5.93 0.81 15.80% rng::for_each(map<int>::iterator)/8192 21283.14 9736.83 -11546.31 -54.25% rng::for_each(multimap<int>)/32 35.22 26.61 -8.61 -24.45% rng::for_each(multimap<int>)/50 64.10 40.07 -24.03 -37.49% rng::for_each(multimap<int>)/8 5.08 6.69 1.61 31.70% rng::for_each(multimap<int>)/8192 23130.44 9026.16 -14104.28 -60.98% rng::for_each(multimap<int>::iterator)/32 35.40 25.08 -10.32 -29.15% rng::for_each(multimap<int>::iterator)/50 64.19 38.15 -26.04 -40.56% rng::for_each(multimap<int>::iterator)/8 5.04 5.25 0.22 4.31% rng::for_each(multimap<int>::iterator)/8192 22875.97 9392.08 -13483.89 -58.94% rng::for_each(multiset<int>)/32 35.82 27.11 -8.72 -24.33% rng::for_each(multiset<int>)/50 62.92 41.59 -21.32 -33.89% rng::for_each(multiset<int>)/8 4.79 6.79 2.00 41.70% rng::for_each(multiset<int>)/8192 22642.68 9280.95 -13361.73 -59.01% rng::for_each(multiset<int>::iterator)/32 35.76 26.71 -9.04 -25.28% rng::for_each(multiset<int>::iterator)/50 63.44 39.00 -24.44 -38.53% rng::for_each(multiset<int>::iterator)/8 4.90 5.21 0.30 6.18% rng::for_each(multiset<int>::iterator)/8192 19930.45 9867.60 -10062.85 -50.49% rng::for_each(set<int>)/32 35.90 27.30 -8.60 -23.96% rng::for_each(set<int>)/50 63.15 40.75 -22.40 -35.47% rng::for_each(set<int>)/8 4.77 6.83 2.06 43.23% rng::for_each(set<int>)/8192 20262.77 9381.57 -10881.20 -53.70% rng::for_each(set<int>::iterator)/32 36.02 26.42 -9.60 -26.64% rng::for_each(set<int>::iterator)/50 63.29 37.97 -25.32 -40.01% rng::for_each(set<int>::iterator)/8 4.72 5.22 0.50 10.50% rng::for_each(set<int>::iterator)/8192 20041.91 9831.91 -10210.00 -50.94% ```
2025-12-11 23:05:51 -09:00
template <class _NodeIter, class _Func, class _Proj>
_LIBCPP_CONSTEXPR_SINCE_CXX26 friend void __tree_iterate_subrange(_NodeIter, _NodeIter, _Func&, _Proj&);
[libc++] Optimize {std,ranges}::for_each for iterating over __trees (#164405) This patch optimizes how `for_each` iterates over trees by using recursion and storing pointers to the next nodes on the stack. This avoids pointer chasing through the `__parent_` pointer, reducing cache misses. It also makes use of the compiler being able tail-call optimize the recursive function, removing back-tracking the iterators have to do. ``` Benchmark old new Difference % Difference -------------------------------------------------- -------------- -------------- ------------ -------------- rng::for_each(map<int>)/32 35.19 26.67 -8.52 -24.21% rng::for_each(map<int>)/50 64.13 40.68 -23.45 -36.57% rng::for_each(map<int>)/8 5.06 6.49 1.43 28.21% rng::for_each(map<int>)/8192 22893.89 9266.68 -13627.21 -59.52% rng::for_each(map<int>::iterator)/32 35.51 26.88 -8.63 -24.31% rng::for_each(map<int>::iterator)/50 64.39 41.24 -23.15 -35.95% rng::for_each(map<int>::iterator)/8 5.12 5.93 0.81 15.80% rng::for_each(map<int>::iterator)/8192 21283.14 9736.83 -11546.31 -54.25% rng::for_each(multimap<int>)/32 35.22 26.61 -8.61 -24.45% rng::for_each(multimap<int>)/50 64.10 40.07 -24.03 -37.49% rng::for_each(multimap<int>)/8 5.08 6.69 1.61 31.70% rng::for_each(multimap<int>)/8192 23130.44 9026.16 -14104.28 -60.98% rng::for_each(multimap<int>::iterator)/32 35.40 25.08 -10.32 -29.15% rng::for_each(multimap<int>::iterator)/50 64.19 38.15 -26.04 -40.56% rng::for_each(multimap<int>::iterator)/8 5.04 5.25 0.22 4.31% rng::for_each(multimap<int>::iterator)/8192 22875.97 9392.08 -13483.89 -58.94% rng::for_each(multiset<int>)/32 35.82 27.11 -8.72 -24.33% rng::for_each(multiset<int>)/50 62.92 41.59 -21.32 -33.89% rng::for_each(multiset<int>)/8 4.79 6.79 2.00 41.70% rng::for_each(multiset<int>)/8192 22642.68 9280.95 -13361.73 -59.01% rng::for_each(multiset<int>::iterator)/32 35.76 26.71 -9.04 -25.28% rng::for_each(multiset<int>::iterator)/50 63.44 39.00 -24.44 -38.53% rng::for_each(multiset<int>::iterator)/8 4.90 5.21 0.30 6.18% rng::for_each(multiset<int>::iterator)/8192 19930.45 9867.60 -10062.85 -50.49% rng::for_each(set<int>)/32 35.90 27.30 -8.60 -23.96% rng::for_each(set<int>)/50 63.15 40.75 -22.40 -35.47% rng::for_each(set<int>)/8 4.77 6.83 2.06 43.23% rng::for_each(set<int>)/8192 20262.77 9381.57 -10881.20 -53.70% rng::for_each(set<int>::iterator)/32 36.02 26.42 -9.60 -26.64% rng::for_each(set<int>::iterator)/50 63.29 37.97 -25.32 -40.01% rng::for_each(set<int>::iterator)/8 4.72 5.22 0.50 10.50% rng::for_each(set<int>::iterator)/8192 20041.91 9831.91 -10210.00 -50.94% ```
2025-12-11 23:05:51 -09:00
};
#ifndef _LIBCPP_CXX03_LANG
// This also handles {multi,}set::const_iterator, since they're just aliases to __tree::iterator
template <class _Tp, class _NodePtr, class _DiffType>
struct __specialized_algorithm<
_Algorithm::__for_each,
__iterator_pair<__tree_const_iterator<_Tp, _NodePtr, _DiffType>, __tree_const_iterator<_Tp, _NodePtr, _DiffType>>> {
static const bool __has_algorithm = true;
using __iterator _LIBCPP_NODEBUG = __tree_const_iterator<_Tp, _NodePtr, _DiffType>;
template <class _Func, class _Proj>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 static void
operator()(__iterator __first, __iterator __last, _Func& __func, _Proj& __proj) {
[libc++] Optimize {std,ranges}::for_each for iterating over __trees (#164405) This patch optimizes how `for_each` iterates over trees by using recursion and storing pointers to the next nodes on the stack. This avoids pointer chasing through the `__parent_` pointer, reducing cache misses. It also makes use of the compiler being able tail-call optimize the recursive function, removing back-tracking the iterators have to do. ``` Benchmark old new Difference % Difference -------------------------------------------------- -------------- -------------- ------------ -------------- rng::for_each(map<int>)/32 35.19 26.67 -8.52 -24.21% rng::for_each(map<int>)/50 64.13 40.68 -23.45 -36.57% rng::for_each(map<int>)/8 5.06 6.49 1.43 28.21% rng::for_each(map<int>)/8192 22893.89 9266.68 -13627.21 -59.52% rng::for_each(map<int>::iterator)/32 35.51 26.88 -8.63 -24.31% rng::for_each(map<int>::iterator)/50 64.39 41.24 -23.15 -35.95% rng::for_each(map<int>::iterator)/8 5.12 5.93 0.81 15.80% rng::for_each(map<int>::iterator)/8192 21283.14 9736.83 -11546.31 -54.25% rng::for_each(multimap<int>)/32 35.22 26.61 -8.61 -24.45% rng::for_each(multimap<int>)/50 64.10 40.07 -24.03 -37.49% rng::for_each(multimap<int>)/8 5.08 6.69 1.61 31.70% rng::for_each(multimap<int>)/8192 23130.44 9026.16 -14104.28 -60.98% rng::for_each(multimap<int>::iterator)/32 35.40 25.08 -10.32 -29.15% rng::for_each(multimap<int>::iterator)/50 64.19 38.15 -26.04 -40.56% rng::for_each(multimap<int>::iterator)/8 5.04 5.25 0.22 4.31% rng::for_each(multimap<int>::iterator)/8192 22875.97 9392.08 -13483.89 -58.94% rng::for_each(multiset<int>)/32 35.82 27.11 -8.72 -24.33% rng::for_each(multiset<int>)/50 62.92 41.59 -21.32 -33.89% rng::for_each(multiset<int>)/8 4.79 6.79 2.00 41.70% rng::for_each(multiset<int>)/8192 22642.68 9280.95 -13361.73 -59.01% rng::for_each(multiset<int>::iterator)/32 35.76 26.71 -9.04 -25.28% rng::for_each(multiset<int>::iterator)/50 63.44 39.00 -24.44 -38.53% rng::for_each(multiset<int>::iterator)/8 4.90 5.21 0.30 6.18% rng::for_each(multiset<int>::iterator)/8192 19930.45 9867.60 -10062.85 -50.49% rng::for_each(set<int>)/32 35.90 27.30 -8.60 -23.96% rng::for_each(set<int>)/50 63.15 40.75 -22.40 -35.47% rng::for_each(set<int>)/8 4.77 6.83 2.06 43.23% rng::for_each(set<int>)/8192 20262.77 9381.57 -10881.20 -53.70% rng::for_each(set<int>::iterator)/32 36.02 26.42 -9.60 -26.64% rng::for_each(set<int>::iterator)/50 63.29 37.97 -25.32 -40.01% rng::for_each(set<int>::iterator)/8 4.72 5.22 0.50 10.50% rng::for_each(set<int>::iterator)/8192 20041.91 9831.91 -10210.00 -50.94% ```
2025-12-11 23:05:51 -09:00
std::__tree_iterate_subrange(__first, __last, __func, __proj);
}
2010-05-11 11:42:16 -08:00
};
[libc++] Optimize {std,ranges}::for_each for iterating over __trees (#164405) This patch optimizes how `for_each` iterates over trees by using recursion and storing pointers to the next nodes on the stack. This avoids pointer chasing through the `__parent_` pointer, reducing cache misses. It also makes use of the compiler being able tail-call optimize the recursive function, removing back-tracking the iterators have to do. ``` Benchmark old new Difference % Difference -------------------------------------------------- -------------- -------------- ------------ -------------- rng::for_each(map<int>)/32 35.19 26.67 -8.52 -24.21% rng::for_each(map<int>)/50 64.13 40.68 -23.45 -36.57% rng::for_each(map<int>)/8 5.06 6.49 1.43 28.21% rng::for_each(map<int>)/8192 22893.89 9266.68 -13627.21 -59.52% rng::for_each(map<int>::iterator)/32 35.51 26.88 -8.63 -24.31% rng::for_each(map<int>::iterator)/50 64.39 41.24 -23.15 -35.95% rng::for_each(map<int>::iterator)/8 5.12 5.93 0.81 15.80% rng::for_each(map<int>::iterator)/8192 21283.14 9736.83 -11546.31 -54.25% rng::for_each(multimap<int>)/32 35.22 26.61 -8.61 -24.45% rng::for_each(multimap<int>)/50 64.10 40.07 -24.03 -37.49% rng::for_each(multimap<int>)/8 5.08 6.69 1.61 31.70% rng::for_each(multimap<int>)/8192 23130.44 9026.16 -14104.28 -60.98% rng::for_each(multimap<int>::iterator)/32 35.40 25.08 -10.32 -29.15% rng::for_each(multimap<int>::iterator)/50 64.19 38.15 -26.04 -40.56% rng::for_each(multimap<int>::iterator)/8 5.04 5.25 0.22 4.31% rng::for_each(multimap<int>::iterator)/8192 22875.97 9392.08 -13483.89 -58.94% rng::for_each(multiset<int>)/32 35.82 27.11 -8.72 -24.33% rng::for_each(multiset<int>)/50 62.92 41.59 -21.32 -33.89% rng::for_each(multiset<int>)/8 4.79 6.79 2.00 41.70% rng::for_each(multiset<int>)/8192 22642.68 9280.95 -13361.73 -59.01% rng::for_each(multiset<int>::iterator)/32 35.76 26.71 -9.04 -25.28% rng::for_each(multiset<int>::iterator)/50 63.44 39.00 -24.44 -38.53% rng::for_each(multiset<int>::iterator)/8 4.90 5.21 0.30 6.18% rng::for_each(multiset<int>::iterator)/8192 19930.45 9867.60 -10062.85 -50.49% rng::for_each(set<int>)/32 35.90 27.30 -8.60 -23.96% rng::for_each(set<int>)/50 63.15 40.75 -22.40 -35.47% rng::for_each(set<int>)/8 4.77 6.83 2.06 43.23% rng::for_each(set<int>)/8192 20262.77 9381.57 -10881.20 -53.70% rng::for_each(set<int>::iterator)/32 36.02 26.42 -9.60 -26.64% rng::for_each(set<int>::iterator)/50 63.29 37.97 -25.32 -40.01% rng::for_each(set<int>::iterator)/8 4.72 5.22 0.50 10.50% rng::for_each(set<int>::iterator)/8192 20041.91 9831.91 -10210.00 -50.94% ```
2025-12-11 23:05:51 -09:00
#endif
2010-05-11 11:42:16 -08:00
template <class _Tp, class _Compare>
#ifndef _LIBCPP_CXX03_LANG
_LIBCPP_DIAGNOSE_WARNING(!__is_invocable_v<_Compare const&, _Tp const&, _Tp const&>,
"the specified comparator type does not provide a viable const call operator")
#endif
int __diagnose_non_const_comparator();
2010-05-11 11:42:16 -08:00
template <class _Tp, class _Compare, class _Allocator>
class __tree {
public:
using value_type = __get_node_value_type_t<_Tp>;
using value_compare = _Compare;
using allocator_type = _Allocator;
private:
using __alloc_traits _LIBCPP_NODEBUG = allocator_traits<allocator_type>;
using key_type = __get_tree_key_type_t<_Tp>;
public:
using pointer = typename __alloc_traits::pointer;
using const_pointer = typename __alloc_traits::const_pointer;
using size_type = typename __alloc_traits::size_type;
using difference_type = typename __alloc_traits::difference_type;
2010-05-11 11:42:16 -08:00
using __void_pointer _LIBCPP_NODEBUG = typename __alloc_traits::void_pointer;
using __node _LIBCPP_NODEBUG = __tree_node<_Tp, __void_pointer>;
// NOLINTNEXTLINE(libcpp-nodebug-on-aliases) lldb relies on this alias for pretty printing
using __node_pointer = __rebind_pointer_t<__void_pointer, __node>;
using __node_base _LIBCPP_NODEBUG = __tree_node_base<__void_pointer>;
using __node_base_pointer _LIBCPP_NODEBUG = __rebind_pointer_t<__void_pointer, __node_base>;
using __end_node_t _LIBCPP_NODEBUG = __tree_end_node<__node_base_pointer>;
using __end_node_pointer _LIBCPP_NODEBUG = __rebind_pointer_t<__void_pointer, __end_node_t>;
using __node_allocator _LIBCPP_NODEBUG = __rebind_alloc<__alloc_traits, __node>;
using __node_traits _LIBCPP_NODEBUG = allocator_traits<__node_allocator>;
2010-05-11 11:42:16 -08:00
private:
// check for sane allocator pointer rebinding semantics. Rebinding the
// allocator for a new pointer type should be exactly the same as rebinding
// the pointer using 'pointer_traits'.
static_assert(is_same<__node_pointer, typename __node_traits::pointer>::value,
"Allocator does not rebind pointers in a sane manner.");
using __node_base_allocator _LIBCPP_NODEBUG = __rebind_alloc<__node_traits, __node_base>;
using __node_base_traits _LIBCPP_NODEBUG = allocator_traits<__node_base_allocator>;
static_assert(is_same<__node_base_pointer, typename __node_base_traits::pointer>::value,
"Allocator does not rebind pointers in a sane manner.");
2010-05-11 11:42:16 -08:00
private:
__end_node_pointer __begin_node_;
_LIBCPP_COMPRESSED_PAIR(__end_node_t, __end_node_, __node_allocator, __node_alloc_);
_LIBCPP_COMPRESSED_PAIR(size_type, __size_, value_compare, __value_comp_);
2010-05-11 11:42:16 -08:00
public:
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __end_node_pointer __end_node() _NOEXCEPT {
return pointer_traits<__end_node_pointer>::pointer_to(__end_node_);
2010-05-11 11:42:16 -08:00
}
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __end_node_pointer __end_node() const _NOEXCEPT {
return pointer_traits<__end_node_pointer>::pointer_to(const_cast<__end_node_t&>(__end_node_));
2010-05-11 11:42:16 -08:00
}
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __node_allocator& __node_alloc() _NOEXCEPT {
return __node_alloc_;
}
2010-05-11 11:42:16 -08:00
private:
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const __node_allocator& __node_alloc() const _NOEXCEPT {
return __node_alloc_;
}
2010-05-11 11:42:16 -08:00
public:
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 allocator_type __alloc() const _NOEXCEPT {
return allocator_type(__node_alloc());
}
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type size() const _NOEXCEPT { return __size_; }
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 value_compare& value_comp() _NOEXCEPT { return __value_comp_; }
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const value_compare& value_comp() const _NOEXCEPT {
return __value_comp_;
}
2010-05-11 11:42:16 -08:00
public:
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __node_pointer __root() const _NOEXCEPT {
return std::__static_fancy_pointer_cast<__node_pointer>(__end_node()->__left_);
}
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __node_base_pointer* __root_ptr() const _NOEXCEPT {
return std::addressof(__end_node()->__left_);
}
2010-05-11 11:42:16 -08:00
using iterator = __tree_iterator<_Tp, __node_pointer, difference_type>;
using const_iterator = __tree_const_iterator<_Tp, __node_pointer, difference_type>;
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 explicit __tree(const value_compare& __comp) _NOEXCEPT_(
is_nothrow_default_constructible<__node_allocator>::value&& is_nothrow_copy_constructible<value_compare>::value)
: __size_(0), __value_comp_(__comp) {
__begin_node_ = __end_node();
}
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 explicit __tree(const allocator_type& __a)
: __begin_node_(), __node_alloc_(__node_allocator(__a)), __size_(0) {
__begin_node_ = __end_node();
}
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree(const value_compare& __comp, const allocator_type& __a)
: __begin_node_(), __node_alloc_(__node_allocator(__a)), __size_(0), __value_comp_(__comp) {
__begin_node_ = __end_node();
}
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree(const __tree& __t);
[libc++] Optimize __tree copy/move constructor/assignment with allocator (#163558) This patch applies the same optimization as implemented in #151304 to the overloads taking an allocator as the second argument. Apple M4: ``` Benchmark old new Difference % Difference ----------------------------------------------------------- -------------- -------------- ------------ -------------- std::map<int,_int>::ctor(&&,_different_allocs)/0 14.59 12.78 -1.81 -12.41% std::map<int,_int>::ctor(&&,_different_allocs)/1024 16407.05 6265.11 -10141.94 -61.81% std::map<int,_int>::ctor(&&,_different_allocs)/32 395.99 199.76 -196.23 -49.56% std::map<int,_int>::ctor(&&,_different_allocs)/8192 141478.67 53767.84 -87710.83 -62.00% std::map<int,_int>::ctor(const&,_alloc)/0 12.83 12.71 -0.12 -0.94% std::map<int,_int>::ctor(const&,_alloc)/1024 9979.71 7849.11 -2130.59 -21.35% std::map<int,_int>::ctor(const&,_alloc)/32 283.82 266.05 -17.77 -6.26% std::map<int,_int>::ctor(const&,_alloc)/8192 81418.63 63190.41 -18228.21 -22.39% std::map<std::string,_int>::ctor(&&,_different_allocs)/0 14.58 12.68 -1.90 -13.00% std::map<std::string,_int>::ctor(&&,_different_allocs)/1024 19513.56 7806.04 -11707.52 -60.00% std::map<std::string,_int>::ctor(&&,_different_allocs)/32 477.80 247.28 -230.52 -48.25% std::map<std::string,_int>::ctor(&&,_different_allocs)/8192 504558.78 69592.21 -434966.56 -86.21% std::map<std::string,_int>::ctor(const&,_alloc)/0 12.64 12.60 -0.04 -0.33% std::map<std::string,_int>::ctor(const&,_alloc)/1024 43198.53 37220.54 -5977.99 -13.84% std::map<std::string,_int>::ctor(const&,_alloc)/32 928.39 867.03 -61.36 -6.61% std::map<std::string,_int>::ctor(const&,_alloc)/8192 461313.81 389200.82 -72112.99 -15.63% ```
2025-11-12 05:53:37 -09:00
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree(const __tree& __other, const allocator_type& __alloc)
[libc++] Optimize __tree copy/move constructor/assignment with allocator (#163558) This patch applies the same optimization as implemented in #151304 to the overloads taking an allocator as the second argument. Apple M4: ``` Benchmark old new Difference % Difference ----------------------------------------------------------- -------------- -------------- ------------ -------------- std::map<int,_int>::ctor(&&,_different_allocs)/0 14.59 12.78 -1.81 -12.41% std::map<int,_int>::ctor(&&,_different_allocs)/1024 16407.05 6265.11 -10141.94 -61.81% std::map<int,_int>::ctor(&&,_different_allocs)/32 395.99 199.76 -196.23 -49.56% std::map<int,_int>::ctor(&&,_different_allocs)/8192 141478.67 53767.84 -87710.83 -62.00% std::map<int,_int>::ctor(const&,_alloc)/0 12.83 12.71 -0.12 -0.94% std::map<int,_int>::ctor(const&,_alloc)/1024 9979.71 7849.11 -2130.59 -21.35% std::map<int,_int>::ctor(const&,_alloc)/32 283.82 266.05 -17.77 -6.26% std::map<int,_int>::ctor(const&,_alloc)/8192 81418.63 63190.41 -18228.21 -22.39% std::map<std::string,_int>::ctor(&&,_different_allocs)/0 14.58 12.68 -1.90 -13.00% std::map<std::string,_int>::ctor(&&,_different_allocs)/1024 19513.56 7806.04 -11707.52 -60.00% std::map<std::string,_int>::ctor(&&,_different_allocs)/32 477.80 247.28 -230.52 -48.25% std::map<std::string,_int>::ctor(&&,_different_allocs)/8192 504558.78 69592.21 -434966.56 -86.21% std::map<std::string,_int>::ctor(const&,_alloc)/0 12.64 12.60 -0.04 -0.33% std::map<std::string,_int>::ctor(const&,_alloc)/1024 43198.53 37220.54 -5977.99 -13.84% std::map<std::string,_int>::ctor(const&,_alloc)/32 928.39 867.03 -61.36 -6.61% std::map<std::string,_int>::ctor(const&,_alloc)/8192 461313.81 389200.82 -72112.99 -15.63% ```
2025-11-12 05:53:37 -09:00
: __begin_node_(__end_node()), __node_alloc_(__alloc), __size_(0), __value_comp_(__other.value_comp()) {
if (__other.size() == 0)
return;
*__root_ptr() = std::__static_fancy_pointer_cast<__node_base_pointer>(__copy_construct_tree(__other.__root()));
[libc++] Optimize __tree copy/move constructor/assignment with allocator (#163558) This patch applies the same optimization as implemented in #151304 to the overloads taking an allocator as the second argument. Apple M4: ``` Benchmark old new Difference % Difference ----------------------------------------------------------- -------------- -------------- ------------ -------------- std::map<int,_int>::ctor(&&,_different_allocs)/0 14.59 12.78 -1.81 -12.41% std::map<int,_int>::ctor(&&,_different_allocs)/1024 16407.05 6265.11 -10141.94 -61.81% std::map<int,_int>::ctor(&&,_different_allocs)/32 395.99 199.76 -196.23 -49.56% std::map<int,_int>::ctor(&&,_different_allocs)/8192 141478.67 53767.84 -87710.83 -62.00% std::map<int,_int>::ctor(const&,_alloc)/0 12.83 12.71 -0.12 -0.94% std::map<int,_int>::ctor(const&,_alloc)/1024 9979.71 7849.11 -2130.59 -21.35% std::map<int,_int>::ctor(const&,_alloc)/32 283.82 266.05 -17.77 -6.26% std::map<int,_int>::ctor(const&,_alloc)/8192 81418.63 63190.41 -18228.21 -22.39% std::map<std::string,_int>::ctor(&&,_different_allocs)/0 14.58 12.68 -1.90 -13.00% std::map<std::string,_int>::ctor(&&,_different_allocs)/1024 19513.56 7806.04 -11707.52 -60.00% std::map<std::string,_int>::ctor(&&,_different_allocs)/32 477.80 247.28 -230.52 -48.25% std::map<std::string,_int>::ctor(&&,_different_allocs)/8192 504558.78 69592.21 -434966.56 -86.21% std::map<std::string,_int>::ctor(const&,_alloc)/0 12.64 12.60 -0.04 -0.33% std::map<std::string,_int>::ctor(const&,_alloc)/1024 43198.53 37220.54 -5977.99 -13.84% std::map<std::string,_int>::ctor(const&,_alloc)/32 928.39 867.03 -61.36 -6.61% std::map<std::string,_int>::ctor(const&,_alloc)/8192 461313.81 389200.82 -72112.99 -15.63% ```
2025-11-12 05:53:37 -09:00
__root()->__parent_ = __end_node();
__begin_node_ = std::__static_fancy_pointer_cast<__end_node_pointer>(std::__tree_min(__end_node()->__left_));
[libc++] Optimize __tree copy/move constructor/assignment with allocator (#163558) This patch applies the same optimization as implemented in #151304 to the overloads taking an allocator as the second argument. Apple M4: ``` Benchmark old new Difference % Difference ----------------------------------------------------------- -------------- -------------- ------------ -------------- std::map<int,_int>::ctor(&&,_different_allocs)/0 14.59 12.78 -1.81 -12.41% std::map<int,_int>::ctor(&&,_different_allocs)/1024 16407.05 6265.11 -10141.94 -61.81% std::map<int,_int>::ctor(&&,_different_allocs)/32 395.99 199.76 -196.23 -49.56% std::map<int,_int>::ctor(&&,_different_allocs)/8192 141478.67 53767.84 -87710.83 -62.00% std::map<int,_int>::ctor(const&,_alloc)/0 12.83 12.71 -0.12 -0.94% std::map<int,_int>::ctor(const&,_alloc)/1024 9979.71 7849.11 -2130.59 -21.35% std::map<int,_int>::ctor(const&,_alloc)/32 283.82 266.05 -17.77 -6.26% std::map<int,_int>::ctor(const&,_alloc)/8192 81418.63 63190.41 -18228.21 -22.39% std::map<std::string,_int>::ctor(&&,_different_allocs)/0 14.58 12.68 -1.90 -13.00% std::map<std::string,_int>::ctor(&&,_different_allocs)/1024 19513.56 7806.04 -11707.52 -60.00% std::map<std::string,_int>::ctor(&&,_different_allocs)/32 477.80 247.28 -230.52 -48.25% std::map<std::string,_int>::ctor(&&,_different_allocs)/8192 504558.78 69592.21 -434966.56 -86.21% std::map<std::string,_int>::ctor(const&,_alloc)/0 12.64 12.60 -0.04 -0.33% std::map<std::string,_int>::ctor(const&,_alloc)/1024 43198.53 37220.54 -5977.99 -13.84% std::map<std::string,_int>::ctor(const&,_alloc)/32 928.39 867.03 -61.36 -6.61% std::map<std::string,_int>::ctor(const&,_alloc)/8192 461313.81 389200.82 -72112.99 -15.63% ```
2025-11-12 05:53:37 -09:00
__size_ = __other.size();
}
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree& operator=(const __tree& __t);
template <class _ForwardIterator>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
__assign_unique(_ForwardIterator __first, _ForwardIterator __last);
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree(__tree&& __t) _NOEXCEPT_(
2011-06-04 06:31:57 -08:00
is_nothrow_move_constructible<__node_allocator>::value&& is_nothrow_move_constructible<value_compare>::value);
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree(__tree&& __t, const allocator_type& __a);
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree& operator=(__tree&& __t)
_NOEXCEPT_(is_nothrow_move_assignable<value_compare>::value &&
((__node_traits::propagate_on_container_move_assignment::value &&
is_nothrow_move_assignable<__node_allocator>::value) ||
allocator_traits<__node_allocator>::is_always_equal::value)) {
__move_assign(__t,
integral_constant<bool,
__node_traits::propagate_on_container_move_assignment::value ||
__node_traits::is_always_equal::value>());
return *this;
}
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 ~__tree() {
static_assert(is_copy_constructible<value_compare>::value, "Comparator must be copy-constructible.");
destroy(__root());
}
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator begin() _NOEXCEPT { return iterator(__begin_node_); }
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator begin() const _NOEXCEPT {
return const_iterator(__begin_node_);
}
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator end() _NOEXCEPT { return iterator(__end_node()); }
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator end() const _NOEXCEPT {
return const_iterator(__end_node());
}
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type max_size() const _NOEXCEPT {
Fix undefined behavior in __tree Summary: This patch attempts to fix the undefined behavior in __tree by changing the node pointer types used throughout. The pointer types are changed for raw pointers in the current ABI and for fancy pointers in ABI V2 (since the fancy pointer types may not be ABI compatible). The UB in `__tree` arises because tree downcasts the embedded end node and then deferences that pointer. Currently there are 3 node types in __tree. * `__tree_end_node` which contains the `__left_` pointer. This node is embedded within the container. * `__tree_node_base` which contains `__right_`, `__parent_` and `__is_black`. This node is used throughout the tree rebalancing algorithms. * `__tree_node` which contains `__value_`. Currently `__tree` stores the start of the tree, `__begin_node_`, as a pointer to a `__tree_node`. Additionally the iterators store their position as a pointer to a `__tree_node`. In both of these cases the pointee can be the end node. This is fixed by changing them to store `__tree_end_node` pointers instead. To make this change I introduced an `__iter_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node` in the current one. Both `__tree::__begin_node_` and iterator pointers are now stored as `__iter_pointers`. The other situation where `__tree_end_node` is stored as the wrong type is in `__tree_node_base::__parent_`. Currently `__left_`, `__right_`, and `__parent_` are all `__tree_node_base` pointers. Since the end node will only be stored in `__parent_` the fix is to change `__parent_` to be a pointer to `__tree_end_node`. To make this change I introduced a `__parent_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node_base` in the current one. Note that in the new ABI `__iter_pointer` and `__parent_pointer` are the same type (but not in the old one). The confusion between these two types is unfortunate but it was the best solution I could come up with that maintains the ABI. The typedef changes force a ton of explicit type casts to correct pointer types and to make current code compatible with both the old and new pointer typedefs. This is the bulk of the change and it's really messy. Unfortunately I don't know how to avoid it. Please let me know what you think. Reviewers: howard.hinnant, mclow.lists Subscribers: howard.hinnant, bbannier, cfe-commits Differential Revision: https://reviews.llvm.org/D20786 llvm-svn: 276003
2016-07-19 09:56:20 -08:00
return std::min<size_type>(__node_traits::max_size(__node_alloc()), numeric_limits<difference_type >::max());
}
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void clear() _NOEXCEPT;
Fix undefined behavior in __tree Summary: This patch attempts to fix the undefined behavior in __tree by changing the node pointer types used throughout. The pointer types are changed for raw pointers in the current ABI and for fancy pointers in ABI V2 (since the fancy pointer types may not be ABI compatible). The UB in `__tree` arises because tree downcasts the embedded end node and then deferences that pointer. Currently there are 3 node types in __tree. * `__tree_end_node` which contains the `__left_` pointer. This node is embedded within the container. * `__tree_node_base` which contains `__right_`, `__parent_` and `__is_black`. This node is used throughout the tree rebalancing algorithms. * `__tree_node` which contains `__value_`. Currently `__tree` stores the start of the tree, `__begin_node_`, as a pointer to a `__tree_node`. Additionally the iterators store their position as a pointer to a `__tree_node`. In both of these cases the pointee can be the end node. This is fixed by changing them to store `__tree_end_node` pointers instead. To make this change I introduced an `__iter_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node` in the current one. Both `__tree::__begin_node_` and iterator pointers are now stored as `__iter_pointers`. The other situation where `__tree_end_node` is stored as the wrong type is in `__tree_node_base::__parent_`. Currently `__left_`, `__right_`, and `__parent_` are all `__tree_node_base` pointers. Since the end node will only be stored in `__parent_` the fix is to change `__parent_` to be a pointer to `__tree_end_node`. To make this change I introduced a `__parent_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node_base` in the current one. Note that in the new ABI `__iter_pointer` and `__parent_pointer` are the same type (but not in the old one). The confusion between these two types is unfortunate but it was the best solution I could come up with that maintains the ABI. The typedef changes force a ton of explicit type casts to correct pointer types and to make current code compatible with both the old and new pointer typedefs. This is the bulk of the change and it's really messy. Unfortunately I don't know how to avoid it. Please let me know what you think. Reviewers: howard.hinnant, mclow.lists Subscribers: howard.hinnant, bbannier, cfe-commits Differential Revision: https://reviews.llvm.org/D20786 llvm-svn: 276003
2016-07-19 09:56:20 -08:00
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void swap(__tree& __t)
#if _LIBCPP_STD_VER <= 11
_NOEXCEPT_(__is_nothrow_swappable_v<value_compare> &&
(!__node_traits::propagate_on_container_swap::value || __is_nothrow_swappable_v<__node_allocator>));
#else
_NOEXCEPT_(__is_nothrow_swappable_v<value_compare>);
#endif
Teach __tree how to handle map's __value_type This patch is fairly large and contains a number of changes. The changes all work towards allowing __tree to properly handle __value_type esspecially when inserting into the __tree. I chose not to break this change into smaller patches because it wouldn't be possible to write meaningful standard-compliant tests for each patch. It is very similar to r260513 "[libcxx] Teach __hash_table how to handle unordered_map's __hash_value_type". Changes in <map> * Remove __value_type's constructors because it should never be constructed directly. * Make map::emplace and multimap::emplace forward to __tree and remove the old definitions * Remove "__construct_node" map and multimap member functions. Almost all of the construction is done within __tree. * Fix map's move constructor to access "__value_type.__nc" directly and pass this object to __tree::insert. Changes in <__tree> * Add traits to detect, handle, and unwrap, map's "__value_type". * Convert methods taking "value_type" to take "__container_value_type" instead. Previously these methods caused unwanted implicit conversions from "std::pair<Key, Value>" to "__value_type<Key, Value>". * Delete __tree_node and __tree_node_base's constructors and assignment operators. The node types should never be constructed because the "__value_" member of __tree_node must be constructed directly by the allocator. * Make the __tree_node_destructor class and "__construct_node" methods unwrap "__node_value_type" into "__container_value_type" before invoking the allocator. The user's allocator can only be used to construct and destroy the container's value_type. Passing it map's "__value_type" was incorrect. * Cleanup the "__insert" and "__emplace" methods. Have __insert forward to an __emplace function wherever possible to reduce code duplication. __insert_unique(value_type const&) and __insert_unique(value_type&&) forward to __emplace_unique_key_args. These functions will not allocate a new node if the value is already in the tree. * Change the __find* functions to take the "key_type" directly instead of passing in "value_type" and unwrapping the key later. This change allows the find functions to be used without having to construct a "value_type" first. This allows for a number of optimizations. * Teach __move_assign and __assign_multi methods to unwrap map's __value_type. llvm-svn: 264986
2016-03-30 18:15:15 -08:00
2010-05-11 11:42:16 -08:00
template <class... _Args>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator __emplace_multi(_Args&&... __args);
template <class... _Args>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator
__emplace_hint_multi(const_iterator __p, _Args&&... __args);
template <class... _Args>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<iterator, bool> __emplace_unique(_Args&&... __args) {
return std::__try_key_extraction<key_type>(
[this](const key_type& __key, _Args&&... __args2) {
auto [__parent, __child] = __find_equal(__key);
__node_pointer __r = std::__static_fancy_pointer_cast<__node_pointer>(__child);
bool __inserted = false;
if (__child == nullptr) {
__node_holder __h = __construct_node(std::forward<_Args>(__args2)...);
__insert_node_at(__parent, __child, std::__static_fancy_pointer_cast<__node_base_pointer>(__h.get()));
__r = __h.release();
__inserted = true;
}
return pair<iterator, bool>(iterator(__r), __inserted);
},
[this](_Args&&... __args2) {
__node_holder __h = __construct_node(std::forward<_Args>(__args2)...);
auto [__parent, __child] = __find_equal(__h->__get_value());
__node_pointer __r = std::__static_fancy_pointer_cast<__node_pointer>(__child);
bool __inserted = false;
if (__child == nullptr) {
__insert_node_at(__parent, __child, std::__static_fancy_pointer_cast<__node_base_pointer>(__h.get()));
__r = __h.release();
__inserted = true;
}
return pair<iterator, bool>(iterator(__r), __inserted);
},
std::forward<_Args>(__args)...);
}
template <class... _Args>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<iterator, bool>
__emplace_hint_unique(const_iterator __p, _Args&&... __args) {
return std::__try_key_extraction<key_type>(
[this, __p](const key_type& __key, _Args&&... __args2) {
__node_base_pointer __dummy;
auto [__parent, __child] = __find_equal(__p, __dummy, __key);
__node_pointer __r = std::__static_fancy_pointer_cast<__node_pointer>(__child);
bool __inserted = false;
if (__child == nullptr) {
__node_holder __h = __construct_node(std::forward<_Args>(__args2)...);
__insert_node_at(__parent, __child, std::__static_fancy_pointer_cast<__node_base_pointer>(__h.get()));
__r = __h.release();
__inserted = true;
}
return pair<iterator, bool>(iterator(__r), __inserted);
},
[this, __p](_Args&&... __args2) {
__node_holder __h = __construct_node(std::forward<_Args>(__args2)...);
__node_base_pointer __dummy;
auto [__parent, __child] = __find_equal(__p, __dummy, __h->__get_value());
__node_pointer __r = std::__static_fancy_pointer_cast<__node_pointer>(__child);
if (__child == nullptr) {
__insert_node_at(__parent, __child, std::__static_fancy_pointer_cast<__node_base_pointer>(__h.get()));
__r = __h.release();
}
return pair<iterator, bool>(iterator(__r), __child == nullptr);
},
std::forward<_Args>(__args)...);
Teach __tree how to handle map's __value_type This patch is fairly large and contains a number of changes. The changes all work towards allowing __tree to properly handle __value_type esspecially when inserting into the __tree. I chose not to break this change into smaller patches because it wouldn't be possible to write meaningful standard-compliant tests for each patch. It is very similar to r260513 "[libcxx] Teach __hash_table how to handle unordered_map's __hash_value_type". Changes in <map> * Remove __value_type's constructors because it should never be constructed directly. * Make map::emplace and multimap::emplace forward to __tree and remove the old definitions * Remove "__construct_node" map and multimap member functions. Almost all of the construction is done within __tree. * Fix map's move constructor to access "__value_type.__nc" directly and pass this object to __tree::insert. Changes in <__tree> * Add traits to detect, handle, and unwrap, map's "__value_type". * Convert methods taking "value_type" to take "__container_value_type" instead. Previously these methods caused unwanted implicit conversions from "std::pair<Key, Value>" to "__value_type<Key, Value>". * Delete __tree_node and __tree_node_base's constructors and assignment operators. The node types should never be constructed because the "__value_" member of __tree_node must be constructed directly by the allocator. * Make the __tree_node_destructor class and "__construct_node" methods unwrap "__node_value_type" into "__container_value_type" before invoking the allocator. The user's allocator can only be used to construct and destroy the container's value_type. Passing it map's "__value_type" was incorrect. * Cleanup the "__insert" and "__emplace" methods. Have __insert forward to an __emplace function wherever possible to reduce code duplication. __insert_unique(value_type const&) and __insert_unique(value_type&&) forward to __emplace_unique_key_args. These functions will not allocate a new node if the value is already in the tree. * Change the __find* functions to take the "key_type" directly instead of passing in "value_type" and unwrapping the key later. This change allows the find functions to be used without having to construct a "value_type" first. This allows for a number of optimizations. * Teach __move_assign and __assign_multi methods to unwrap map's __value_type. llvm-svn: 264986
2016-03-30 18:15:15 -08:00
}
template <class _InIter, class _Sent>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __insert_range_multi(_InIter __first, _Sent __last) {
if (__first == __last)
return;
if (__root() == nullptr) { // Make sure we always have a root node
__insert_node_at(__end_node(),
__end_node()->__left_,
std::__static_fancy_pointer_cast<__node_base_pointer>(__construct_node(*__first).release()));
++__first;
}
auto __max_node = std::__static_fancy_pointer_cast<__node_pointer>(
std::__tree_max(std::__static_fancy_pointer_cast<__node_base_pointer>(__root())));
for (; __first != __last; ++__first) {
__node_holder __nd = __construct_node(*__first);
// Always check the max node first. This optimizes for sorted ranges inserted at the end.
if (!value_comp()(__nd->__get_value(), __max_node->__get_value())) { // __node >= __max_val
__insert_node_at(std::__static_fancy_pointer_cast<__end_node_pointer>(__max_node),
__max_node->__right_,
std::__static_fancy_pointer_cast<__node_base_pointer>(__nd.get()));
__max_node = __nd.release();
} else {
__end_node_pointer __parent;
__node_base_pointer& __child = __find_leaf_high(__parent, __nd->__get_value());
__insert_node_at(__parent, __child, std::__static_fancy_pointer_cast<__node_base_pointer>(__nd.release()));
}
}
}
[libc++] Optimize {map,set}::insert(InputIterator, InputIterator) (#154703) ``` ---------------------------------------------------------------------------------------------------------------------------- Benchmark old new ---------------------------------------------------------------------------------------------------------------------------- std::map<int, int>::ctor(iterator, iterator) (unsorted sequence)/0 14.2 ns 14.8 ns std::map<int, int>::ctor(iterator, iterator) (unsorted sequence)/32 519 ns 404 ns std::map<int, int>::ctor(iterator, iterator) (unsorted sequence)/1024 52460 ns 36242 ns std::map<int, int>::ctor(iterator, iterator) (unsorted sequence)/8192 724222 ns 706496 ns std::map<int, int>::ctor(iterator, iterator) (sorted sequence)/0 14.2 ns 14.7 ns std::map<int, int>::ctor(iterator, iterator) (sorted sequence)/32 429 ns 349 ns std::map<int, int>::ctor(iterator, iterator) (sorted sequence)/1024 23601 ns 14734 ns std::map<int, int>::ctor(iterator, iterator) (sorted sequence)/8192 267753 ns 112155 ns std::map<int, int>::insert(iterator, iterator) (all new keys)/0 434 ns 448 ns std::map<int, int>::insert(iterator, iterator) (all new keys)/32 950 ns 963 ns std::map<int, int>::insert(iterator, iterator) (all new keys)/1024 27205 ns 25344 ns std::map<int, int>::insert(iterator, iterator) (all new keys)/8192 294248 ns 280713 ns std::map<int, int>::insert(iterator, iterator) (half new keys)/0 435 ns 449 ns std::map<int, int>::insert(iterator, iterator) (half new keys)/32 771 ns 706 ns std::map<int, int>::insert(iterator, iterator) (half new keys)/1024 30841 ns 17495 ns std::map<int, int>::insert(iterator, iterator) (half new keys)/8192 468807 ns 285847 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from same type)/0 449 ns 453 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from same type)/32 1021 ns 932 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from same type)/1024 29796 ns 19518 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from same type)/8192 345688 ns 153966 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from zip_view)/0 449 ns 450 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from zip_view)/32 1026 ns 807 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from zip_view)/1024 31632 ns 15573 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from zip_view)/8192 303024 ns 128946 ns std::map<int, int>::erase(iterator, iterator) (erase half the container)/0 447 ns 452 ns std::map<int, int>::erase(iterator, iterator) (erase half the container)/32 687 ns 710 ns std::map<int, int>::erase(iterator, iterator) (erase half the container)/1024 8604 ns 8581 ns std::map<int, int>::erase(iterator, iterator) (erase half the container)/8192 65693 ns 67406 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/0 15.0 ns 15.0 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/32 2781 ns 1845 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/1024 187999 ns 182103 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/8192 2937242 ns 2934912 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/0 15.0 ns 15.2 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/32 1326 ns 2462 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/1024 81778 ns 72193 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/8192 1177292 ns 669152 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/0 439 ns 454 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/32 2483 ns 2465 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/1024 187614 ns 188072 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/8192 1654675 ns 1706603 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/0 437 ns 452 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/32 1836 ns 1820 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/1024 114885 ns 121865 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/8192 1151960 ns 1197318 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/0 438 ns 455 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/32 1599 ns 1614 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/1024 95935 ns 82159 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/8192 776480 ns 941043 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/0 435 ns 462 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/32 1723 ns 1550 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/1024 107096 ns 92850 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/8192 893976 ns 775046 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/0 436 ns 453 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/32 775 ns 824 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/1024 20241 ns 20454 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/8192 139038 ns 138032 ns std::set<int>::ctor(iterator, iterator) (unsorted sequence)/0 14.8 ns 14.7 ns std::set<int>::ctor(iterator, iterator) (unsorted sequence)/32 468 ns 426 ns std::set<int>::ctor(iterator, iterator) (unsorted sequence)/1024 54289 ns 39028 ns std::set<int>::ctor(iterator, iterator) (unsorted sequence)/8192 738438 ns 695720 ns std::set<int>::ctor(iterator, iterator) (sorted sequence)/0 14.7 ns 14.6 ns std::set<int>::ctor(iterator, iterator) (sorted sequence)/32 478 ns 391 ns std::set<int>::ctor(iterator, iterator) (sorted sequence)/1024 24017 ns 13905 ns std::set<int>::ctor(iterator, iterator) (sorted sequence)/8192 267862 ns 111378 ns std::set<int>::insert(iterator, iterator) (all new keys)/0 458 ns 450 ns std::set<int>::insert(iterator, iterator) (all new keys)/32 1066 ns 956 ns std::set<int>::insert(iterator, iterator) (all new keys)/1024 29190 ns 25212 ns std::set<int>::insert(iterator, iterator) (all new keys)/8192 320441 ns 279602 ns std::set<int>::insert(iterator, iterator) (half new keys)/0 454 ns 453 ns std::set<int>::insert(iterator, iterator) (half new keys)/32 816 ns 709 ns std::set<int>::insert(iterator, iterator) (half new keys)/1024 32072 ns 17074 ns std::set<int>::insert(iterator, iterator) (half new keys)/8192 403386 ns 286202 ns std::set<int>::erase(iterator, iterator) (erase half the container)/0 451 ns 452 ns std::set<int>::erase(iterator, iterator) (erase half the container)/32 710 ns 703 ns std::set<int>::erase(iterator, iterator) (erase half the container)/1024 8261 ns 8499 ns std::set<int>::erase(iterator, iterator) (erase half the container)/8192 64466 ns 67343 ns std::set<std::string>::ctor(iterator, iterator) (unsorted sequence)/0 15.2 ns 15.0 ns std::set<std::string>::ctor(iterator, iterator) (unsorted sequence)/32 3069 ns 3005 ns std::set<std::string>::ctor(iterator, iterator) (unsorted sequence)/1024 189552 ns 180933 ns std::set<std::string>::ctor(iterator, iterator) (unsorted sequence)/8192 2887579 ns 2691678 ns std::set<std::string>::ctor(iterator, iterator) (sorted sequence)/0 15.1 ns 14.9 ns std::set<std::string>::ctor(iterator, iterator) (sorted sequence)/32 2611 ns 2514 ns std::set<std::string>::ctor(iterator, iterator) (sorted sequence)/1024 91581 ns 78727 ns std::set<std::string>::ctor(iterator, iterator) (sorted sequence)/8192 1192640 ns 1158959 ns std::set<std::string>::insert(iterator, iterator) (all new keys)/0 452 ns 457 ns std::set<std::string>::insert(iterator, iterator) (all new keys)/32 2530 ns 2544 ns std::set<std::string>::insert(iterator, iterator) (all new keys)/1024 195352 ns 179614 ns std::set<std::string>::insert(iterator, iterator) (all new keys)/8192 1737890 ns 1749615 ns std::set<std::string>::insert(iterator, iterator) (half new keys)/0 451 ns 454 ns std::set<std::string>::insert(iterator, iterator) (half new keys)/32 1949 ns 1766 ns std::set<std::string>::insert(iterator, iterator) (half new keys)/1024 128853 ns 109467 ns std::set<std::string>::insert(iterator, iterator) (half new keys)/8192 1233077 ns 1177289 ns std::set<std::string>::erase(iterator, iterator) (erase half the container)/0 450 ns 451 ns std::set<std::string>::erase(iterator, iterator) (erase half the container)/32 809 ns 812 ns std::set<std::string>::erase(iterator, iterator) (erase half the container)/1024 21736 ns 21922 ns std::set<std::string>::erase(iterator, iterator) (erase half the container)/8192 135884 ns 133228 ns ``` Fixes #154650
2025-09-02 22:15:29 -08:00
template <class _InIter, class _Sent>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __insert_range_unique(_InIter __first, _Sent __last) {
[libc++] Optimize {map,set}::insert(InputIterator, InputIterator) (#154703) ``` ---------------------------------------------------------------------------------------------------------------------------- Benchmark old new ---------------------------------------------------------------------------------------------------------------------------- std::map<int, int>::ctor(iterator, iterator) (unsorted sequence)/0 14.2 ns 14.8 ns std::map<int, int>::ctor(iterator, iterator) (unsorted sequence)/32 519 ns 404 ns std::map<int, int>::ctor(iterator, iterator) (unsorted sequence)/1024 52460 ns 36242 ns std::map<int, int>::ctor(iterator, iterator) (unsorted sequence)/8192 724222 ns 706496 ns std::map<int, int>::ctor(iterator, iterator) (sorted sequence)/0 14.2 ns 14.7 ns std::map<int, int>::ctor(iterator, iterator) (sorted sequence)/32 429 ns 349 ns std::map<int, int>::ctor(iterator, iterator) (sorted sequence)/1024 23601 ns 14734 ns std::map<int, int>::ctor(iterator, iterator) (sorted sequence)/8192 267753 ns 112155 ns std::map<int, int>::insert(iterator, iterator) (all new keys)/0 434 ns 448 ns std::map<int, int>::insert(iterator, iterator) (all new keys)/32 950 ns 963 ns std::map<int, int>::insert(iterator, iterator) (all new keys)/1024 27205 ns 25344 ns std::map<int, int>::insert(iterator, iterator) (all new keys)/8192 294248 ns 280713 ns std::map<int, int>::insert(iterator, iterator) (half new keys)/0 435 ns 449 ns std::map<int, int>::insert(iterator, iterator) (half new keys)/32 771 ns 706 ns std::map<int, int>::insert(iterator, iterator) (half new keys)/1024 30841 ns 17495 ns std::map<int, int>::insert(iterator, iterator) (half new keys)/8192 468807 ns 285847 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from same type)/0 449 ns 453 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from same type)/32 1021 ns 932 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from same type)/1024 29796 ns 19518 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from same type)/8192 345688 ns 153966 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from zip_view)/0 449 ns 450 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from zip_view)/32 1026 ns 807 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from zip_view)/1024 31632 ns 15573 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from zip_view)/8192 303024 ns 128946 ns std::map<int, int>::erase(iterator, iterator) (erase half the container)/0 447 ns 452 ns std::map<int, int>::erase(iterator, iterator) (erase half the container)/32 687 ns 710 ns std::map<int, int>::erase(iterator, iterator) (erase half the container)/1024 8604 ns 8581 ns std::map<int, int>::erase(iterator, iterator) (erase half the container)/8192 65693 ns 67406 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/0 15.0 ns 15.0 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/32 2781 ns 1845 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/1024 187999 ns 182103 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/8192 2937242 ns 2934912 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/0 15.0 ns 15.2 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/32 1326 ns 2462 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/1024 81778 ns 72193 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/8192 1177292 ns 669152 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/0 439 ns 454 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/32 2483 ns 2465 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/1024 187614 ns 188072 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/8192 1654675 ns 1706603 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/0 437 ns 452 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/32 1836 ns 1820 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/1024 114885 ns 121865 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/8192 1151960 ns 1197318 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/0 438 ns 455 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/32 1599 ns 1614 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/1024 95935 ns 82159 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/8192 776480 ns 941043 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/0 435 ns 462 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/32 1723 ns 1550 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/1024 107096 ns 92850 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/8192 893976 ns 775046 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/0 436 ns 453 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/32 775 ns 824 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/1024 20241 ns 20454 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/8192 139038 ns 138032 ns std::set<int>::ctor(iterator, iterator) (unsorted sequence)/0 14.8 ns 14.7 ns std::set<int>::ctor(iterator, iterator) (unsorted sequence)/32 468 ns 426 ns std::set<int>::ctor(iterator, iterator) (unsorted sequence)/1024 54289 ns 39028 ns std::set<int>::ctor(iterator, iterator) (unsorted sequence)/8192 738438 ns 695720 ns std::set<int>::ctor(iterator, iterator) (sorted sequence)/0 14.7 ns 14.6 ns std::set<int>::ctor(iterator, iterator) (sorted sequence)/32 478 ns 391 ns std::set<int>::ctor(iterator, iterator) (sorted sequence)/1024 24017 ns 13905 ns std::set<int>::ctor(iterator, iterator) (sorted sequence)/8192 267862 ns 111378 ns std::set<int>::insert(iterator, iterator) (all new keys)/0 458 ns 450 ns std::set<int>::insert(iterator, iterator) (all new keys)/32 1066 ns 956 ns std::set<int>::insert(iterator, iterator) (all new keys)/1024 29190 ns 25212 ns std::set<int>::insert(iterator, iterator) (all new keys)/8192 320441 ns 279602 ns std::set<int>::insert(iterator, iterator) (half new keys)/0 454 ns 453 ns std::set<int>::insert(iterator, iterator) (half new keys)/32 816 ns 709 ns std::set<int>::insert(iterator, iterator) (half new keys)/1024 32072 ns 17074 ns std::set<int>::insert(iterator, iterator) (half new keys)/8192 403386 ns 286202 ns std::set<int>::erase(iterator, iterator) (erase half the container)/0 451 ns 452 ns std::set<int>::erase(iterator, iterator) (erase half the container)/32 710 ns 703 ns std::set<int>::erase(iterator, iterator) (erase half the container)/1024 8261 ns 8499 ns std::set<int>::erase(iterator, iterator) (erase half the container)/8192 64466 ns 67343 ns std::set<std::string>::ctor(iterator, iterator) (unsorted sequence)/0 15.2 ns 15.0 ns std::set<std::string>::ctor(iterator, iterator) (unsorted sequence)/32 3069 ns 3005 ns std::set<std::string>::ctor(iterator, iterator) (unsorted sequence)/1024 189552 ns 180933 ns std::set<std::string>::ctor(iterator, iterator) (unsorted sequence)/8192 2887579 ns 2691678 ns std::set<std::string>::ctor(iterator, iterator) (sorted sequence)/0 15.1 ns 14.9 ns std::set<std::string>::ctor(iterator, iterator) (sorted sequence)/32 2611 ns 2514 ns std::set<std::string>::ctor(iterator, iterator) (sorted sequence)/1024 91581 ns 78727 ns std::set<std::string>::ctor(iterator, iterator) (sorted sequence)/8192 1192640 ns 1158959 ns std::set<std::string>::insert(iterator, iterator) (all new keys)/0 452 ns 457 ns std::set<std::string>::insert(iterator, iterator) (all new keys)/32 2530 ns 2544 ns std::set<std::string>::insert(iterator, iterator) (all new keys)/1024 195352 ns 179614 ns std::set<std::string>::insert(iterator, iterator) (all new keys)/8192 1737890 ns 1749615 ns std::set<std::string>::insert(iterator, iterator) (half new keys)/0 451 ns 454 ns std::set<std::string>::insert(iterator, iterator) (half new keys)/32 1949 ns 1766 ns std::set<std::string>::insert(iterator, iterator) (half new keys)/1024 128853 ns 109467 ns std::set<std::string>::insert(iterator, iterator) (half new keys)/8192 1233077 ns 1177289 ns std::set<std::string>::erase(iterator, iterator) (erase half the container)/0 450 ns 451 ns std::set<std::string>::erase(iterator, iterator) (erase half the container)/32 809 ns 812 ns std::set<std::string>::erase(iterator, iterator) (erase half the container)/1024 21736 ns 21922 ns std::set<std::string>::erase(iterator, iterator) (erase half the container)/8192 135884 ns 133228 ns ``` Fixes #154650
2025-09-02 22:15:29 -08:00
if (__first == __last)
return;
if (__root() == nullptr) {
__insert_node_at(__end_node(),
__end_node()->__left_,
std::__static_fancy_pointer_cast<__node_base_pointer>(__construct_node(*__first).release()));
[libc++] Optimize {map,set}::insert(InputIterator, InputIterator) (#154703) ``` ---------------------------------------------------------------------------------------------------------------------------- Benchmark old new ---------------------------------------------------------------------------------------------------------------------------- std::map<int, int>::ctor(iterator, iterator) (unsorted sequence)/0 14.2 ns 14.8 ns std::map<int, int>::ctor(iterator, iterator) (unsorted sequence)/32 519 ns 404 ns std::map<int, int>::ctor(iterator, iterator) (unsorted sequence)/1024 52460 ns 36242 ns std::map<int, int>::ctor(iterator, iterator) (unsorted sequence)/8192 724222 ns 706496 ns std::map<int, int>::ctor(iterator, iterator) (sorted sequence)/0 14.2 ns 14.7 ns std::map<int, int>::ctor(iterator, iterator) (sorted sequence)/32 429 ns 349 ns std::map<int, int>::ctor(iterator, iterator) (sorted sequence)/1024 23601 ns 14734 ns std::map<int, int>::ctor(iterator, iterator) (sorted sequence)/8192 267753 ns 112155 ns std::map<int, int>::insert(iterator, iterator) (all new keys)/0 434 ns 448 ns std::map<int, int>::insert(iterator, iterator) (all new keys)/32 950 ns 963 ns std::map<int, int>::insert(iterator, iterator) (all new keys)/1024 27205 ns 25344 ns std::map<int, int>::insert(iterator, iterator) (all new keys)/8192 294248 ns 280713 ns std::map<int, int>::insert(iterator, iterator) (half new keys)/0 435 ns 449 ns std::map<int, int>::insert(iterator, iterator) (half new keys)/32 771 ns 706 ns std::map<int, int>::insert(iterator, iterator) (half new keys)/1024 30841 ns 17495 ns std::map<int, int>::insert(iterator, iterator) (half new keys)/8192 468807 ns 285847 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from same type)/0 449 ns 453 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from same type)/32 1021 ns 932 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from same type)/1024 29796 ns 19518 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from same type)/8192 345688 ns 153966 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from zip_view)/0 449 ns 450 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from zip_view)/32 1026 ns 807 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from zip_view)/1024 31632 ns 15573 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from zip_view)/8192 303024 ns 128946 ns std::map<int, int>::erase(iterator, iterator) (erase half the container)/0 447 ns 452 ns std::map<int, int>::erase(iterator, iterator) (erase half the container)/32 687 ns 710 ns std::map<int, int>::erase(iterator, iterator) (erase half the container)/1024 8604 ns 8581 ns std::map<int, int>::erase(iterator, iterator) (erase half the container)/8192 65693 ns 67406 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/0 15.0 ns 15.0 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/32 2781 ns 1845 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/1024 187999 ns 182103 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/8192 2937242 ns 2934912 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/0 15.0 ns 15.2 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/32 1326 ns 2462 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/1024 81778 ns 72193 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/8192 1177292 ns 669152 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/0 439 ns 454 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/32 2483 ns 2465 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/1024 187614 ns 188072 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/8192 1654675 ns 1706603 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/0 437 ns 452 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/32 1836 ns 1820 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/1024 114885 ns 121865 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/8192 1151960 ns 1197318 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/0 438 ns 455 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/32 1599 ns 1614 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/1024 95935 ns 82159 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/8192 776480 ns 941043 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/0 435 ns 462 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/32 1723 ns 1550 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/1024 107096 ns 92850 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/8192 893976 ns 775046 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/0 436 ns 453 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/32 775 ns 824 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/1024 20241 ns 20454 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/8192 139038 ns 138032 ns std::set<int>::ctor(iterator, iterator) (unsorted sequence)/0 14.8 ns 14.7 ns std::set<int>::ctor(iterator, iterator) (unsorted sequence)/32 468 ns 426 ns std::set<int>::ctor(iterator, iterator) (unsorted sequence)/1024 54289 ns 39028 ns std::set<int>::ctor(iterator, iterator) (unsorted sequence)/8192 738438 ns 695720 ns std::set<int>::ctor(iterator, iterator) (sorted sequence)/0 14.7 ns 14.6 ns std::set<int>::ctor(iterator, iterator) (sorted sequence)/32 478 ns 391 ns std::set<int>::ctor(iterator, iterator) (sorted sequence)/1024 24017 ns 13905 ns std::set<int>::ctor(iterator, iterator) (sorted sequence)/8192 267862 ns 111378 ns std::set<int>::insert(iterator, iterator) (all new keys)/0 458 ns 450 ns std::set<int>::insert(iterator, iterator) (all new keys)/32 1066 ns 956 ns std::set<int>::insert(iterator, iterator) (all new keys)/1024 29190 ns 25212 ns std::set<int>::insert(iterator, iterator) (all new keys)/8192 320441 ns 279602 ns std::set<int>::insert(iterator, iterator) (half new keys)/0 454 ns 453 ns std::set<int>::insert(iterator, iterator) (half new keys)/32 816 ns 709 ns std::set<int>::insert(iterator, iterator) (half new keys)/1024 32072 ns 17074 ns std::set<int>::insert(iterator, iterator) (half new keys)/8192 403386 ns 286202 ns std::set<int>::erase(iterator, iterator) (erase half the container)/0 451 ns 452 ns std::set<int>::erase(iterator, iterator) (erase half the container)/32 710 ns 703 ns std::set<int>::erase(iterator, iterator) (erase half the container)/1024 8261 ns 8499 ns std::set<int>::erase(iterator, iterator) (erase half the container)/8192 64466 ns 67343 ns std::set<std::string>::ctor(iterator, iterator) (unsorted sequence)/0 15.2 ns 15.0 ns std::set<std::string>::ctor(iterator, iterator) (unsorted sequence)/32 3069 ns 3005 ns std::set<std::string>::ctor(iterator, iterator) (unsorted sequence)/1024 189552 ns 180933 ns std::set<std::string>::ctor(iterator, iterator) (unsorted sequence)/8192 2887579 ns 2691678 ns std::set<std::string>::ctor(iterator, iterator) (sorted sequence)/0 15.1 ns 14.9 ns std::set<std::string>::ctor(iterator, iterator) (sorted sequence)/32 2611 ns 2514 ns std::set<std::string>::ctor(iterator, iterator) (sorted sequence)/1024 91581 ns 78727 ns std::set<std::string>::ctor(iterator, iterator) (sorted sequence)/8192 1192640 ns 1158959 ns std::set<std::string>::insert(iterator, iterator) (all new keys)/0 452 ns 457 ns std::set<std::string>::insert(iterator, iterator) (all new keys)/32 2530 ns 2544 ns std::set<std::string>::insert(iterator, iterator) (all new keys)/1024 195352 ns 179614 ns std::set<std::string>::insert(iterator, iterator) (all new keys)/8192 1737890 ns 1749615 ns std::set<std::string>::insert(iterator, iterator) (half new keys)/0 451 ns 454 ns std::set<std::string>::insert(iterator, iterator) (half new keys)/32 1949 ns 1766 ns std::set<std::string>::insert(iterator, iterator) (half new keys)/1024 128853 ns 109467 ns std::set<std::string>::insert(iterator, iterator) (half new keys)/8192 1233077 ns 1177289 ns std::set<std::string>::erase(iterator, iterator) (erase half the container)/0 450 ns 451 ns std::set<std::string>::erase(iterator, iterator) (erase half the container)/32 809 ns 812 ns std::set<std::string>::erase(iterator, iterator) (erase half the container)/1024 21736 ns 21922 ns std::set<std::string>::erase(iterator, iterator) (erase half the container)/8192 135884 ns 133228 ns ``` Fixes #154650
2025-09-02 22:15:29 -08:00
++__first;
}
auto __max_node = std::__static_fancy_pointer_cast<__node_pointer>(
std::__tree_max(std::__static_fancy_pointer_cast<__node_base_pointer>(__root())));
[libc++] Optimize {map,set}::insert(InputIterator, InputIterator) (#154703) ``` ---------------------------------------------------------------------------------------------------------------------------- Benchmark old new ---------------------------------------------------------------------------------------------------------------------------- std::map<int, int>::ctor(iterator, iterator) (unsorted sequence)/0 14.2 ns 14.8 ns std::map<int, int>::ctor(iterator, iterator) (unsorted sequence)/32 519 ns 404 ns std::map<int, int>::ctor(iterator, iterator) (unsorted sequence)/1024 52460 ns 36242 ns std::map<int, int>::ctor(iterator, iterator) (unsorted sequence)/8192 724222 ns 706496 ns std::map<int, int>::ctor(iterator, iterator) (sorted sequence)/0 14.2 ns 14.7 ns std::map<int, int>::ctor(iterator, iterator) (sorted sequence)/32 429 ns 349 ns std::map<int, int>::ctor(iterator, iterator) (sorted sequence)/1024 23601 ns 14734 ns std::map<int, int>::ctor(iterator, iterator) (sorted sequence)/8192 267753 ns 112155 ns std::map<int, int>::insert(iterator, iterator) (all new keys)/0 434 ns 448 ns std::map<int, int>::insert(iterator, iterator) (all new keys)/32 950 ns 963 ns std::map<int, int>::insert(iterator, iterator) (all new keys)/1024 27205 ns 25344 ns std::map<int, int>::insert(iterator, iterator) (all new keys)/8192 294248 ns 280713 ns std::map<int, int>::insert(iterator, iterator) (half new keys)/0 435 ns 449 ns std::map<int, int>::insert(iterator, iterator) (half new keys)/32 771 ns 706 ns std::map<int, int>::insert(iterator, iterator) (half new keys)/1024 30841 ns 17495 ns std::map<int, int>::insert(iterator, iterator) (half new keys)/8192 468807 ns 285847 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from same type)/0 449 ns 453 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from same type)/32 1021 ns 932 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from same type)/1024 29796 ns 19518 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from same type)/8192 345688 ns 153966 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from zip_view)/0 449 ns 450 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from zip_view)/32 1026 ns 807 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from zip_view)/1024 31632 ns 15573 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from zip_view)/8192 303024 ns 128946 ns std::map<int, int>::erase(iterator, iterator) (erase half the container)/0 447 ns 452 ns std::map<int, int>::erase(iterator, iterator) (erase half the container)/32 687 ns 710 ns std::map<int, int>::erase(iterator, iterator) (erase half the container)/1024 8604 ns 8581 ns std::map<int, int>::erase(iterator, iterator) (erase half the container)/8192 65693 ns 67406 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/0 15.0 ns 15.0 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/32 2781 ns 1845 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/1024 187999 ns 182103 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/8192 2937242 ns 2934912 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/0 15.0 ns 15.2 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/32 1326 ns 2462 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/1024 81778 ns 72193 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/8192 1177292 ns 669152 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/0 439 ns 454 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/32 2483 ns 2465 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/1024 187614 ns 188072 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/8192 1654675 ns 1706603 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/0 437 ns 452 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/32 1836 ns 1820 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/1024 114885 ns 121865 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/8192 1151960 ns 1197318 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/0 438 ns 455 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/32 1599 ns 1614 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/1024 95935 ns 82159 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/8192 776480 ns 941043 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/0 435 ns 462 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/32 1723 ns 1550 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/1024 107096 ns 92850 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/8192 893976 ns 775046 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/0 436 ns 453 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/32 775 ns 824 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/1024 20241 ns 20454 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/8192 139038 ns 138032 ns std::set<int>::ctor(iterator, iterator) (unsorted sequence)/0 14.8 ns 14.7 ns std::set<int>::ctor(iterator, iterator) (unsorted sequence)/32 468 ns 426 ns std::set<int>::ctor(iterator, iterator) (unsorted sequence)/1024 54289 ns 39028 ns std::set<int>::ctor(iterator, iterator) (unsorted sequence)/8192 738438 ns 695720 ns std::set<int>::ctor(iterator, iterator) (sorted sequence)/0 14.7 ns 14.6 ns std::set<int>::ctor(iterator, iterator) (sorted sequence)/32 478 ns 391 ns std::set<int>::ctor(iterator, iterator) (sorted sequence)/1024 24017 ns 13905 ns std::set<int>::ctor(iterator, iterator) (sorted sequence)/8192 267862 ns 111378 ns std::set<int>::insert(iterator, iterator) (all new keys)/0 458 ns 450 ns std::set<int>::insert(iterator, iterator) (all new keys)/32 1066 ns 956 ns std::set<int>::insert(iterator, iterator) (all new keys)/1024 29190 ns 25212 ns std::set<int>::insert(iterator, iterator) (all new keys)/8192 320441 ns 279602 ns std::set<int>::insert(iterator, iterator) (half new keys)/0 454 ns 453 ns std::set<int>::insert(iterator, iterator) (half new keys)/32 816 ns 709 ns std::set<int>::insert(iterator, iterator) (half new keys)/1024 32072 ns 17074 ns std::set<int>::insert(iterator, iterator) (half new keys)/8192 403386 ns 286202 ns std::set<int>::erase(iterator, iterator) (erase half the container)/0 451 ns 452 ns std::set<int>::erase(iterator, iterator) (erase half the container)/32 710 ns 703 ns std::set<int>::erase(iterator, iterator) (erase half the container)/1024 8261 ns 8499 ns std::set<int>::erase(iterator, iterator) (erase half the container)/8192 64466 ns 67343 ns std::set<std::string>::ctor(iterator, iterator) (unsorted sequence)/0 15.2 ns 15.0 ns std::set<std::string>::ctor(iterator, iterator) (unsorted sequence)/32 3069 ns 3005 ns std::set<std::string>::ctor(iterator, iterator) (unsorted sequence)/1024 189552 ns 180933 ns std::set<std::string>::ctor(iterator, iterator) (unsorted sequence)/8192 2887579 ns 2691678 ns std::set<std::string>::ctor(iterator, iterator) (sorted sequence)/0 15.1 ns 14.9 ns std::set<std::string>::ctor(iterator, iterator) (sorted sequence)/32 2611 ns 2514 ns std::set<std::string>::ctor(iterator, iterator) (sorted sequence)/1024 91581 ns 78727 ns std::set<std::string>::ctor(iterator, iterator) (sorted sequence)/8192 1192640 ns 1158959 ns std::set<std::string>::insert(iterator, iterator) (all new keys)/0 452 ns 457 ns std::set<std::string>::insert(iterator, iterator) (all new keys)/32 2530 ns 2544 ns std::set<std::string>::insert(iterator, iterator) (all new keys)/1024 195352 ns 179614 ns std::set<std::string>::insert(iterator, iterator) (all new keys)/8192 1737890 ns 1749615 ns std::set<std::string>::insert(iterator, iterator) (half new keys)/0 451 ns 454 ns std::set<std::string>::insert(iterator, iterator) (half new keys)/32 1949 ns 1766 ns std::set<std::string>::insert(iterator, iterator) (half new keys)/1024 128853 ns 109467 ns std::set<std::string>::insert(iterator, iterator) (half new keys)/8192 1233077 ns 1177289 ns std::set<std::string>::erase(iterator, iterator) (erase half the container)/0 450 ns 451 ns std::set<std::string>::erase(iterator, iterator) (erase half the container)/32 809 ns 812 ns std::set<std::string>::erase(iterator, iterator) (erase half the container)/1024 21736 ns 21922 ns std::set<std::string>::erase(iterator, iterator) (erase half the container)/8192 135884 ns 133228 ns ``` Fixes #154650
2025-09-02 22:15:29 -08:00
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(std::__static_fancy_pointer_cast<__end_node_pointer>(__max_node),
[libc++] Optimize {map,set}::insert(InputIterator, InputIterator) (#154703) ``` ---------------------------------------------------------------------------------------------------------------------------- Benchmark old new ---------------------------------------------------------------------------------------------------------------------------- std::map<int, int>::ctor(iterator, iterator) (unsorted sequence)/0 14.2 ns 14.8 ns std::map<int, int>::ctor(iterator, iterator) (unsorted sequence)/32 519 ns 404 ns std::map<int, int>::ctor(iterator, iterator) (unsorted sequence)/1024 52460 ns 36242 ns std::map<int, int>::ctor(iterator, iterator) (unsorted sequence)/8192 724222 ns 706496 ns std::map<int, int>::ctor(iterator, iterator) (sorted sequence)/0 14.2 ns 14.7 ns std::map<int, int>::ctor(iterator, iterator) (sorted sequence)/32 429 ns 349 ns std::map<int, int>::ctor(iterator, iterator) (sorted sequence)/1024 23601 ns 14734 ns std::map<int, int>::ctor(iterator, iterator) (sorted sequence)/8192 267753 ns 112155 ns std::map<int, int>::insert(iterator, iterator) (all new keys)/0 434 ns 448 ns std::map<int, int>::insert(iterator, iterator) (all new keys)/32 950 ns 963 ns std::map<int, int>::insert(iterator, iterator) (all new keys)/1024 27205 ns 25344 ns std::map<int, int>::insert(iterator, iterator) (all new keys)/8192 294248 ns 280713 ns std::map<int, int>::insert(iterator, iterator) (half new keys)/0 435 ns 449 ns std::map<int, int>::insert(iterator, iterator) (half new keys)/32 771 ns 706 ns std::map<int, int>::insert(iterator, iterator) (half new keys)/1024 30841 ns 17495 ns std::map<int, int>::insert(iterator, iterator) (half new keys)/8192 468807 ns 285847 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from same type)/0 449 ns 453 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from same type)/32 1021 ns 932 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from same type)/1024 29796 ns 19518 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from same type)/8192 345688 ns 153966 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from zip_view)/0 449 ns 450 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from zip_view)/32 1026 ns 807 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from zip_view)/1024 31632 ns 15573 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from zip_view)/8192 303024 ns 128946 ns std::map<int, int>::erase(iterator, iterator) (erase half the container)/0 447 ns 452 ns std::map<int, int>::erase(iterator, iterator) (erase half the container)/32 687 ns 710 ns std::map<int, int>::erase(iterator, iterator) (erase half the container)/1024 8604 ns 8581 ns std::map<int, int>::erase(iterator, iterator) (erase half the container)/8192 65693 ns 67406 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/0 15.0 ns 15.0 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/32 2781 ns 1845 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/1024 187999 ns 182103 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/8192 2937242 ns 2934912 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/0 15.0 ns 15.2 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/32 1326 ns 2462 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/1024 81778 ns 72193 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/8192 1177292 ns 669152 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/0 439 ns 454 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/32 2483 ns 2465 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/1024 187614 ns 188072 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/8192 1654675 ns 1706603 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/0 437 ns 452 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/32 1836 ns 1820 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/1024 114885 ns 121865 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/8192 1151960 ns 1197318 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/0 438 ns 455 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/32 1599 ns 1614 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/1024 95935 ns 82159 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/8192 776480 ns 941043 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/0 435 ns 462 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/32 1723 ns 1550 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/1024 107096 ns 92850 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/8192 893976 ns 775046 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/0 436 ns 453 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/32 775 ns 824 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/1024 20241 ns 20454 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/8192 139038 ns 138032 ns std::set<int>::ctor(iterator, iterator) (unsorted sequence)/0 14.8 ns 14.7 ns std::set<int>::ctor(iterator, iterator) (unsorted sequence)/32 468 ns 426 ns std::set<int>::ctor(iterator, iterator) (unsorted sequence)/1024 54289 ns 39028 ns std::set<int>::ctor(iterator, iterator) (unsorted sequence)/8192 738438 ns 695720 ns std::set<int>::ctor(iterator, iterator) (sorted sequence)/0 14.7 ns 14.6 ns std::set<int>::ctor(iterator, iterator) (sorted sequence)/32 478 ns 391 ns std::set<int>::ctor(iterator, iterator) (sorted sequence)/1024 24017 ns 13905 ns std::set<int>::ctor(iterator, iterator) (sorted sequence)/8192 267862 ns 111378 ns std::set<int>::insert(iterator, iterator) (all new keys)/0 458 ns 450 ns std::set<int>::insert(iterator, iterator) (all new keys)/32 1066 ns 956 ns std::set<int>::insert(iterator, iterator) (all new keys)/1024 29190 ns 25212 ns std::set<int>::insert(iterator, iterator) (all new keys)/8192 320441 ns 279602 ns std::set<int>::insert(iterator, iterator) (half new keys)/0 454 ns 453 ns std::set<int>::insert(iterator, iterator) (half new keys)/32 816 ns 709 ns std::set<int>::insert(iterator, iterator) (half new keys)/1024 32072 ns 17074 ns std::set<int>::insert(iterator, iterator) (half new keys)/8192 403386 ns 286202 ns std::set<int>::erase(iterator, iterator) (erase half the container)/0 451 ns 452 ns std::set<int>::erase(iterator, iterator) (erase half the container)/32 710 ns 703 ns std::set<int>::erase(iterator, iterator) (erase half the container)/1024 8261 ns 8499 ns std::set<int>::erase(iterator, iterator) (erase half the container)/8192 64466 ns 67343 ns std::set<std::string>::ctor(iterator, iterator) (unsorted sequence)/0 15.2 ns 15.0 ns std::set<std::string>::ctor(iterator, iterator) (unsorted sequence)/32 3069 ns 3005 ns std::set<std::string>::ctor(iterator, iterator) (unsorted sequence)/1024 189552 ns 180933 ns std::set<std::string>::ctor(iterator, iterator) (unsorted sequence)/8192 2887579 ns 2691678 ns std::set<std::string>::ctor(iterator, iterator) (sorted sequence)/0 15.1 ns 14.9 ns std::set<std::string>::ctor(iterator, iterator) (sorted sequence)/32 2611 ns 2514 ns std::set<std::string>::ctor(iterator, iterator) (sorted sequence)/1024 91581 ns 78727 ns std::set<std::string>::ctor(iterator, iterator) (sorted sequence)/8192 1192640 ns 1158959 ns std::set<std::string>::insert(iterator, iterator) (all new keys)/0 452 ns 457 ns std::set<std::string>::insert(iterator, iterator) (all new keys)/32 2530 ns 2544 ns std::set<std::string>::insert(iterator, iterator) (all new keys)/1024 195352 ns 179614 ns std::set<std::string>::insert(iterator, iterator) (all new keys)/8192 1737890 ns 1749615 ns std::set<std::string>::insert(iterator, iterator) (half new keys)/0 451 ns 454 ns std::set<std::string>::insert(iterator, iterator) (half new keys)/32 1949 ns 1766 ns std::set<std::string>::insert(iterator, iterator) (half new keys)/1024 128853 ns 109467 ns std::set<std::string>::insert(iterator, iterator) (half new keys)/8192 1233077 ns 1177289 ns std::set<std::string>::erase(iterator, iterator) (erase half the container)/0 450 ns 451 ns std::set<std::string>::erase(iterator, iterator) (erase half the container)/32 809 ns 812 ns std::set<std::string>::erase(iterator, iterator) (erase half the container)/1024 21736 ns 21922 ns std::set<std::string>::erase(iterator, iterator) (erase half the container)/8192 135884 ns 133228 ns ``` Fixes #154650
2025-09-02 22:15:29 -08:00
__max_node->__right_,
std::__static_fancy_pointer_cast<__node_base_pointer>(__nd.get()));
[libc++] Optimize {map,set}::insert(InputIterator, InputIterator) (#154703) ``` ---------------------------------------------------------------------------------------------------------------------------- Benchmark old new ---------------------------------------------------------------------------------------------------------------------------- std::map<int, int>::ctor(iterator, iterator) (unsorted sequence)/0 14.2 ns 14.8 ns std::map<int, int>::ctor(iterator, iterator) (unsorted sequence)/32 519 ns 404 ns std::map<int, int>::ctor(iterator, iterator) (unsorted sequence)/1024 52460 ns 36242 ns std::map<int, int>::ctor(iterator, iterator) (unsorted sequence)/8192 724222 ns 706496 ns std::map<int, int>::ctor(iterator, iterator) (sorted sequence)/0 14.2 ns 14.7 ns std::map<int, int>::ctor(iterator, iterator) (sorted sequence)/32 429 ns 349 ns std::map<int, int>::ctor(iterator, iterator) (sorted sequence)/1024 23601 ns 14734 ns std::map<int, int>::ctor(iterator, iterator) (sorted sequence)/8192 267753 ns 112155 ns std::map<int, int>::insert(iterator, iterator) (all new keys)/0 434 ns 448 ns std::map<int, int>::insert(iterator, iterator) (all new keys)/32 950 ns 963 ns std::map<int, int>::insert(iterator, iterator) (all new keys)/1024 27205 ns 25344 ns std::map<int, int>::insert(iterator, iterator) (all new keys)/8192 294248 ns 280713 ns std::map<int, int>::insert(iterator, iterator) (half new keys)/0 435 ns 449 ns std::map<int, int>::insert(iterator, iterator) (half new keys)/32 771 ns 706 ns std::map<int, int>::insert(iterator, iterator) (half new keys)/1024 30841 ns 17495 ns std::map<int, int>::insert(iterator, iterator) (half new keys)/8192 468807 ns 285847 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from same type)/0 449 ns 453 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from same type)/32 1021 ns 932 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from same type)/1024 29796 ns 19518 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from same type)/8192 345688 ns 153966 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from zip_view)/0 449 ns 450 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from zip_view)/32 1026 ns 807 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from zip_view)/1024 31632 ns 15573 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from zip_view)/8192 303024 ns 128946 ns std::map<int, int>::erase(iterator, iterator) (erase half the container)/0 447 ns 452 ns std::map<int, int>::erase(iterator, iterator) (erase half the container)/32 687 ns 710 ns std::map<int, int>::erase(iterator, iterator) (erase half the container)/1024 8604 ns 8581 ns std::map<int, int>::erase(iterator, iterator) (erase half the container)/8192 65693 ns 67406 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/0 15.0 ns 15.0 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/32 2781 ns 1845 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/1024 187999 ns 182103 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/8192 2937242 ns 2934912 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/0 15.0 ns 15.2 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/32 1326 ns 2462 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/1024 81778 ns 72193 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/8192 1177292 ns 669152 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/0 439 ns 454 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/32 2483 ns 2465 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/1024 187614 ns 188072 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/8192 1654675 ns 1706603 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/0 437 ns 452 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/32 1836 ns 1820 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/1024 114885 ns 121865 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/8192 1151960 ns 1197318 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/0 438 ns 455 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/32 1599 ns 1614 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/1024 95935 ns 82159 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/8192 776480 ns 941043 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/0 435 ns 462 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/32 1723 ns 1550 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/1024 107096 ns 92850 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/8192 893976 ns 775046 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/0 436 ns 453 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/32 775 ns 824 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/1024 20241 ns 20454 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/8192 139038 ns 138032 ns std::set<int>::ctor(iterator, iterator) (unsorted sequence)/0 14.8 ns 14.7 ns std::set<int>::ctor(iterator, iterator) (unsorted sequence)/32 468 ns 426 ns std::set<int>::ctor(iterator, iterator) (unsorted sequence)/1024 54289 ns 39028 ns std::set<int>::ctor(iterator, iterator) (unsorted sequence)/8192 738438 ns 695720 ns std::set<int>::ctor(iterator, iterator) (sorted sequence)/0 14.7 ns 14.6 ns std::set<int>::ctor(iterator, iterator) (sorted sequence)/32 478 ns 391 ns std::set<int>::ctor(iterator, iterator) (sorted sequence)/1024 24017 ns 13905 ns std::set<int>::ctor(iterator, iterator) (sorted sequence)/8192 267862 ns 111378 ns std::set<int>::insert(iterator, iterator) (all new keys)/0 458 ns 450 ns std::set<int>::insert(iterator, iterator) (all new keys)/32 1066 ns 956 ns std::set<int>::insert(iterator, iterator) (all new keys)/1024 29190 ns 25212 ns std::set<int>::insert(iterator, iterator) (all new keys)/8192 320441 ns 279602 ns std::set<int>::insert(iterator, iterator) (half new keys)/0 454 ns 453 ns std::set<int>::insert(iterator, iterator) (half new keys)/32 816 ns 709 ns std::set<int>::insert(iterator, iterator) (half new keys)/1024 32072 ns 17074 ns std::set<int>::insert(iterator, iterator) (half new keys)/8192 403386 ns 286202 ns std::set<int>::erase(iterator, iterator) (erase half the container)/0 451 ns 452 ns std::set<int>::erase(iterator, iterator) (erase half the container)/32 710 ns 703 ns std::set<int>::erase(iterator, iterator) (erase half the container)/1024 8261 ns 8499 ns std::set<int>::erase(iterator, iterator) (erase half the container)/8192 64466 ns 67343 ns std::set<std::string>::ctor(iterator, iterator) (unsorted sequence)/0 15.2 ns 15.0 ns std::set<std::string>::ctor(iterator, iterator) (unsorted sequence)/32 3069 ns 3005 ns std::set<std::string>::ctor(iterator, iterator) (unsorted sequence)/1024 189552 ns 180933 ns std::set<std::string>::ctor(iterator, iterator) (unsorted sequence)/8192 2887579 ns 2691678 ns std::set<std::string>::ctor(iterator, iterator) (sorted sequence)/0 15.1 ns 14.9 ns std::set<std::string>::ctor(iterator, iterator) (sorted sequence)/32 2611 ns 2514 ns std::set<std::string>::ctor(iterator, iterator) (sorted sequence)/1024 91581 ns 78727 ns std::set<std::string>::ctor(iterator, iterator) (sorted sequence)/8192 1192640 ns 1158959 ns std::set<std::string>::insert(iterator, iterator) (all new keys)/0 452 ns 457 ns std::set<std::string>::insert(iterator, iterator) (all new keys)/32 2530 ns 2544 ns std::set<std::string>::insert(iterator, iterator) (all new keys)/1024 195352 ns 179614 ns std::set<std::string>::insert(iterator, iterator) (all new keys)/8192 1737890 ns 1749615 ns std::set<std::string>::insert(iterator, iterator) (half new keys)/0 451 ns 454 ns std::set<std::string>::insert(iterator, iterator) (half new keys)/32 1949 ns 1766 ns std::set<std::string>::insert(iterator, iterator) (half new keys)/1024 128853 ns 109467 ns std::set<std::string>::insert(iterator, iterator) (half new keys)/8192 1233077 ns 1177289 ns std::set<std::string>::erase(iterator, iterator) (erase half the container)/0 450 ns 451 ns std::set<std::string>::erase(iterator, iterator) (erase half the container)/32 809 ns 812 ns std::set<std::string>::erase(iterator, iterator) (erase half the container)/1024 21736 ns 21922 ns std::set<std::string>::erase(iterator, iterator) (erase half the container)/8192 135884 ns 133228 ns ``` Fixes #154650
2025-09-02 22:15:29 -08:00
__max_node = __nd.release();
} else {
auto [__parent, __child] = __find_equal(__key);
[libc++] Optimize {map,set}::insert(InputIterator, InputIterator) (#154703) ``` ---------------------------------------------------------------------------------------------------------------------------- Benchmark old new ---------------------------------------------------------------------------------------------------------------------------- std::map<int, int>::ctor(iterator, iterator) (unsorted sequence)/0 14.2 ns 14.8 ns std::map<int, int>::ctor(iterator, iterator) (unsorted sequence)/32 519 ns 404 ns std::map<int, int>::ctor(iterator, iterator) (unsorted sequence)/1024 52460 ns 36242 ns std::map<int, int>::ctor(iterator, iterator) (unsorted sequence)/8192 724222 ns 706496 ns std::map<int, int>::ctor(iterator, iterator) (sorted sequence)/0 14.2 ns 14.7 ns std::map<int, int>::ctor(iterator, iterator) (sorted sequence)/32 429 ns 349 ns std::map<int, int>::ctor(iterator, iterator) (sorted sequence)/1024 23601 ns 14734 ns std::map<int, int>::ctor(iterator, iterator) (sorted sequence)/8192 267753 ns 112155 ns std::map<int, int>::insert(iterator, iterator) (all new keys)/0 434 ns 448 ns std::map<int, int>::insert(iterator, iterator) (all new keys)/32 950 ns 963 ns std::map<int, int>::insert(iterator, iterator) (all new keys)/1024 27205 ns 25344 ns std::map<int, int>::insert(iterator, iterator) (all new keys)/8192 294248 ns 280713 ns std::map<int, int>::insert(iterator, iterator) (half new keys)/0 435 ns 449 ns std::map<int, int>::insert(iterator, iterator) (half new keys)/32 771 ns 706 ns std::map<int, int>::insert(iterator, iterator) (half new keys)/1024 30841 ns 17495 ns std::map<int, int>::insert(iterator, iterator) (half new keys)/8192 468807 ns 285847 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from same type)/0 449 ns 453 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from same type)/32 1021 ns 932 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from same type)/1024 29796 ns 19518 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from same type)/8192 345688 ns 153966 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from zip_view)/0 449 ns 450 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from zip_view)/32 1026 ns 807 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from zip_view)/1024 31632 ns 15573 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from zip_view)/8192 303024 ns 128946 ns std::map<int, int>::erase(iterator, iterator) (erase half the container)/0 447 ns 452 ns std::map<int, int>::erase(iterator, iterator) (erase half the container)/32 687 ns 710 ns std::map<int, int>::erase(iterator, iterator) (erase half the container)/1024 8604 ns 8581 ns std::map<int, int>::erase(iterator, iterator) (erase half the container)/8192 65693 ns 67406 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/0 15.0 ns 15.0 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/32 2781 ns 1845 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/1024 187999 ns 182103 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/8192 2937242 ns 2934912 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/0 15.0 ns 15.2 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/32 1326 ns 2462 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/1024 81778 ns 72193 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/8192 1177292 ns 669152 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/0 439 ns 454 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/32 2483 ns 2465 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/1024 187614 ns 188072 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/8192 1654675 ns 1706603 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/0 437 ns 452 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/32 1836 ns 1820 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/1024 114885 ns 121865 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/8192 1151960 ns 1197318 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/0 438 ns 455 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/32 1599 ns 1614 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/1024 95935 ns 82159 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/8192 776480 ns 941043 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/0 435 ns 462 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/32 1723 ns 1550 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/1024 107096 ns 92850 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/8192 893976 ns 775046 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/0 436 ns 453 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/32 775 ns 824 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/1024 20241 ns 20454 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/8192 139038 ns 138032 ns std::set<int>::ctor(iterator, iterator) (unsorted sequence)/0 14.8 ns 14.7 ns std::set<int>::ctor(iterator, iterator) (unsorted sequence)/32 468 ns 426 ns std::set<int>::ctor(iterator, iterator) (unsorted sequence)/1024 54289 ns 39028 ns std::set<int>::ctor(iterator, iterator) (unsorted sequence)/8192 738438 ns 695720 ns std::set<int>::ctor(iterator, iterator) (sorted sequence)/0 14.7 ns 14.6 ns std::set<int>::ctor(iterator, iterator) (sorted sequence)/32 478 ns 391 ns std::set<int>::ctor(iterator, iterator) (sorted sequence)/1024 24017 ns 13905 ns std::set<int>::ctor(iterator, iterator) (sorted sequence)/8192 267862 ns 111378 ns std::set<int>::insert(iterator, iterator) (all new keys)/0 458 ns 450 ns std::set<int>::insert(iterator, iterator) (all new keys)/32 1066 ns 956 ns std::set<int>::insert(iterator, iterator) (all new keys)/1024 29190 ns 25212 ns std::set<int>::insert(iterator, iterator) (all new keys)/8192 320441 ns 279602 ns std::set<int>::insert(iterator, iterator) (half new keys)/0 454 ns 453 ns std::set<int>::insert(iterator, iterator) (half new keys)/32 816 ns 709 ns std::set<int>::insert(iterator, iterator) (half new keys)/1024 32072 ns 17074 ns std::set<int>::insert(iterator, iterator) (half new keys)/8192 403386 ns 286202 ns std::set<int>::erase(iterator, iterator) (erase half the container)/0 451 ns 452 ns std::set<int>::erase(iterator, iterator) (erase half the container)/32 710 ns 703 ns std::set<int>::erase(iterator, iterator) (erase half the container)/1024 8261 ns 8499 ns std::set<int>::erase(iterator, iterator) (erase half the container)/8192 64466 ns 67343 ns std::set<std::string>::ctor(iterator, iterator) (unsorted sequence)/0 15.2 ns 15.0 ns std::set<std::string>::ctor(iterator, iterator) (unsorted sequence)/32 3069 ns 3005 ns std::set<std::string>::ctor(iterator, iterator) (unsorted sequence)/1024 189552 ns 180933 ns std::set<std::string>::ctor(iterator, iterator) (unsorted sequence)/8192 2887579 ns 2691678 ns std::set<std::string>::ctor(iterator, iterator) (sorted sequence)/0 15.1 ns 14.9 ns std::set<std::string>::ctor(iterator, iterator) (sorted sequence)/32 2611 ns 2514 ns std::set<std::string>::ctor(iterator, iterator) (sorted sequence)/1024 91581 ns 78727 ns std::set<std::string>::ctor(iterator, iterator) (sorted sequence)/8192 1192640 ns 1158959 ns std::set<std::string>::insert(iterator, iterator) (all new keys)/0 452 ns 457 ns std::set<std::string>::insert(iterator, iterator) (all new keys)/32 2530 ns 2544 ns std::set<std::string>::insert(iterator, iterator) (all new keys)/1024 195352 ns 179614 ns std::set<std::string>::insert(iterator, iterator) (all new keys)/8192 1737890 ns 1749615 ns std::set<std::string>::insert(iterator, iterator) (half new keys)/0 451 ns 454 ns std::set<std::string>::insert(iterator, iterator) (half new keys)/32 1949 ns 1766 ns std::set<std::string>::insert(iterator, iterator) (half new keys)/1024 128853 ns 109467 ns std::set<std::string>::insert(iterator, iterator) (half new keys)/8192 1233077 ns 1177289 ns std::set<std::string>::erase(iterator, iterator) (erase half the container)/0 450 ns 451 ns std::set<std::string>::erase(iterator, iterator) (erase half the container)/32 809 ns 812 ns std::set<std::string>::erase(iterator, iterator) (erase half the container)/1024 21736 ns 21922 ns std::set<std::string>::erase(iterator, iterator) (erase half the container)/8192 135884 ns 133228 ns ``` Fixes #154650
2025-09-02 22:15:29 -08:00
if (__child == nullptr) {
__node_holder __nd = __construct_node(std::forward<__reference>(__val));
__insert_node_at(
__parent, __child, std::__static_fancy_pointer_cast<__node_base_pointer>(__nd.release()));
[libc++] Optimize {map,set}::insert(InputIterator, InputIterator) (#154703) ``` ---------------------------------------------------------------------------------------------------------------------------- Benchmark old new ---------------------------------------------------------------------------------------------------------------------------- std::map<int, int>::ctor(iterator, iterator) (unsorted sequence)/0 14.2 ns 14.8 ns std::map<int, int>::ctor(iterator, iterator) (unsorted sequence)/32 519 ns 404 ns std::map<int, int>::ctor(iterator, iterator) (unsorted sequence)/1024 52460 ns 36242 ns std::map<int, int>::ctor(iterator, iterator) (unsorted sequence)/8192 724222 ns 706496 ns std::map<int, int>::ctor(iterator, iterator) (sorted sequence)/0 14.2 ns 14.7 ns std::map<int, int>::ctor(iterator, iterator) (sorted sequence)/32 429 ns 349 ns std::map<int, int>::ctor(iterator, iterator) (sorted sequence)/1024 23601 ns 14734 ns std::map<int, int>::ctor(iterator, iterator) (sorted sequence)/8192 267753 ns 112155 ns std::map<int, int>::insert(iterator, iterator) (all new keys)/0 434 ns 448 ns std::map<int, int>::insert(iterator, iterator) (all new keys)/32 950 ns 963 ns std::map<int, int>::insert(iterator, iterator) (all new keys)/1024 27205 ns 25344 ns std::map<int, int>::insert(iterator, iterator) (all new keys)/8192 294248 ns 280713 ns std::map<int, int>::insert(iterator, iterator) (half new keys)/0 435 ns 449 ns std::map<int, int>::insert(iterator, iterator) (half new keys)/32 771 ns 706 ns std::map<int, int>::insert(iterator, iterator) (half new keys)/1024 30841 ns 17495 ns std::map<int, int>::insert(iterator, iterator) (half new keys)/8192 468807 ns 285847 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from same type)/0 449 ns 453 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from same type)/32 1021 ns 932 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from same type)/1024 29796 ns 19518 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from same type)/8192 345688 ns 153966 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from zip_view)/0 449 ns 450 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from zip_view)/32 1026 ns 807 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from zip_view)/1024 31632 ns 15573 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from zip_view)/8192 303024 ns 128946 ns std::map<int, int>::erase(iterator, iterator) (erase half the container)/0 447 ns 452 ns std::map<int, int>::erase(iterator, iterator) (erase half the container)/32 687 ns 710 ns std::map<int, int>::erase(iterator, iterator) (erase half the container)/1024 8604 ns 8581 ns std::map<int, int>::erase(iterator, iterator) (erase half the container)/8192 65693 ns 67406 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/0 15.0 ns 15.0 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/32 2781 ns 1845 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/1024 187999 ns 182103 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/8192 2937242 ns 2934912 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/0 15.0 ns 15.2 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/32 1326 ns 2462 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/1024 81778 ns 72193 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/8192 1177292 ns 669152 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/0 439 ns 454 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/32 2483 ns 2465 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/1024 187614 ns 188072 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/8192 1654675 ns 1706603 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/0 437 ns 452 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/32 1836 ns 1820 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/1024 114885 ns 121865 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/8192 1151960 ns 1197318 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/0 438 ns 455 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/32 1599 ns 1614 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/1024 95935 ns 82159 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/8192 776480 ns 941043 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/0 435 ns 462 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/32 1723 ns 1550 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/1024 107096 ns 92850 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/8192 893976 ns 775046 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/0 436 ns 453 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/32 775 ns 824 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/1024 20241 ns 20454 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/8192 139038 ns 138032 ns std::set<int>::ctor(iterator, iterator) (unsorted sequence)/0 14.8 ns 14.7 ns std::set<int>::ctor(iterator, iterator) (unsorted sequence)/32 468 ns 426 ns std::set<int>::ctor(iterator, iterator) (unsorted sequence)/1024 54289 ns 39028 ns std::set<int>::ctor(iterator, iterator) (unsorted sequence)/8192 738438 ns 695720 ns std::set<int>::ctor(iterator, iterator) (sorted sequence)/0 14.7 ns 14.6 ns std::set<int>::ctor(iterator, iterator) (sorted sequence)/32 478 ns 391 ns std::set<int>::ctor(iterator, iterator) (sorted sequence)/1024 24017 ns 13905 ns std::set<int>::ctor(iterator, iterator) (sorted sequence)/8192 267862 ns 111378 ns std::set<int>::insert(iterator, iterator) (all new keys)/0 458 ns 450 ns std::set<int>::insert(iterator, iterator) (all new keys)/32 1066 ns 956 ns std::set<int>::insert(iterator, iterator) (all new keys)/1024 29190 ns 25212 ns std::set<int>::insert(iterator, iterator) (all new keys)/8192 320441 ns 279602 ns std::set<int>::insert(iterator, iterator) (half new keys)/0 454 ns 453 ns std::set<int>::insert(iterator, iterator) (half new keys)/32 816 ns 709 ns std::set<int>::insert(iterator, iterator) (half new keys)/1024 32072 ns 17074 ns std::set<int>::insert(iterator, iterator) (half new keys)/8192 403386 ns 286202 ns std::set<int>::erase(iterator, iterator) (erase half the container)/0 451 ns 452 ns std::set<int>::erase(iterator, iterator) (erase half the container)/32 710 ns 703 ns std::set<int>::erase(iterator, iterator) (erase half the container)/1024 8261 ns 8499 ns std::set<int>::erase(iterator, iterator) (erase half the container)/8192 64466 ns 67343 ns std::set<std::string>::ctor(iterator, iterator) (unsorted sequence)/0 15.2 ns 15.0 ns std::set<std::string>::ctor(iterator, iterator) (unsorted sequence)/32 3069 ns 3005 ns std::set<std::string>::ctor(iterator, iterator) (unsorted sequence)/1024 189552 ns 180933 ns std::set<std::string>::ctor(iterator, iterator) (unsorted sequence)/8192 2887579 ns 2691678 ns std::set<std::string>::ctor(iterator, iterator) (sorted sequence)/0 15.1 ns 14.9 ns std::set<std::string>::ctor(iterator, iterator) (sorted sequence)/32 2611 ns 2514 ns std::set<std::string>::ctor(iterator, iterator) (sorted sequence)/1024 91581 ns 78727 ns std::set<std::string>::ctor(iterator, iterator) (sorted sequence)/8192 1192640 ns 1158959 ns std::set<std::string>::insert(iterator, iterator) (all new keys)/0 452 ns 457 ns std::set<std::string>::insert(iterator, iterator) (all new keys)/32 2530 ns 2544 ns std::set<std::string>::insert(iterator, iterator) (all new keys)/1024 195352 ns 179614 ns std::set<std::string>::insert(iterator, iterator) (all new keys)/8192 1737890 ns 1749615 ns std::set<std::string>::insert(iterator, iterator) (half new keys)/0 451 ns 454 ns std::set<std::string>::insert(iterator, iterator) (half new keys)/32 1949 ns 1766 ns std::set<std::string>::insert(iterator, iterator) (half new keys)/1024 128853 ns 109467 ns std::set<std::string>::insert(iterator, iterator) (half new keys)/8192 1233077 ns 1177289 ns std::set<std::string>::erase(iterator, iterator) (erase half the container)/0 450 ns 451 ns std::set<std::string>::erase(iterator, iterator) (erase half the container)/32 809 ns 812 ns std::set<std::string>::erase(iterator, iterator) (erase half the container)/1024 21736 ns 21922 ns std::set<std::string>::erase(iterator, iterator) (erase half the container)/8192 135884 ns 133228 ns ``` Fixes #154650
2025-09-02 22:15:29 -08:00
}
}
},
[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(std::__static_fancy_pointer_cast<__end_node_pointer>(__max_node),
[libc++] Optimize {map,set}::insert(InputIterator, InputIterator) (#154703) ``` ---------------------------------------------------------------------------------------------------------------------------- Benchmark old new ---------------------------------------------------------------------------------------------------------------------------- std::map<int, int>::ctor(iterator, iterator) (unsorted sequence)/0 14.2 ns 14.8 ns std::map<int, int>::ctor(iterator, iterator) (unsorted sequence)/32 519 ns 404 ns std::map<int, int>::ctor(iterator, iterator) (unsorted sequence)/1024 52460 ns 36242 ns std::map<int, int>::ctor(iterator, iterator) (unsorted sequence)/8192 724222 ns 706496 ns std::map<int, int>::ctor(iterator, iterator) (sorted sequence)/0 14.2 ns 14.7 ns std::map<int, int>::ctor(iterator, iterator) (sorted sequence)/32 429 ns 349 ns std::map<int, int>::ctor(iterator, iterator) (sorted sequence)/1024 23601 ns 14734 ns std::map<int, int>::ctor(iterator, iterator) (sorted sequence)/8192 267753 ns 112155 ns std::map<int, int>::insert(iterator, iterator) (all new keys)/0 434 ns 448 ns std::map<int, int>::insert(iterator, iterator) (all new keys)/32 950 ns 963 ns std::map<int, int>::insert(iterator, iterator) (all new keys)/1024 27205 ns 25344 ns std::map<int, int>::insert(iterator, iterator) (all new keys)/8192 294248 ns 280713 ns std::map<int, int>::insert(iterator, iterator) (half new keys)/0 435 ns 449 ns std::map<int, int>::insert(iterator, iterator) (half new keys)/32 771 ns 706 ns std::map<int, int>::insert(iterator, iterator) (half new keys)/1024 30841 ns 17495 ns std::map<int, int>::insert(iterator, iterator) (half new keys)/8192 468807 ns 285847 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from same type)/0 449 ns 453 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from same type)/32 1021 ns 932 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from same type)/1024 29796 ns 19518 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from same type)/8192 345688 ns 153966 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from zip_view)/0 449 ns 450 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from zip_view)/32 1026 ns 807 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from zip_view)/1024 31632 ns 15573 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from zip_view)/8192 303024 ns 128946 ns std::map<int, int>::erase(iterator, iterator) (erase half the container)/0 447 ns 452 ns std::map<int, int>::erase(iterator, iterator) (erase half the container)/32 687 ns 710 ns std::map<int, int>::erase(iterator, iterator) (erase half the container)/1024 8604 ns 8581 ns std::map<int, int>::erase(iterator, iterator) (erase half the container)/8192 65693 ns 67406 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/0 15.0 ns 15.0 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/32 2781 ns 1845 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/1024 187999 ns 182103 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/8192 2937242 ns 2934912 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/0 15.0 ns 15.2 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/32 1326 ns 2462 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/1024 81778 ns 72193 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/8192 1177292 ns 669152 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/0 439 ns 454 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/32 2483 ns 2465 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/1024 187614 ns 188072 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/8192 1654675 ns 1706603 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/0 437 ns 452 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/32 1836 ns 1820 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/1024 114885 ns 121865 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/8192 1151960 ns 1197318 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/0 438 ns 455 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/32 1599 ns 1614 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/1024 95935 ns 82159 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/8192 776480 ns 941043 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/0 435 ns 462 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/32 1723 ns 1550 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/1024 107096 ns 92850 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/8192 893976 ns 775046 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/0 436 ns 453 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/32 775 ns 824 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/1024 20241 ns 20454 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/8192 139038 ns 138032 ns std::set<int>::ctor(iterator, iterator) (unsorted sequence)/0 14.8 ns 14.7 ns std::set<int>::ctor(iterator, iterator) (unsorted sequence)/32 468 ns 426 ns std::set<int>::ctor(iterator, iterator) (unsorted sequence)/1024 54289 ns 39028 ns std::set<int>::ctor(iterator, iterator) (unsorted sequence)/8192 738438 ns 695720 ns std::set<int>::ctor(iterator, iterator) (sorted sequence)/0 14.7 ns 14.6 ns std::set<int>::ctor(iterator, iterator) (sorted sequence)/32 478 ns 391 ns std::set<int>::ctor(iterator, iterator) (sorted sequence)/1024 24017 ns 13905 ns std::set<int>::ctor(iterator, iterator) (sorted sequence)/8192 267862 ns 111378 ns std::set<int>::insert(iterator, iterator) (all new keys)/0 458 ns 450 ns std::set<int>::insert(iterator, iterator) (all new keys)/32 1066 ns 956 ns std::set<int>::insert(iterator, iterator) (all new keys)/1024 29190 ns 25212 ns std::set<int>::insert(iterator, iterator) (all new keys)/8192 320441 ns 279602 ns std::set<int>::insert(iterator, iterator) (half new keys)/0 454 ns 453 ns std::set<int>::insert(iterator, iterator) (half new keys)/32 816 ns 709 ns std::set<int>::insert(iterator, iterator) (half new keys)/1024 32072 ns 17074 ns std::set<int>::insert(iterator, iterator) (half new keys)/8192 403386 ns 286202 ns std::set<int>::erase(iterator, iterator) (erase half the container)/0 451 ns 452 ns std::set<int>::erase(iterator, iterator) (erase half the container)/32 710 ns 703 ns std::set<int>::erase(iterator, iterator) (erase half the container)/1024 8261 ns 8499 ns std::set<int>::erase(iterator, iterator) (erase half the container)/8192 64466 ns 67343 ns std::set<std::string>::ctor(iterator, iterator) (unsorted sequence)/0 15.2 ns 15.0 ns std::set<std::string>::ctor(iterator, iterator) (unsorted sequence)/32 3069 ns 3005 ns std::set<std::string>::ctor(iterator, iterator) (unsorted sequence)/1024 189552 ns 180933 ns std::set<std::string>::ctor(iterator, iterator) (unsorted sequence)/8192 2887579 ns 2691678 ns std::set<std::string>::ctor(iterator, iterator) (sorted sequence)/0 15.1 ns 14.9 ns std::set<std::string>::ctor(iterator, iterator) (sorted sequence)/32 2611 ns 2514 ns std::set<std::string>::ctor(iterator, iterator) (sorted sequence)/1024 91581 ns 78727 ns std::set<std::string>::ctor(iterator, iterator) (sorted sequence)/8192 1192640 ns 1158959 ns std::set<std::string>::insert(iterator, iterator) (all new keys)/0 452 ns 457 ns std::set<std::string>::insert(iterator, iterator) (all new keys)/32 2530 ns 2544 ns std::set<std::string>::insert(iterator, iterator) (all new keys)/1024 195352 ns 179614 ns std::set<std::string>::insert(iterator, iterator) (all new keys)/8192 1737890 ns 1749615 ns std::set<std::string>::insert(iterator, iterator) (half new keys)/0 451 ns 454 ns std::set<std::string>::insert(iterator, iterator) (half new keys)/32 1949 ns 1766 ns std::set<std::string>::insert(iterator, iterator) (half new keys)/1024 128853 ns 109467 ns std::set<std::string>::insert(iterator, iterator) (half new keys)/8192 1233077 ns 1177289 ns std::set<std::string>::erase(iterator, iterator) (erase half the container)/0 450 ns 451 ns std::set<std::string>::erase(iterator, iterator) (erase half the container)/32 809 ns 812 ns std::set<std::string>::erase(iterator, iterator) (erase half the container)/1024 21736 ns 21922 ns std::set<std::string>::erase(iterator, iterator) (erase half the container)/8192 135884 ns 133228 ns ``` Fixes #154650
2025-09-02 22:15:29 -08:00
__max_node->__right_,
std::__static_fancy_pointer_cast<__node_base_pointer>(__nd.get()));
[libc++] Optimize {map,set}::insert(InputIterator, InputIterator) (#154703) ``` ---------------------------------------------------------------------------------------------------------------------------- Benchmark old new ---------------------------------------------------------------------------------------------------------------------------- std::map<int, int>::ctor(iterator, iterator) (unsorted sequence)/0 14.2 ns 14.8 ns std::map<int, int>::ctor(iterator, iterator) (unsorted sequence)/32 519 ns 404 ns std::map<int, int>::ctor(iterator, iterator) (unsorted sequence)/1024 52460 ns 36242 ns std::map<int, int>::ctor(iterator, iterator) (unsorted sequence)/8192 724222 ns 706496 ns std::map<int, int>::ctor(iterator, iterator) (sorted sequence)/0 14.2 ns 14.7 ns std::map<int, int>::ctor(iterator, iterator) (sorted sequence)/32 429 ns 349 ns std::map<int, int>::ctor(iterator, iterator) (sorted sequence)/1024 23601 ns 14734 ns std::map<int, int>::ctor(iterator, iterator) (sorted sequence)/8192 267753 ns 112155 ns std::map<int, int>::insert(iterator, iterator) (all new keys)/0 434 ns 448 ns std::map<int, int>::insert(iterator, iterator) (all new keys)/32 950 ns 963 ns std::map<int, int>::insert(iterator, iterator) (all new keys)/1024 27205 ns 25344 ns std::map<int, int>::insert(iterator, iterator) (all new keys)/8192 294248 ns 280713 ns std::map<int, int>::insert(iterator, iterator) (half new keys)/0 435 ns 449 ns std::map<int, int>::insert(iterator, iterator) (half new keys)/32 771 ns 706 ns std::map<int, int>::insert(iterator, iterator) (half new keys)/1024 30841 ns 17495 ns std::map<int, int>::insert(iterator, iterator) (half new keys)/8192 468807 ns 285847 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from same type)/0 449 ns 453 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from same type)/32 1021 ns 932 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from same type)/1024 29796 ns 19518 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from same type)/8192 345688 ns 153966 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from zip_view)/0 449 ns 450 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from zip_view)/32 1026 ns 807 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from zip_view)/1024 31632 ns 15573 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from zip_view)/8192 303024 ns 128946 ns std::map<int, int>::erase(iterator, iterator) (erase half the container)/0 447 ns 452 ns std::map<int, int>::erase(iterator, iterator) (erase half the container)/32 687 ns 710 ns std::map<int, int>::erase(iterator, iterator) (erase half the container)/1024 8604 ns 8581 ns std::map<int, int>::erase(iterator, iterator) (erase half the container)/8192 65693 ns 67406 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/0 15.0 ns 15.0 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/32 2781 ns 1845 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/1024 187999 ns 182103 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/8192 2937242 ns 2934912 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/0 15.0 ns 15.2 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/32 1326 ns 2462 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/1024 81778 ns 72193 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/8192 1177292 ns 669152 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/0 439 ns 454 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/32 2483 ns 2465 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/1024 187614 ns 188072 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/8192 1654675 ns 1706603 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/0 437 ns 452 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/32 1836 ns 1820 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/1024 114885 ns 121865 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/8192 1151960 ns 1197318 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/0 438 ns 455 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/32 1599 ns 1614 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/1024 95935 ns 82159 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/8192 776480 ns 941043 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/0 435 ns 462 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/32 1723 ns 1550 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/1024 107096 ns 92850 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/8192 893976 ns 775046 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/0 436 ns 453 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/32 775 ns 824 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/1024 20241 ns 20454 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/8192 139038 ns 138032 ns std::set<int>::ctor(iterator, iterator) (unsorted sequence)/0 14.8 ns 14.7 ns std::set<int>::ctor(iterator, iterator) (unsorted sequence)/32 468 ns 426 ns std::set<int>::ctor(iterator, iterator) (unsorted sequence)/1024 54289 ns 39028 ns std::set<int>::ctor(iterator, iterator) (unsorted sequence)/8192 738438 ns 695720 ns std::set<int>::ctor(iterator, iterator) (sorted sequence)/0 14.7 ns 14.6 ns std::set<int>::ctor(iterator, iterator) (sorted sequence)/32 478 ns 391 ns std::set<int>::ctor(iterator, iterator) (sorted sequence)/1024 24017 ns 13905 ns std::set<int>::ctor(iterator, iterator) (sorted sequence)/8192 267862 ns 111378 ns std::set<int>::insert(iterator, iterator) (all new keys)/0 458 ns 450 ns std::set<int>::insert(iterator, iterator) (all new keys)/32 1066 ns 956 ns std::set<int>::insert(iterator, iterator) (all new keys)/1024 29190 ns 25212 ns std::set<int>::insert(iterator, iterator) (all new keys)/8192 320441 ns 279602 ns std::set<int>::insert(iterator, iterator) (half new keys)/0 454 ns 453 ns std::set<int>::insert(iterator, iterator) (half new keys)/32 816 ns 709 ns std::set<int>::insert(iterator, iterator) (half new keys)/1024 32072 ns 17074 ns std::set<int>::insert(iterator, iterator) (half new keys)/8192 403386 ns 286202 ns std::set<int>::erase(iterator, iterator) (erase half the container)/0 451 ns 452 ns std::set<int>::erase(iterator, iterator) (erase half the container)/32 710 ns 703 ns std::set<int>::erase(iterator, iterator) (erase half the container)/1024 8261 ns 8499 ns std::set<int>::erase(iterator, iterator) (erase half the container)/8192 64466 ns 67343 ns std::set<std::string>::ctor(iterator, iterator) (unsorted sequence)/0 15.2 ns 15.0 ns std::set<std::string>::ctor(iterator, iterator) (unsorted sequence)/32 3069 ns 3005 ns std::set<std::string>::ctor(iterator, iterator) (unsorted sequence)/1024 189552 ns 180933 ns std::set<std::string>::ctor(iterator, iterator) (unsorted sequence)/8192 2887579 ns 2691678 ns std::set<std::string>::ctor(iterator, iterator) (sorted sequence)/0 15.1 ns 14.9 ns std::set<std::string>::ctor(iterator, iterator) (sorted sequence)/32 2611 ns 2514 ns std::set<std::string>::ctor(iterator, iterator) (sorted sequence)/1024 91581 ns 78727 ns std::set<std::string>::ctor(iterator, iterator) (sorted sequence)/8192 1192640 ns 1158959 ns std::set<std::string>::insert(iterator, iterator) (all new keys)/0 452 ns 457 ns std::set<std::string>::insert(iterator, iterator) (all new keys)/32 2530 ns 2544 ns std::set<std::string>::insert(iterator, iterator) (all new keys)/1024 195352 ns 179614 ns std::set<std::string>::insert(iterator, iterator) (all new keys)/8192 1737890 ns 1749615 ns std::set<std::string>::insert(iterator, iterator) (half new keys)/0 451 ns 454 ns std::set<std::string>::insert(iterator, iterator) (half new keys)/32 1949 ns 1766 ns std::set<std::string>::insert(iterator, iterator) (half new keys)/1024 128853 ns 109467 ns std::set<std::string>::insert(iterator, iterator) (half new keys)/8192 1233077 ns 1177289 ns std::set<std::string>::erase(iterator, iterator) (erase half the container)/0 450 ns 451 ns std::set<std::string>::erase(iterator, iterator) (erase half the container)/32 809 ns 812 ns std::set<std::string>::erase(iterator, iterator) (erase half the container)/1024 21736 ns 21922 ns std::set<std::string>::erase(iterator, iterator) (erase half the container)/8192 135884 ns 133228 ns ``` Fixes #154650
2025-09-02 22:15:29 -08:00
__max_node = __nd.release();
} else {
auto [__parent, __child] = __find_equal(__nd->__get_value());
[libc++] Optimize {map,set}::insert(InputIterator, InputIterator) (#154703) ``` ---------------------------------------------------------------------------------------------------------------------------- Benchmark old new ---------------------------------------------------------------------------------------------------------------------------- std::map<int, int>::ctor(iterator, iterator) (unsorted sequence)/0 14.2 ns 14.8 ns std::map<int, int>::ctor(iterator, iterator) (unsorted sequence)/32 519 ns 404 ns std::map<int, int>::ctor(iterator, iterator) (unsorted sequence)/1024 52460 ns 36242 ns std::map<int, int>::ctor(iterator, iterator) (unsorted sequence)/8192 724222 ns 706496 ns std::map<int, int>::ctor(iterator, iterator) (sorted sequence)/0 14.2 ns 14.7 ns std::map<int, int>::ctor(iterator, iterator) (sorted sequence)/32 429 ns 349 ns std::map<int, int>::ctor(iterator, iterator) (sorted sequence)/1024 23601 ns 14734 ns std::map<int, int>::ctor(iterator, iterator) (sorted sequence)/8192 267753 ns 112155 ns std::map<int, int>::insert(iterator, iterator) (all new keys)/0 434 ns 448 ns std::map<int, int>::insert(iterator, iterator) (all new keys)/32 950 ns 963 ns std::map<int, int>::insert(iterator, iterator) (all new keys)/1024 27205 ns 25344 ns std::map<int, int>::insert(iterator, iterator) (all new keys)/8192 294248 ns 280713 ns std::map<int, int>::insert(iterator, iterator) (half new keys)/0 435 ns 449 ns std::map<int, int>::insert(iterator, iterator) (half new keys)/32 771 ns 706 ns std::map<int, int>::insert(iterator, iterator) (half new keys)/1024 30841 ns 17495 ns std::map<int, int>::insert(iterator, iterator) (half new keys)/8192 468807 ns 285847 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from same type)/0 449 ns 453 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from same type)/32 1021 ns 932 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from same type)/1024 29796 ns 19518 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from same type)/8192 345688 ns 153966 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from zip_view)/0 449 ns 450 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from zip_view)/32 1026 ns 807 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from zip_view)/1024 31632 ns 15573 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from zip_view)/8192 303024 ns 128946 ns std::map<int, int>::erase(iterator, iterator) (erase half the container)/0 447 ns 452 ns std::map<int, int>::erase(iterator, iterator) (erase half the container)/32 687 ns 710 ns std::map<int, int>::erase(iterator, iterator) (erase half the container)/1024 8604 ns 8581 ns std::map<int, int>::erase(iterator, iterator) (erase half the container)/8192 65693 ns 67406 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/0 15.0 ns 15.0 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/32 2781 ns 1845 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/1024 187999 ns 182103 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/8192 2937242 ns 2934912 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/0 15.0 ns 15.2 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/32 1326 ns 2462 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/1024 81778 ns 72193 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/8192 1177292 ns 669152 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/0 439 ns 454 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/32 2483 ns 2465 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/1024 187614 ns 188072 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/8192 1654675 ns 1706603 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/0 437 ns 452 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/32 1836 ns 1820 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/1024 114885 ns 121865 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/8192 1151960 ns 1197318 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/0 438 ns 455 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/32 1599 ns 1614 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/1024 95935 ns 82159 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/8192 776480 ns 941043 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/0 435 ns 462 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/32 1723 ns 1550 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/1024 107096 ns 92850 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/8192 893976 ns 775046 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/0 436 ns 453 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/32 775 ns 824 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/1024 20241 ns 20454 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/8192 139038 ns 138032 ns std::set<int>::ctor(iterator, iterator) (unsorted sequence)/0 14.8 ns 14.7 ns std::set<int>::ctor(iterator, iterator) (unsorted sequence)/32 468 ns 426 ns std::set<int>::ctor(iterator, iterator) (unsorted sequence)/1024 54289 ns 39028 ns std::set<int>::ctor(iterator, iterator) (unsorted sequence)/8192 738438 ns 695720 ns std::set<int>::ctor(iterator, iterator) (sorted sequence)/0 14.7 ns 14.6 ns std::set<int>::ctor(iterator, iterator) (sorted sequence)/32 478 ns 391 ns std::set<int>::ctor(iterator, iterator) (sorted sequence)/1024 24017 ns 13905 ns std::set<int>::ctor(iterator, iterator) (sorted sequence)/8192 267862 ns 111378 ns std::set<int>::insert(iterator, iterator) (all new keys)/0 458 ns 450 ns std::set<int>::insert(iterator, iterator) (all new keys)/32 1066 ns 956 ns std::set<int>::insert(iterator, iterator) (all new keys)/1024 29190 ns 25212 ns std::set<int>::insert(iterator, iterator) (all new keys)/8192 320441 ns 279602 ns std::set<int>::insert(iterator, iterator) (half new keys)/0 454 ns 453 ns std::set<int>::insert(iterator, iterator) (half new keys)/32 816 ns 709 ns std::set<int>::insert(iterator, iterator) (half new keys)/1024 32072 ns 17074 ns std::set<int>::insert(iterator, iterator) (half new keys)/8192 403386 ns 286202 ns std::set<int>::erase(iterator, iterator) (erase half the container)/0 451 ns 452 ns std::set<int>::erase(iterator, iterator) (erase half the container)/32 710 ns 703 ns std::set<int>::erase(iterator, iterator) (erase half the container)/1024 8261 ns 8499 ns std::set<int>::erase(iterator, iterator) (erase half the container)/8192 64466 ns 67343 ns std::set<std::string>::ctor(iterator, iterator) (unsorted sequence)/0 15.2 ns 15.0 ns std::set<std::string>::ctor(iterator, iterator) (unsorted sequence)/32 3069 ns 3005 ns std::set<std::string>::ctor(iterator, iterator) (unsorted sequence)/1024 189552 ns 180933 ns std::set<std::string>::ctor(iterator, iterator) (unsorted sequence)/8192 2887579 ns 2691678 ns std::set<std::string>::ctor(iterator, iterator) (sorted sequence)/0 15.1 ns 14.9 ns std::set<std::string>::ctor(iterator, iterator) (sorted sequence)/32 2611 ns 2514 ns std::set<std::string>::ctor(iterator, iterator) (sorted sequence)/1024 91581 ns 78727 ns std::set<std::string>::ctor(iterator, iterator) (sorted sequence)/8192 1192640 ns 1158959 ns std::set<std::string>::insert(iterator, iterator) (all new keys)/0 452 ns 457 ns std::set<std::string>::insert(iterator, iterator) (all new keys)/32 2530 ns 2544 ns std::set<std::string>::insert(iterator, iterator) (all new keys)/1024 195352 ns 179614 ns std::set<std::string>::insert(iterator, iterator) (all new keys)/8192 1737890 ns 1749615 ns std::set<std::string>::insert(iterator, iterator) (half new keys)/0 451 ns 454 ns std::set<std::string>::insert(iterator, iterator) (half new keys)/32 1949 ns 1766 ns std::set<std::string>::insert(iterator, iterator) (half new keys)/1024 128853 ns 109467 ns std::set<std::string>::insert(iterator, iterator) (half new keys)/8192 1233077 ns 1177289 ns std::set<std::string>::erase(iterator, iterator) (erase half the container)/0 450 ns 451 ns std::set<std::string>::erase(iterator, iterator) (erase half the container)/32 809 ns 812 ns std::set<std::string>::erase(iterator, iterator) (erase half the container)/1024 21736 ns 21922 ns std::set<std::string>::erase(iterator, iterator) (erase half the container)/8192 135884 ns 133228 ns ``` Fixes #154650
2025-09-02 22:15:29 -08:00
if (__child == nullptr) {
__insert_node_at(
__parent, __child, std::__static_fancy_pointer_cast<__node_base_pointer>(__nd.release()));
[libc++] Optimize {map,set}::insert(InputIterator, InputIterator) (#154703) ``` ---------------------------------------------------------------------------------------------------------------------------- Benchmark old new ---------------------------------------------------------------------------------------------------------------------------- std::map<int, int>::ctor(iterator, iterator) (unsorted sequence)/0 14.2 ns 14.8 ns std::map<int, int>::ctor(iterator, iterator) (unsorted sequence)/32 519 ns 404 ns std::map<int, int>::ctor(iterator, iterator) (unsorted sequence)/1024 52460 ns 36242 ns std::map<int, int>::ctor(iterator, iterator) (unsorted sequence)/8192 724222 ns 706496 ns std::map<int, int>::ctor(iterator, iterator) (sorted sequence)/0 14.2 ns 14.7 ns std::map<int, int>::ctor(iterator, iterator) (sorted sequence)/32 429 ns 349 ns std::map<int, int>::ctor(iterator, iterator) (sorted sequence)/1024 23601 ns 14734 ns std::map<int, int>::ctor(iterator, iterator) (sorted sequence)/8192 267753 ns 112155 ns std::map<int, int>::insert(iterator, iterator) (all new keys)/0 434 ns 448 ns std::map<int, int>::insert(iterator, iterator) (all new keys)/32 950 ns 963 ns std::map<int, int>::insert(iterator, iterator) (all new keys)/1024 27205 ns 25344 ns std::map<int, int>::insert(iterator, iterator) (all new keys)/8192 294248 ns 280713 ns std::map<int, int>::insert(iterator, iterator) (half new keys)/0 435 ns 449 ns std::map<int, int>::insert(iterator, iterator) (half new keys)/32 771 ns 706 ns std::map<int, int>::insert(iterator, iterator) (half new keys)/1024 30841 ns 17495 ns std::map<int, int>::insert(iterator, iterator) (half new keys)/8192 468807 ns 285847 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from same type)/0 449 ns 453 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from same type)/32 1021 ns 932 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from same type)/1024 29796 ns 19518 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from same type)/8192 345688 ns 153966 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from zip_view)/0 449 ns 450 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from zip_view)/32 1026 ns 807 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from zip_view)/1024 31632 ns 15573 ns std::map<int, int>::insert(iterator, iterator) (product_iterator from zip_view)/8192 303024 ns 128946 ns std::map<int, int>::erase(iterator, iterator) (erase half the container)/0 447 ns 452 ns std::map<int, int>::erase(iterator, iterator) (erase half the container)/32 687 ns 710 ns std::map<int, int>::erase(iterator, iterator) (erase half the container)/1024 8604 ns 8581 ns std::map<int, int>::erase(iterator, iterator) (erase half the container)/8192 65693 ns 67406 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/0 15.0 ns 15.0 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/32 2781 ns 1845 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/1024 187999 ns 182103 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/8192 2937242 ns 2934912 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/0 15.0 ns 15.2 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/32 1326 ns 2462 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/1024 81778 ns 72193 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/8192 1177292 ns 669152 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/0 439 ns 454 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/32 2483 ns 2465 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/1024 187614 ns 188072 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/8192 1654675 ns 1706603 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/0 437 ns 452 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/32 1836 ns 1820 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/1024 114885 ns 121865 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/8192 1151960 ns 1197318 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/0 438 ns 455 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/32 1599 ns 1614 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/1024 95935 ns 82159 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/8192 776480 ns 941043 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/0 435 ns 462 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/32 1723 ns 1550 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/1024 107096 ns 92850 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/8192 893976 ns 775046 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/0 436 ns 453 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/32 775 ns 824 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/1024 20241 ns 20454 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/8192 139038 ns 138032 ns std::set<int>::ctor(iterator, iterator) (unsorted sequence)/0 14.8 ns 14.7 ns std::set<int>::ctor(iterator, iterator) (unsorted sequence)/32 468 ns 426 ns std::set<int>::ctor(iterator, iterator) (unsorted sequence)/1024 54289 ns 39028 ns std::set<int>::ctor(iterator, iterator) (unsorted sequence)/8192 738438 ns 695720 ns std::set<int>::ctor(iterator, iterator) (sorted sequence)/0 14.7 ns 14.6 ns std::set<int>::ctor(iterator, iterator) (sorted sequence)/32 478 ns 391 ns std::set<int>::ctor(iterator, iterator) (sorted sequence)/1024 24017 ns 13905 ns std::set<int>::ctor(iterator, iterator) (sorted sequence)/8192 267862 ns 111378 ns std::set<int>::insert(iterator, iterator) (all new keys)/0 458 ns 450 ns std::set<int>::insert(iterator, iterator) (all new keys)/32 1066 ns 956 ns std::set<int>::insert(iterator, iterator) (all new keys)/1024 29190 ns 25212 ns std::set<int>::insert(iterator, iterator) (all new keys)/8192 320441 ns 279602 ns std::set<int>::insert(iterator, iterator) (half new keys)/0 454 ns 453 ns std::set<int>::insert(iterator, iterator) (half new keys)/32 816 ns 709 ns std::set<int>::insert(iterator, iterator) (half new keys)/1024 32072 ns 17074 ns std::set<int>::insert(iterator, iterator) (half new keys)/8192 403386 ns 286202 ns std::set<int>::erase(iterator, iterator) (erase half the container)/0 451 ns 452 ns std::set<int>::erase(iterator, iterator) (erase half the container)/32 710 ns 703 ns std::set<int>::erase(iterator, iterator) (erase half the container)/1024 8261 ns 8499 ns std::set<int>::erase(iterator, iterator) (erase half the container)/8192 64466 ns 67343 ns std::set<std::string>::ctor(iterator, iterator) (unsorted sequence)/0 15.2 ns 15.0 ns std::set<std::string>::ctor(iterator, iterator) (unsorted sequence)/32 3069 ns 3005 ns std::set<std::string>::ctor(iterator, iterator) (unsorted sequence)/1024 189552 ns 180933 ns std::set<std::string>::ctor(iterator, iterator) (unsorted sequence)/8192 2887579 ns 2691678 ns std::set<std::string>::ctor(iterator, iterator) (sorted sequence)/0 15.1 ns 14.9 ns std::set<std::string>::ctor(iterator, iterator) (sorted sequence)/32 2611 ns 2514 ns std::set<std::string>::ctor(iterator, iterator) (sorted sequence)/1024 91581 ns 78727 ns std::set<std::string>::ctor(iterator, iterator) (sorted sequence)/8192 1192640 ns 1158959 ns std::set<std::string>::insert(iterator, iterator) (all new keys)/0 452 ns 457 ns std::set<std::string>::insert(iterator, iterator) (all new keys)/32 2530 ns 2544 ns std::set<std::string>::insert(iterator, iterator) (all new keys)/1024 195352 ns 179614 ns std::set<std::string>::insert(iterator, iterator) (all new keys)/8192 1737890 ns 1749615 ns std::set<std::string>::insert(iterator, iterator) (half new keys)/0 451 ns 454 ns std::set<std::string>::insert(iterator, iterator) (half new keys)/32 1949 ns 1766 ns std::set<std::string>::insert(iterator, iterator) (half new keys)/1024 128853 ns 109467 ns std::set<std::string>::insert(iterator, iterator) (half new keys)/8192 1233077 ns 1177289 ns std::set<std::string>::erase(iterator, iterator) (erase half the container)/0 450 ns 451 ns std::set<std::string>::erase(iterator, iterator) (erase half the container)/32 809 ns 812 ns std::set<std::string>::erase(iterator, iterator) (erase half the container)/1024 21736 ns 21922 ns std::set<std::string>::erase(iterator, iterator) (erase half the container)/8192 135884 ns 133228 ns ``` Fixes #154650
2025-09-02 22:15:29 -08:00
}
}
},
*__first);
}
}
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator __remove_node_pointer(__node_pointer) _NOEXCEPT;
#if _LIBCPP_STD_VER >= 17
template <class _NodeHandle, class _InsertReturnType>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _InsertReturnType __node_handle_insert_unique(_NodeHandle&&);
template <class _NodeHandle>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator
__node_handle_insert_unique(const_iterator, _NodeHandle&&);
template <class _Comp2>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
__node_handle_merge_unique(__tree<_Tp, _Comp2, _Allocator>& __source);
template <class _NodeHandle>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator __node_handle_insert_multi(_NodeHandle&&);
template <class _NodeHandle>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator
__node_handle_insert_multi(const_iterator, _NodeHandle&&);
template <class _Comp2>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
__node_handle_merge_multi(__tree<_Tp, _Comp2, _Allocator>& __source);
template <class _NodeHandle>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodeHandle __node_handle_extract(key_type const&);
template <class _NodeHandle>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodeHandle __node_handle_extract(const_iterator);
#endif
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator erase(const_iterator __p);
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator erase(const_iterator __f, const_iterator __l);
2010-05-11 11:42:16 -08:00
template <class _Key>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type __erase_unique(const _Key& __k);
2010-05-11 11:42:16 -08:00
template <class _Key>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type __erase_multi(const _Key& __k);
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
__insert_node_at(__end_node_pointer __parent, __node_base_pointer& __child, __node_base_pointer __new_node) _NOEXCEPT;
2010-05-11 11:42:16 -08:00
template <class _Key>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator find(const _Key& __key) {
auto [__dummy, __match] = __find_equal(__key);
[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 ```
2025-08-14 22:59:39 -08:00
if (__match == nullptr)
return end();
return iterator(std::__static_fancy_pointer_cast<__node_pointer>(__match));
[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 ```
2025-08-14 22:59:39 -08:00
}
2010-05-11 11:42:16 -08:00
template <class _Key>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator find(const _Key& __key) const {
auto [__dummy, __match] = __find_equal(__key);
[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 ```
2025-08-14 22:59:39 -08:00
if (__match == nullptr)
return end();
return const_iterator(std::__static_fancy_pointer_cast<__node_pointer>(__match));
[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 ```
2025-08-14 22:59:39 -08:00
}
2010-05-11 11:42:16 -08:00
template <class _Key>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type __count_unique(const _Key& __k) const;
2010-05-11 11:42:16 -08:00
template <class _Key>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type __count_multi(const _Key& __k) const;
[libc++] Optimize {set,map}::{lower,upper}_bound (#161366) Apple M4: ``` Benchmark Baseline Candidate Difference % Difference --------------------------------------------------------------------------------------------- ---------- ----------- ------------ -------------- std::map<int,_int>::lower_bound(key)_(existent)/0 0.01 0.01 -0.00 -25.78 std::map<int,_int>::lower_bound(key)_(existent)/1024 7.94 4.28 -3.66 -46.09 std::map<int,_int>::lower_bound(key)_(existent)/32 2.73 1.69 -1.03 -37.89 std::map<int,_int>::lower_bound(key)_(existent)/8192 11.63 5.52 -6.11 -52.55 std::map<int,_int>::lower_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -1.35 std::map<int,_int>::lower_bound(key)_(non-existent)/1024 17.21 7.63 -9.58 -55.67 std::map<int,_int>::lower_bound(key)_(non-existent)/32 4.71 3.26 -1.45 -30.71 std::map<int,_int>::lower_bound(key)_(non-existent)/8192 26.82 10.58 -16.24 -60.55 std::map<int,_int>::upper_bound(key)_(existent)/0 0.01 0.01 0.00 20.62 std::map<int,_int>::upper_bound(key)_(existent)/1024 7.93 3.61 -4.32 -54.49 std::map<int,_int>::upper_bound(key)_(existent)/32 2.83 1.98 -0.85 -30.01 std::map<int,_int>::upper_bound(key)_(existent)/8192 11.69 5.72 -5.97 -51.06 std::map<int,_int>::upper_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -1.36 std::map<int,_int>::upper_bound(key)_(non-existent)/1024 17.21 8.00 -9.21 -53.53 std::map<int,_int>::upper_bound(key)_(non-existent)/32 4.70 2.93 -1.78 -37.76 std::map<int,_int>::upper_bound(key)_(non-existent)/8192 26.54 11.18 -15.36 -57.89 std::map<std::string,_int>::lower_bound(key)_(existent)/0 0.04 0.04 -0.00 -3.26 std::map<std::string,_int>::lower_bound(key)_(existent)/1024 27.46 26.25 -1.22 -4.43 std::map<std::string,_int>::lower_bound(key)_(existent)/32 19.17 15.71 -3.46 -18.07 std::map<std::string,_int>::lower_bound(key)_(existent)/8192 35.33 35.03 -0.30 -0.84 std::map<std::string,_int>::lower_bound(key)_(non-existent)/0 0.27 0.27 -0.00 -1.45 std::map<std::string,_int>::lower_bound(key)_(non-existent)/1024 24.88 24.17 -0.70 -2.83 std::map<std::string,_int>::lower_bound(key)_(non-existent)/32 11.67 11.63 -0.04 -0.32 std::map<std::string,_int>::lower_bound(key)_(non-existent)/8192 31.81 32.33 0.52 1.64 std::map<std::string,_int>::upper_bound(key)_(existent)/0 0.04 0.04 -0.00 -2.22 std::map<std::string,_int>::upper_bound(key)_(existent)/1024 29.91 26.51 -3.40 -11.38 std::map<std::string,_int>::upper_bound(key)_(existent)/32 19.69 17.74 -1.95 -9.92 std::map<std::string,_int>::upper_bound(key)_(existent)/8192 32.55 35.24 2.69 8.25 std::map<std::string,_int>::upper_bound(key)_(non-existent)/0 0.27 0.27 -0.00 -1.74 std::map<std::string,_int>::upper_bound(key)_(non-existent)/1024 23.87 26.77 2.91 12.18 std::map<std::string,_int>::upper_bound(key)_(non-existent)/32 11.44 11.81 0.37 3.24 std::map<std::string,_int>::upper_bound(key)_(non-existent)/8192 33.02 32.59 -0.43 -1.29 std::set<int>::lower_bound(key)_(existent)/0 0.01 0.01 0.00 0.48 std::set<int>::lower_bound(key)_(existent)/1024 7.83 4.21 -3.62 -46.23 std::set<int>::lower_bound(key)_(existent)/32 2.74 1.68 -1.06 -38.81 std::set<int>::lower_bound(key)_(existent)/8192 22.75 11.12 -11.63 -51.12 std::set<int>::lower_bound(key)_(non-existent)/0 0.28 0.27 -0.01 -3.52 std::set<int>::lower_bound(key)_(non-existent)/1024 17.15 8.40 -8.75 -51.03 std::set<int>::lower_bound(key)_(non-existent)/32 4.63 2.50 -2.13 -46.03 std::set<int>::lower_bound(key)_(non-existent)/8192 28.88 11.05 -17.82 -61.72 std::set<int>::upper_bound(key)_(existent)/0 0.01 0.01 -0.00 -7.79 std::set<int>::upper_bound(key)_(existent)/1024 7.80 3.63 -4.16 -53.42 std::set<int>::upper_bound(key)_(existent)/32 2.81 1.90 -0.91 -32.44 std::set<int>::upper_bound(key)_(existent)/8192 21.93 11.35 -10.58 -48.26 std::set<int>::upper_bound(key)_(non-existent)/0 0.28 0.27 -0.01 -3.81 std::set<int>::upper_bound(key)_(non-existent)/1024 16.76 7.38 -9.38 -55.98 std::set<int>::upper_bound(key)_(non-existent)/32 4.58 3.10 -1.48 -32.31 std::set<int>::upper_bound(key)_(non-existent)/8192 26.95 10.70 -16.25 -60.29 std::set<std::string>::lower_bound(key)_(existent)/0 0.04 0.04 0.00 0.02 std::set<std::string>::lower_bound(key)_(existent)/1024 28.08 27.04 -1.04 -3.71 std::set<std::string>::lower_bound(key)_(existent)/32 17.53 16.94 -0.58 -3.34 std::set<std::string>::lower_bound(key)_(existent)/8192 32.79 33.28 0.49 1.49 std::set<std::string>::lower_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -0.06 std::set<std::string>::lower_bound(key)_(non-existent)/1024 25.23 24.38 -0.85 -3.38 std::set<std::string>::lower_bound(key)_(non-existent)/32 11.45 11.68 0.24 2.07 std::set<std::string>::lower_bound(key)_(non-existent)/8192 32.30 36.80 4.50 13.95 std::set<std::string>::upper_bound(key)_(existent)/0 0.04 0.04 -0.00 -0.14 std::set<std::string>::upper_bound(key)_(existent)/1024 26.71 26.37 -0.34 -1.27 std::set<std::string>::upper_bound(key)_(existent)/32 20.07 19.06 -1.02 -5.06 std::set<std::string>::upper_bound(key)_(existent)/8192 36.69 35.50 -1.19 -3.25 std::set<std::string>::upper_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -0.16 std::set<std::string>::upper_bound(key)_(non-existent)/1024 24.48 24.90 0.42 1.73 std::set<std::string>::upper_bound(key)_(non-existent)/32 11.68 11.77 0.09 0.77 std::set<std::string>::upper_bound(key)_(non-existent)/8192 33.16 34.12 0.96 2.89 ```
2025-10-08 22:45:03 -08:00
template <bool _LowerBound, class _Key>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __end_node_pointer
__lower_upper_bound_unique_impl(const _Key& __v) const {
[libc++] Optimize {set,map}::{lower,upper}_bound (#161366) Apple M4: ``` Benchmark Baseline Candidate Difference % Difference --------------------------------------------------------------------------------------------- ---------- ----------- ------------ -------------- std::map<int,_int>::lower_bound(key)_(existent)/0 0.01 0.01 -0.00 -25.78 std::map<int,_int>::lower_bound(key)_(existent)/1024 7.94 4.28 -3.66 -46.09 std::map<int,_int>::lower_bound(key)_(existent)/32 2.73 1.69 -1.03 -37.89 std::map<int,_int>::lower_bound(key)_(existent)/8192 11.63 5.52 -6.11 -52.55 std::map<int,_int>::lower_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -1.35 std::map<int,_int>::lower_bound(key)_(non-existent)/1024 17.21 7.63 -9.58 -55.67 std::map<int,_int>::lower_bound(key)_(non-existent)/32 4.71 3.26 -1.45 -30.71 std::map<int,_int>::lower_bound(key)_(non-existent)/8192 26.82 10.58 -16.24 -60.55 std::map<int,_int>::upper_bound(key)_(existent)/0 0.01 0.01 0.00 20.62 std::map<int,_int>::upper_bound(key)_(existent)/1024 7.93 3.61 -4.32 -54.49 std::map<int,_int>::upper_bound(key)_(existent)/32 2.83 1.98 -0.85 -30.01 std::map<int,_int>::upper_bound(key)_(existent)/8192 11.69 5.72 -5.97 -51.06 std::map<int,_int>::upper_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -1.36 std::map<int,_int>::upper_bound(key)_(non-existent)/1024 17.21 8.00 -9.21 -53.53 std::map<int,_int>::upper_bound(key)_(non-existent)/32 4.70 2.93 -1.78 -37.76 std::map<int,_int>::upper_bound(key)_(non-existent)/8192 26.54 11.18 -15.36 -57.89 std::map<std::string,_int>::lower_bound(key)_(existent)/0 0.04 0.04 -0.00 -3.26 std::map<std::string,_int>::lower_bound(key)_(existent)/1024 27.46 26.25 -1.22 -4.43 std::map<std::string,_int>::lower_bound(key)_(existent)/32 19.17 15.71 -3.46 -18.07 std::map<std::string,_int>::lower_bound(key)_(existent)/8192 35.33 35.03 -0.30 -0.84 std::map<std::string,_int>::lower_bound(key)_(non-existent)/0 0.27 0.27 -0.00 -1.45 std::map<std::string,_int>::lower_bound(key)_(non-existent)/1024 24.88 24.17 -0.70 -2.83 std::map<std::string,_int>::lower_bound(key)_(non-existent)/32 11.67 11.63 -0.04 -0.32 std::map<std::string,_int>::lower_bound(key)_(non-existent)/8192 31.81 32.33 0.52 1.64 std::map<std::string,_int>::upper_bound(key)_(existent)/0 0.04 0.04 -0.00 -2.22 std::map<std::string,_int>::upper_bound(key)_(existent)/1024 29.91 26.51 -3.40 -11.38 std::map<std::string,_int>::upper_bound(key)_(existent)/32 19.69 17.74 -1.95 -9.92 std::map<std::string,_int>::upper_bound(key)_(existent)/8192 32.55 35.24 2.69 8.25 std::map<std::string,_int>::upper_bound(key)_(non-existent)/0 0.27 0.27 -0.00 -1.74 std::map<std::string,_int>::upper_bound(key)_(non-existent)/1024 23.87 26.77 2.91 12.18 std::map<std::string,_int>::upper_bound(key)_(non-existent)/32 11.44 11.81 0.37 3.24 std::map<std::string,_int>::upper_bound(key)_(non-existent)/8192 33.02 32.59 -0.43 -1.29 std::set<int>::lower_bound(key)_(existent)/0 0.01 0.01 0.00 0.48 std::set<int>::lower_bound(key)_(existent)/1024 7.83 4.21 -3.62 -46.23 std::set<int>::lower_bound(key)_(existent)/32 2.74 1.68 -1.06 -38.81 std::set<int>::lower_bound(key)_(existent)/8192 22.75 11.12 -11.63 -51.12 std::set<int>::lower_bound(key)_(non-existent)/0 0.28 0.27 -0.01 -3.52 std::set<int>::lower_bound(key)_(non-existent)/1024 17.15 8.40 -8.75 -51.03 std::set<int>::lower_bound(key)_(non-existent)/32 4.63 2.50 -2.13 -46.03 std::set<int>::lower_bound(key)_(non-existent)/8192 28.88 11.05 -17.82 -61.72 std::set<int>::upper_bound(key)_(existent)/0 0.01 0.01 -0.00 -7.79 std::set<int>::upper_bound(key)_(existent)/1024 7.80 3.63 -4.16 -53.42 std::set<int>::upper_bound(key)_(existent)/32 2.81 1.90 -0.91 -32.44 std::set<int>::upper_bound(key)_(existent)/8192 21.93 11.35 -10.58 -48.26 std::set<int>::upper_bound(key)_(non-existent)/0 0.28 0.27 -0.01 -3.81 std::set<int>::upper_bound(key)_(non-existent)/1024 16.76 7.38 -9.38 -55.98 std::set<int>::upper_bound(key)_(non-existent)/32 4.58 3.10 -1.48 -32.31 std::set<int>::upper_bound(key)_(non-existent)/8192 26.95 10.70 -16.25 -60.29 std::set<std::string>::lower_bound(key)_(existent)/0 0.04 0.04 0.00 0.02 std::set<std::string>::lower_bound(key)_(existent)/1024 28.08 27.04 -1.04 -3.71 std::set<std::string>::lower_bound(key)_(existent)/32 17.53 16.94 -0.58 -3.34 std::set<std::string>::lower_bound(key)_(existent)/8192 32.79 33.28 0.49 1.49 std::set<std::string>::lower_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -0.06 std::set<std::string>::lower_bound(key)_(non-existent)/1024 25.23 24.38 -0.85 -3.38 std::set<std::string>::lower_bound(key)_(non-existent)/32 11.45 11.68 0.24 2.07 std::set<std::string>::lower_bound(key)_(non-existent)/8192 32.30 36.80 4.50 13.95 std::set<std::string>::upper_bound(key)_(existent)/0 0.04 0.04 -0.00 -0.14 std::set<std::string>::upper_bound(key)_(existent)/1024 26.71 26.37 -0.34 -1.27 std::set<std::string>::upper_bound(key)_(existent)/32 20.07 19.06 -1.02 -5.06 std::set<std::string>::upper_bound(key)_(existent)/8192 36.69 35.50 -1.19 -3.25 std::set<std::string>::upper_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -0.16 std::set<std::string>::upper_bound(key)_(non-existent)/1024 24.48 24.90 0.42 1.73 std::set<std::string>::upper_bound(key)_(non-existent)/32 11.68 11.77 0.09 0.77 std::set<std::string>::upper_bound(key)_(non-existent)/8192 33.16 34.12 0.96 2.89 ```
2025-10-08 22:45:03 -08:00
auto __rt = __root();
auto __result = __end_node();
auto __comp = __lazy_synth_three_way_comparator<_Compare, _Key, value_type>(value_comp());
while (__rt != nullptr) {
auto __comp_res = __comp(__v, __rt->__get_value());
if (__comp_res.__less()) {
__result = std::__static_fancy_pointer_cast<__end_node_pointer>(__rt);
__rt = std::__static_fancy_pointer_cast<__node_pointer>(__rt->__left_);
[libc++] Optimize {set,map}::{lower,upper}_bound (#161366) Apple M4: ``` Benchmark Baseline Candidate Difference % Difference --------------------------------------------------------------------------------------------- ---------- ----------- ------------ -------------- std::map<int,_int>::lower_bound(key)_(existent)/0 0.01 0.01 -0.00 -25.78 std::map<int,_int>::lower_bound(key)_(existent)/1024 7.94 4.28 -3.66 -46.09 std::map<int,_int>::lower_bound(key)_(existent)/32 2.73 1.69 -1.03 -37.89 std::map<int,_int>::lower_bound(key)_(existent)/8192 11.63 5.52 -6.11 -52.55 std::map<int,_int>::lower_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -1.35 std::map<int,_int>::lower_bound(key)_(non-existent)/1024 17.21 7.63 -9.58 -55.67 std::map<int,_int>::lower_bound(key)_(non-existent)/32 4.71 3.26 -1.45 -30.71 std::map<int,_int>::lower_bound(key)_(non-existent)/8192 26.82 10.58 -16.24 -60.55 std::map<int,_int>::upper_bound(key)_(existent)/0 0.01 0.01 0.00 20.62 std::map<int,_int>::upper_bound(key)_(existent)/1024 7.93 3.61 -4.32 -54.49 std::map<int,_int>::upper_bound(key)_(existent)/32 2.83 1.98 -0.85 -30.01 std::map<int,_int>::upper_bound(key)_(existent)/8192 11.69 5.72 -5.97 -51.06 std::map<int,_int>::upper_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -1.36 std::map<int,_int>::upper_bound(key)_(non-existent)/1024 17.21 8.00 -9.21 -53.53 std::map<int,_int>::upper_bound(key)_(non-existent)/32 4.70 2.93 -1.78 -37.76 std::map<int,_int>::upper_bound(key)_(non-existent)/8192 26.54 11.18 -15.36 -57.89 std::map<std::string,_int>::lower_bound(key)_(existent)/0 0.04 0.04 -0.00 -3.26 std::map<std::string,_int>::lower_bound(key)_(existent)/1024 27.46 26.25 -1.22 -4.43 std::map<std::string,_int>::lower_bound(key)_(existent)/32 19.17 15.71 -3.46 -18.07 std::map<std::string,_int>::lower_bound(key)_(existent)/8192 35.33 35.03 -0.30 -0.84 std::map<std::string,_int>::lower_bound(key)_(non-existent)/0 0.27 0.27 -0.00 -1.45 std::map<std::string,_int>::lower_bound(key)_(non-existent)/1024 24.88 24.17 -0.70 -2.83 std::map<std::string,_int>::lower_bound(key)_(non-existent)/32 11.67 11.63 -0.04 -0.32 std::map<std::string,_int>::lower_bound(key)_(non-existent)/8192 31.81 32.33 0.52 1.64 std::map<std::string,_int>::upper_bound(key)_(existent)/0 0.04 0.04 -0.00 -2.22 std::map<std::string,_int>::upper_bound(key)_(existent)/1024 29.91 26.51 -3.40 -11.38 std::map<std::string,_int>::upper_bound(key)_(existent)/32 19.69 17.74 -1.95 -9.92 std::map<std::string,_int>::upper_bound(key)_(existent)/8192 32.55 35.24 2.69 8.25 std::map<std::string,_int>::upper_bound(key)_(non-existent)/0 0.27 0.27 -0.00 -1.74 std::map<std::string,_int>::upper_bound(key)_(non-existent)/1024 23.87 26.77 2.91 12.18 std::map<std::string,_int>::upper_bound(key)_(non-existent)/32 11.44 11.81 0.37 3.24 std::map<std::string,_int>::upper_bound(key)_(non-existent)/8192 33.02 32.59 -0.43 -1.29 std::set<int>::lower_bound(key)_(existent)/0 0.01 0.01 0.00 0.48 std::set<int>::lower_bound(key)_(existent)/1024 7.83 4.21 -3.62 -46.23 std::set<int>::lower_bound(key)_(existent)/32 2.74 1.68 -1.06 -38.81 std::set<int>::lower_bound(key)_(existent)/8192 22.75 11.12 -11.63 -51.12 std::set<int>::lower_bound(key)_(non-existent)/0 0.28 0.27 -0.01 -3.52 std::set<int>::lower_bound(key)_(non-existent)/1024 17.15 8.40 -8.75 -51.03 std::set<int>::lower_bound(key)_(non-existent)/32 4.63 2.50 -2.13 -46.03 std::set<int>::lower_bound(key)_(non-existent)/8192 28.88 11.05 -17.82 -61.72 std::set<int>::upper_bound(key)_(existent)/0 0.01 0.01 -0.00 -7.79 std::set<int>::upper_bound(key)_(existent)/1024 7.80 3.63 -4.16 -53.42 std::set<int>::upper_bound(key)_(existent)/32 2.81 1.90 -0.91 -32.44 std::set<int>::upper_bound(key)_(existent)/8192 21.93 11.35 -10.58 -48.26 std::set<int>::upper_bound(key)_(non-existent)/0 0.28 0.27 -0.01 -3.81 std::set<int>::upper_bound(key)_(non-existent)/1024 16.76 7.38 -9.38 -55.98 std::set<int>::upper_bound(key)_(non-existent)/32 4.58 3.10 -1.48 -32.31 std::set<int>::upper_bound(key)_(non-existent)/8192 26.95 10.70 -16.25 -60.29 std::set<std::string>::lower_bound(key)_(existent)/0 0.04 0.04 0.00 0.02 std::set<std::string>::lower_bound(key)_(existent)/1024 28.08 27.04 -1.04 -3.71 std::set<std::string>::lower_bound(key)_(existent)/32 17.53 16.94 -0.58 -3.34 std::set<std::string>::lower_bound(key)_(existent)/8192 32.79 33.28 0.49 1.49 std::set<std::string>::lower_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -0.06 std::set<std::string>::lower_bound(key)_(non-existent)/1024 25.23 24.38 -0.85 -3.38 std::set<std::string>::lower_bound(key)_(non-existent)/32 11.45 11.68 0.24 2.07 std::set<std::string>::lower_bound(key)_(non-existent)/8192 32.30 36.80 4.50 13.95 std::set<std::string>::upper_bound(key)_(existent)/0 0.04 0.04 -0.00 -0.14 std::set<std::string>::upper_bound(key)_(existent)/1024 26.71 26.37 -0.34 -1.27 std::set<std::string>::upper_bound(key)_(existent)/32 20.07 19.06 -1.02 -5.06 std::set<std::string>::upper_bound(key)_(existent)/8192 36.69 35.50 -1.19 -3.25 std::set<std::string>::upper_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -0.16 std::set<std::string>::upper_bound(key)_(non-existent)/1024 24.48 24.90 0.42 1.73 std::set<std::string>::upper_bound(key)_(non-existent)/32 11.68 11.77 0.09 0.77 std::set<std::string>::upper_bound(key)_(non-existent)/8192 33.16 34.12 0.96 2.89 ```
2025-10-08 22:45:03 -08:00
} else if (__comp_res.__greater()) {
__rt = std::__static_fancy_pointer_cast<__node_pointer>(__rt->__right_);
[libc++] Optimize {set,map}::{lower,upper}_bound (#161366) Apple M4: ``` Benchmark Baseline Candidate Difference % Difference --------------------------------------------------------------------------------------------- ---------- ----------- ------------ -------------- std::map<int,_int>::lower_bound(key)_(existent)/0 0.01 0.01 -0.00 -25.78 std::map<int,_int>::lower_bound(key)_(existent)/1024 7.94 4.28 -3.66 -46.09 std::map<int,_int>::lower_bound(key)_(existent)/32 2.73 1.69 -1.03 -37.89 std::map<int,_int>::lower_bound(key)_(existent)/8192 11.63 5.52 -6.11 -52.55 std::map<int,_int>::lower_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -1.35 std::map<int,_int>::lower_bound(key)_(non-existent)/1024 17.21 7.63 -9.58 -55.67 std::map<int,_int>::lower_bound(key)_(non-existent)/32 4.71 3.26 -1.45 -30.71 std::map<int,_int>::lower_bound(key)_(non-existent)/8192 26.82 10.58 -16.24 -60.55 std::map<int,_int>::upper_bound(key)_(existent)/0 0.01 0.01 0.00 20.62 std::map<int,_int>::upper_bound(key)_(existent)/1024 7.93 3.61 -4.32 -54.49 std::map<int,_int>::upper_bound(key)_(existent)/32 2.83 1.98 -0.85 -30.01 std::map<int,_int>::upper_bound(key)_(existent)/8192 11.69 5.72 -5.97 -51.06 std::map<int,_int>::upper_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -1.36 std::map<int,_int>::upper_bound(key)_(non-existent)/1024 17.21 8.00 -9.21 -53.53 std::map<int,_int>::upper_bound(key)_(non-existent)/32 4.70 2.93 -1.78 -37.76 std::map<int,_int>::upper_bound(key)_(non-existent)/8192 26.54 11.18 -15.36 -57.89 std::map<std::string,_int>::lower_bound(key)_(existent)/0 0.04 0.04 -0.00 -3.26 std::map<std::string,_int>::lower_bound(key)_(existent)/1024 27.46 26.25 -1.22 -4.43 std::map<std::string,_int>::lower_bound(key)_(existent)/32 19.17 15.71 -3.46 -18.07 std::map<std::string,_int>::lower_bound(key)_(existent)/8192 35.33 35.03 -0.30 -0.84 std::map<std::string,_int>::lower_bound(key)_(non-existent)/0 0.27 0.27 -0.00 -1.45 std::map<std::string,_int>::lower_bound(key)_(non-existent)/1024 24.88 24.17 -0.70 -2.83 std::map<std::string,_int>::lower_bound(key)_(non-existent)/32 11.67 11.63 -0.04 -0.32 std::map<std::string,_int>::lower_bound(key)_(non-existent)/8192 31.81 32.33 0.52 1.64 std::map<std::string,_int>::upper_bound(key)_(existent)/0 0.04 0.04 -0.00 -2.22 std::map<std::string,_int>::upper_bound(key)_(existent)/1024 29.91 26.51 -3.40 -11.38 std::map<std::string,_int>::upper_bound(key)_(existent)/32 19.69 17.74 -1.95 -9.92 std::map<std::string,_int>::upper_bound(key)_(existent)/8192 32.55 35.24 2.69 8.25 std::map<std::string,_int>::upper_bound(key)_(non-existent)/0 0.27 0.27 -0.00 -1.74 std::map<std::string,_int>::upper_bound(key)_(non-existent)/1024 23.87 26.77 2.91 12.18 std::map<std::string,_int>::upper_bound(key)_(non-existent)/32 11.44 11.81 0.37 3.24 std::map<std::string,_int>::upper_bound(key)_(non-existent)/8192 33.02 32.59 -0.43 -1.29 std::set<int>::lower_bound(key)_(existent)/0 0.01 0.01 0.00 0.48 std::set<int>::lower_bound(key)_(existent)/1024 7.83 4.21 -3.62 -46.23 std::set<int>::lower_bound(key)_(existent)/32 2.74 1.68 -1.06 -38.81 std::set<int>::lower_bound(key)_(existent)/8192 22.75 11.12 -11.63 -51.12 std::set<int>::lower_bound(key)_(non-existent)/0 0.28 0.27 -0.01 -3.52 std::set<int>::lower_bound(key)_(non-existent)/1024 17.15 8.40 -8.75 -51.03 std::set<int>::lower_bound(key)_(non-existent)/32 4.63 2.50 -2.13 -46.03 std::set<int>::lower_bound(key)_(non-existent)/8192 28.88 11.05 -17.82 -61.72 std::set<int>::upper_bound(key)_(existent)/0 0.01 0.01 -0.00 -7.79 std::set<int>::upper_bound(key)_(existent)/1024 7.80 3.63 -4.16 -53.42 std::set<int>::upper_bound(key)_(existent)/32 2.81 1.90 -0.91 -32.44 std::set<int>::upper_bound(key)_(existent)/8192 21.93 11.35 -10.58 -48.26 std::set<int>::upper_bound(key)_(non-existent)/0 0.28 0.27 -0.01 -3.81 std::set<int>::upper_bound(key)_(non-existent)/1024 16.76 7.38 -9.38 -55.98 std::set<int>::upper_bound(key)_(non-existent)/32 4.58 3.10 -1.48 -32.31 std::set<int>::upper_bound(key)_(non-existent)/8192 26.95 10.70 -16.25 -60.29 std::set<std::string>::lower_bound(key)_(existent)/0 0.04 0.04 0.00 0.02 std::set<std::string>::lower_bound(key)_(existent)/1024 28.08 27.04 -1.04 -3.71 std::set<std::string>::lower_bound(key)_(existent)/32 17.53 16.94 -0.58 -3.34 std::set<std::string>::lower_bound(key)_(existent)/8192 32.79 33.28 0.49 1.49 std::set<std::string>::lower_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -0.06 std::set<std::string>::lower_bound(key)_(non-existent)/1024 25.23 24.38 -0.85 -3.38 std::set<std::string>::lower_bound(key)_(non-existent)/32 11.45 11.68 0.24 2.07 std::set<std::string>::lower_bound(key)_(non-existent)/8192 32.30 36.80 4.50 13.95 std::set<std::string>::upper_bound(key)_(existent)/0 0.04 0.04 -0.00 -0.14 std::set<std::string>::upper_bound(key)_(existent)/1024 26.71 26.37 -0.34 -1.27 std::set<std::string>::upper_bound(key)_(existent)/32 20.07 19.06 -1.02 -5.06 std::set<std::string>::upper_bound(key)_(existent)/8192 36.69 35.50 -1.19 -3.25 std::set<std::string>::upper_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -0.16 std::set<std::string>::upper_bound(key)_(non-existent)/1024 24.48 24.90 0.42 1.73 std::set<std::string>::upper_bound(key)_(non-existent)/32 11.68 11.77 0.09 0.77 std::set<std::string>::upper_bound(key)_(non-existent)/8192 33.16 34.12 0.96 2.89 ```
2025-10-08 22:45:03 -08:00
} else if _LIBCPP_CONSTEXPR (_LowerBound) {
return std::__static_fancy_pointer_cast<__end_node_pointer>(__rt);
[libc++] Optimize {set,map}::{lower,upper}_bound (#161366) Apple M4: ``` Benchmark Baseline Candidate Difference % Difference --------------------------------------------------------------------------------------------- ---------- ----------- ------------ -------------- std::map<int,_int>::lower_bound(key)_(existent)/0 0.01 0.01 -0.00 -25.78 std::map<int,_int>::lower_bound(key)_(existent)/1024 7.94 4.28 -3.66 -46.09 std::map<int,_int>::lower_bound(key)_(existent)/32 2.73 1.69 -1.03 -37.89 std::map<int,_int>::lower_bound(key)_(existent)/8192 11.63 5.52 -6.11 -52.55 std::map<int,_int>::lower_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -1.35 std::map<int,_int>::lower_bound(key)_(non-existent)/1024 17.21 7.63 -9.58 -55.67 std::map<int,_int>::lower_bound(key)_(non-existent)/32 4.71 3.26 -1.45 -30.71 std::map<int,_int>::lower_bound(key)_(non-existent)/8192 26.82 10.58 -16.24 -60.55 std::map<int,_int>::upper_bound(key)_(existent)/0 0.01 0.01 0.00 20.62 std::map<int,_int>::upper_bound(key)_(existent)/1024 7.93 3.61 -4.32 -54.49 std::map<int,_int>::upper_bound(key)_(existent)/32 2.83 1.98 -0.85 -30.01 std::map<int,_int>::upper_bound(key)_(existent)/8192 11.69 5.72 -5.97 -51.06 std::map<int,_int>::upper_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -1.36 std::map<int,_int>::upper_bound(key)_(non-existent)/1024 17.21 8.00 -9.21 -53.53 std::map<int,_int>::upper_bound(key)_(non-existent)/32 4.70 2.93 -1.78 -37.76 std::map<int,_int>::upper_bound(key)_(non-existent)/8192 26.54 11.18 -15.36 -57.89 std::map<std::string,_int>::lower_bound(key)_(existent)/0 0.04 0.04 -0.00 -3.26 std::map<std::string,_int>::lower_bound(key)_(existent)/1024 27.46 26.25 -1.22 -4.43 std::map<std::string,_int>::lower_bound(key)_(existent)/32 19.17 15.71 -3.46 -18.07 std::map<std::string,_int>::lower_bound(key)_(existent)/8192 35.33 35.03 -0.30 -0.84 std::map<std::string,_int>::lower_bound(key)_(non-existent)/0 0.27 0.27 -0.00 -1.45 std::map<std::string,_int>::lower_bound(key)_(non-existent)/1024 24.88 24.17 -0.70 -2.83 std::map<std::string,_int>::lower_bound(key)_(non-existent)/32 11.67 11.63 -0.04 -0.32 std::map<std::string,_int>::lower_bound(key)_(non-existent)/8192 31.81 32.33 0.52 1.64 std::map<std::string,_int>::upper_bound(key)_(existent)/0 0.04 0.04 -0.00 -2.22 std::map<std::string,_int>::upper_bound(key)_(existent)/1024 29.91 26.51 -3.40 -11.38 std::map<std::string,_int>::upper_bound(key)_(existent)/32 19.69 17.74 -1.95 -9.92 std::map<std::string,_int>::upper_bound(key)_(existent)/8192 32.55 35.24 2.69 8.25 std::map<std::string,_int>::upper_bound(key)_(non-existent)/0 0.27 0.27 -0.00 -1.74 std::map<std::string,_int>::upper_bound(key)_(non-existent)/1024 23.87 26.77 2.91 12.18 std::map<std::string,_int>::upper_bound(key)_(non-existent)/32 11.44 11.81 0.37 3.24 std::map<std::string,_int>::upper_bound(key)_(non-existent)/8192 33.02 32.59 -0.43 -1.29 std::set<int>::lower_bound(key)_(existent)/0 0.01 0.01 0.00 0.48 std::set<int>::lower_bound(key)_(existent)/1024 7.83 4.21 -3.62 -46.23 std::set<int>::lower_bound(key)_(existent)/32 2.74 1.68 -1.06 -38.81 std::set<int>::lower_bound(key)_(existent)/8192 22.75 11.12 -11.63 -51.12 std::set<int>::lower_bound(key)_(non-existent)/0 0.28 0.27 -0.01 -3.52 std::set<int>::lower_bound(key)_(non-existent)/1024 17.15 8.40 -8.75 -51.03 std::set<int>::lower_bound(key)_(non-existent)/32 4.63 2.50 -2.13 -46.03 std::set<int>::lower_bound(key)_(non-existent)/8192 28.88 11.05 -17.82 -61.72 std::set<int>::upper_bound(key)_(existent)/0 0.01 0.01 -0.00 -7.79 std::set<int>::upper_bound(key)_(existent)/1024 7.80 3.63 -4.16 -53.42 std::set<int>::upper_bound(key)_(existent)/32 2.81 1.90 -0.91 -32.44 std::set<int>::upper_bound(key)_(existent)/8192 21.93 11.35 -10.58 -48.26 std::set<int>::upper_bound(key)_(non-existent)/0 0.28 0.27 -0.01 -3.81 std::set<int>::upper_bound(key)_(non-existent)/1024 16.76 7.38 -9.38 -55.98 std::set<int>::upper_bound(key)_(non-existent)/32 4.58 3.10 -1.48 -32.31 std::set<int>::upper_bound(key)_(non-existent)/8192 26.95 10.70 -16.25 -60.29 std::set<std::string>::lower_bound(key)_(existent)/0 0.04 0.04 0.00 0.02 std::set<std::string>::lower_bound(key)_(existent)/1024 28.08 27.04 -1.04 -3.71 std::set<std::string>::lower_bound(key)_(existent)/32 17.53 16.94 -0.58 -3.34 std::set<std::string>::lower_bound(key)_(existent)/8192 32.79 33.28 0.49 1.49 std::set<std::string>::lower_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -0.06 std::set<std::string>::lower_bound(key)_(non-existent)/1024 25.23 24.38 -0.85 -3.38 std::set<std::string>::lower_bound(key)_(non-existent)/32 11.45 11.68 0.24 2.07 std::set<std::string>::lower_bound(key)_(non-existent)/8192 32.30 36.80 4.50 13.95 std::set<std::string>::upper_bound(key)_(existent)/0 0.04 0.04 -0.00 -0.14 std::set<std::string>::upper_bound(key)_(existent)/1024 26.71 26.37 -0.34 -1.27 std::set<std::string>::upper_bound(key)_(existent)/32 20.07 19.06 -1.02 -5.06 std::set<std::string>::upper_bound(key)_(existent)/8192 36.69 35.50 -1.19 -3.25 std::set<std::string>::upper_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -0.16 std::set<std::string>::upper_bound(key)_(non-existent)/1024 24.48 24.90 0.42 1.73 std::set<std::string>::upper_bound(key)_(non-existent)/32 11.68 11.77 0.09 0.77 std::set<std::string>::upper_bound(key)_(non-existent)/8192 33.16 34.12 0.96 2.89 ```
2025-10-08 22:45:03 -08:00
} else {
return __rt->__right_ ? std::__static_fancy_pointer_cast<__end_node_pointer>(std::__tree_min(__rt->__right_))
: __result;
[libc++] Optimize {set,map}::{lower,upper}_bound (#161366) Apple M4: ``` Benchmark Baseline Candidate Difference % Difference --------------------------------------------------------------------------------------------- ---------- ----------- ------------ -------------- std::map<int,_int>::lower_bound(key)_(existent)/0 0.01 0.01 -0.00 -25.78 std::map<int,_int>::lower_bound(key)_(existent)/1024 7.94 4.28 -3.66 -46.09 std::map<int,_int>::lower_bound(key)_(existent)/32 2.73 1.69 -1.03 -37.89 std::map<int,_int>::lower_bound(key)_(existent)/8192 11.63 5.52 -6.11 -52.55 std::map<int,_int>::lower_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -1.35 std::map<int,_int>::lower_bound(key)_(non-existent)/1024 17.21 7.63 -9.58 -55.67 std::map<int,_int>::lower_bound(key)_(non-existent)/32 4.71 3.26 -1.45 -30.71 std::map<int,_int>::lower_bound(key)_(non-existent)/8192 26.82 10.58 -16.24 -60.55 std::map<int,_int>::upper_bound(key)_(existent)/0 0.01 0.01 0.00 20.62 std::map<int,_int>::upper_bound(key)_(existent)/1024 7.93 3.61 -4.32 -54.49 std::map<int,_int>::upper_bound(key)_(existent)/32 2.83 1.98 -0.85 -30.01 std::map<int,_int>::upper_bound(key)_(existent)/8192 11.69 5.72 -5.97 -51.06 std::map<int,_int>::upper_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -1.36 std::map<int,_int>::upper_bound(key)_(non-existent)/1024 17.21 8.00 -9.21 -53.53 std::map<int,_int>::upper_bound(key)_(non-existent)/32 4.70 2.93 -1.78 -37.76 std::map<int,_int>::upper_bound(key)_(non-existent)/8192 26.54 11.18 -15.36 -57.89 std::map<std::string,_int>::lower_bound(key)_(existent)/0 0.04 0.04 -0.00 -3.26 std::map<std::string,_int>::lower_bound(key)_(existent)/1024 27.46 26.25 -1.22 -4.43 std::map<std::string,_int>::lower_bound(key)_(existent)/32 19.17 15.71 -3.46 -18.07 std::map<std::string,_int>::lower_bound(key)_(existent)/8192 35.33 35.03 -0.30 -0.84 std::map<std::string,_int>::lower_bound(key)_(non-existent)/0 0.27 0.27 -0.00 -1.45 std::map<std::string,_int>::lower_bound(key)_(non-existent)/1024 24.88 24.17 -0.70 -2.83 std::map<std::string,_int>::lower_bound(key)_(non-existent)/32 11.67 11.63 -0.04 -0.32 std::map<std::string,_int>::lower_bound(key)_(non-existent)/8192 31.81 32.33 0.52 1.64 std::map<std::string,_int>::upper_bound(key)_(existent)/0 0.04 0.04 -0.00 -2.22 std::map<std::string,_int>::upper_bound(key)_(existent)/1024 29.91 26.51 -3.40 -11.38 std::map<std::string,_int>::upper_bound(key)_(existent)/32 19.69 17.74 -1.95 -9.92 std::map<std::string,_int>::upper_bound(key)_(existent)/8192 32.55 35.24 2.69 8.25 std::map<std::string,_int>::upper_bound(key)_(non-existent)/0 0.27 0.27 -0.00 -1.74 std::map<std::string,_int>::upper_bound(key)_(non-existent)/1024 23.87 26.77 2.91 12.18 std::map<std::string,_int>::upper_bound(key)_(non-existent)/32 11.44 11.81 0.37 3.24 std::map<std::string,_int>::upper_bound(key)_(non-existent)/8192 33.02 32.59 -0.43 -1.29 std::set<int>::lower_bound(key)_(existent)/0 0.01 0.01 0.00 0.48 std::set<int>::lower_bound(key)_(existent)/1024 7.83 4.21 -3.62 -46.23 std::set<int>::lower_bound(key)_(existent)/32 2.74 1.68 -1.06 -38.81 std::set<int>::lower_bound(key)_(existent)/8192 22.75 11.12 -11.63 -51.12 std::set<int>::lower_bound(key)_(non-existent)/0 0.28 0.27 -0.01 -3.52 std::set<int>::lower_bound(key)_(non-existent)/1024 17.15 8.40 -8.75 -51.03 std::set<int>::lower_bound(key)_(non-existent)/32 4.63 2.50 -2.13 -46.03 std::set<int>::lower_bound(key)_(non-existent)/8192 28.88 11.05 -17.82 -61.72 std::set<int>::upper_bound(key)_(existent)/0 0.01 0.01 -0.00 -7.79 std::set<int>::upper_bound(key)_(existent)/1024 7.80 3.63 -4.16 -53.42 std::set<int>::upper_bound(key)_(existent)/32 2.81 1.90 -0.91 -32.44 std::set<int>::upper_bound(key)_(existent)/8192 21.93 11.35 -10.58 -48.26 std::set<int>::upper_bound(key)_(non-existent)/0 0.28 0.27 -0.01 -3.81 std::set<int>::upper_bound(key)_(non-existent)/1024 16.76 7.38 -9.38 -55.98 std::set<int>::upper_bound(key)_(non-existent)/32 4.58 3.10 -1.48 -32.31 std::set<int>::upper_bound(key)_(non-existent)/8192 26.95 10.70 -16.25 -60.29 std::set<std::string>::lower_bound(key)_(existent)/0 0.04 0.04 0.00 0.02 std::set<std::string>::lower_bound(key)_(existent)/1024 28.08 27.04 -1.04 -3.71 std::set<std::string>::lower_bound(key)_(existent)/32 17.53 16.94 -0.58 -3.34 std::set<std::string>::lower_bound(key)_(existent)/8192 32.79 33.28 0.49 1.49 std::set<std::string>::lower_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -0.06 std::set<std::string>::lower_bound(key)_(non-existent)/1024 25.23 24.38 -0.85 -3.38 std::set<std::string>::lower_bound(key)_(non-existent)/32 11.45 11.68 0.24 2.07 std::set<std::string>::lower_bound(key)_(non-existent)/8192 32.30 36.80 4.50 13.95 std::set<std::string>::upper_bound(key)_(existent)/0 0.04 0.04 -0.00 -0.14 std::set<std::string>::upper_bound(key)_(existent)/1024 26.71 26.37 -0.34 -1.27 std::set<std::string>::upper_bound(key)_(existent)/32 20.07 19.06 -1.02 -5.06 std::set<std::string>::upper_bound(key)_(existent)/8192 36.69 35.50 -1.19 -3.25 std::set<std::string>::upper_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -0.16 std::set<std::string>::upper_bound(key)_(non-existent)/1024 24.48 24.90 0.42 1.73 std::set<std::string>::upper_bound(key)_(non-existent)/32 11.68 11.77 0.09 0.77 std::set<std::string>::upper_bound(key)_(non-existent)/8192 33.16 34.12 0.96 2.89 ```
2025-10-08 22:45:03 -08:00
}
}
return __result;
}
// Compatibility escape hatch for comparators that are not strict weak orderings. This
// can be removed for the LLVM 23 release.
#if defined(_LIBCPP_ENABLE_LEGACY_TREE_LOWER_UPPER_BOUND)
template <class _Key>
_LIBCPP_HIDE_FROM_ABI __end_node_pointer __lower_bound_unique_compat_impl(const _Key& __v) const {
auto __rt = __root();
auto __result = __end_node();
while (__rt != nullptr) {
if (!value_comp()(__rt->__get_value(), __v)) {
__result = std::__static_fancy_pointer_cast<__end_node_pointer>(__rt);
__rt = std::__static_fancy_pointer_cast<__node_pointer>(__rt->__left_);
} else {
__rt = std::__static_fancy_pointer_cast<__node_pointer>(__rt->__right_);
}
}
return __result;
}
template <class _Key>
_LIBCPP_HIDE_FROM_ABI __end_node_pointer __upper_bound_unique_compat_impl(const _Key& __v) const {
auto __rt = __root();
auto __result = __end_node();
while (__rt != nullptr) {
if (value_comp()(__v, __rt->__get_value())) {
__result = std::__static_fancy_pointer_cast<__end_node_pointer>(__rt);
__rt = std::__static_fancy_pointer_cast<__node_pointer>(__rt->__left_);
} else {
__rt = std::__static_fancy_pointer_cast<__node_pointer>(__rt->__right_);
}
}
return __result;
}
#endif // _LIBCPP_ENABLE_LEGACY_TREE_LOWER_UPPER_BOUND
[libc++] Optimize {set,map}::{lower,upper}_bound (#161366) Apple M4: ``` Benchmark Baseline Candidate Difference % Difference --------------------------------------------------------------------------------------------- ---------- ----------- ------------ -------------- std::map<int,_int>::lower_bound(key)_(existent)/0 0.01 0.01 -0.00 -25.78 std::map<int,_int>::lower_bound(key)_(existent)/1024 7.94 4.28 -3.66 -46.09 std::map<int,_int>::lower_bound(key)_(existent)/32 2.73 1.69 -1.03 -37.89 std::map<int,_int>::lower_bound(key)_(existent)/8192 11.63 5.52 -6.11 -52.55 std::map<int,_int>::lower_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -1.35 std::map<int,_int>::lower_bound(key)_(non-existent)/1024 17.21 7.63 -9.58 -55.67 std::map<int,_int>::lower_bound(key)_(non-existent)/32 4.71 3.26 -1.45 -30.71 std::map<int,_int>::lower_bound(key)_(non-existent)/8192 26.82 10.58 -16.24 -60.55 std::map<int,_int>::upper_bound(key)_(existent)/0 0.01 0.01 0.00 20.62 std::map<int,_int>::upper_bound(key)_(existent)/1024 7.93 3.61 -4.32 -54.49 std::map<int,_int>::upper_bound(key)_(existent)/32 2.83 1.98 -0.85 -30.01 std::map<int,_int>::upper_bound(key)_(existent)/8192 11.69 5.72 -5.97 -51.06 std::map<int,_int>::upper_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -1.36 std::map<int,_int>::upper_bound(key)_(non-existent)/1024 17.21 8.00 -9.21 -53.53 std::map<int,_int>::upper_bound(key)_(non-existent)/32 4.70 2.93 -1.78 -37.76 std::map<int,_int>::upper_bound(key)_(non-existent)/8192 26.54 11.18 -15.36 -57.89 std::map<std::string,_int>::lower_bound(key)_(existent)/0 0.04 0.04 -0.00 -3.26 std::map<std::string,_int>::lower_bound(key)_(existent)/1024 27.46 26.25 -1.22 -4.43 std::map<std::string,_int>::lower_bound(key)_(existent)/32 19.17 15.71 -3.46 -18.07 std::map<std::string,_int>::lower_bound(key)_(existent)/8192 35.33 35.03 -0.30 -0.84 std::map<std::string,_int>::lower_bound(key)_(non-existent)/0 0.27 0.27 -0.00 -1.45 std::map<std::string,_int>::lower_bound(key)_(non-existent)/1024 24.88 24.17 -0.70 -2.83 std::map<std::string,_int>::lower_bound(key)_(non-existent)/32 11.67 11.63 -0.04 -0.32 std::map<std::string,_int>::lower_bound(key)_(non-existent)/8192 31.81 32.33 0.52 1.64 std::map<std::string,_int>::upper_bound(key)_(existent)/0 0.04 0.04 -0.00 -2.22 std::map<std::string,_int>::upper_bound(key)_(existent)/1024 29.91 26.51 -3.40 -11.38 std::map<std::string,_int>::upper_bound(key)_(existent)/32 19.69 17.74 -1.95 -9.92 std::map<std::string,_int>::upper_bound(key)_(existent)/8192 32.55 35.24 2.69 8.25 std::map<std::string,_int>::upper_bound(key)_(non-existent)/0 0.27 0.27 -0.00 -1.74 std::map<std::string,_int>::upper_bound(key)_(non-existent)/1024 23.87 26.77 2.91 12.18 std::map<std::string,_int>::upper_bound(key)_(non-existent)/32 11.44 11.81 0.37 3.24 std::map<std::string,_int>::upper_bound(key)_(non-existent)/8192 33.02 32.59 -0.43 -1.29 std::set<int>::lower_bound(key)_(existent)/0 0.01 0.01 0.00 0.48 std::set<int>::lower_bound(key)_(existent)/1024 7.83 4.21 -3.62 -46.23 std::set<int>::lower_bound(key)_(existent)/32 2.74 1.68 -1.06 -38.81 std::set<int>::lower_bound(key)_(existent)/8192 22.75 11.12 -11.63 -51.12 std::set<int>::lower_bound(key)_(non-existent)/0 0.28 0.27 -0.01 -3.52 std::set<int>::lower_bound(key)_(non-existent)/1024 17.15 8.40 -8.75 -51.03 std::set<int>::lower_bound(key)_(non-existent)/32 4.63 2.50 -2.13 -46.03 std::set<int>::lower_bound(key)_(non-existent)/8192 28.88 11.05 -17.82 -61.72 std::set<int>::upper_bound(key)_(existent)/0 0.01 0.01 -0.00 -7.79 std::set<int>::upper_bound(key)_(existent)/1024 7.80 3.63 -4.16 -53.42 std::set<int>::upper_bound(key)_(existent)/32 2.81 1.90 -0.91 -32.44 std::set<int>::upper_bound(key)_(existent)/8192 21.93 11.35 -10.58 -48.26 std::set<int>::upper_bound(key)_(non-existent)/0 0.28 0.27 -0.01 -3.81 std::set<int>::upper_bound(key)_(non-existent)/1024 16.76 7.38 -9.38 -55.98 std::set<int>::upper_bound(key)_(non-existent)/32 4.58 3.10 -1.48 -32.31 std::set<int>::upper_bound(key)_(non-existent)/8192 26.95 10.70 -16.25 -60.29 std::set<std::string>::lower_bound(key)_(existent)/0 0.04 0.04 0.00 0.02 std::set<std::string>::lower_bound(key)_(existent)/1024 28.08 27.04 -1.04 -3.71 std::set<std::string>::lower_bound(key)_(existent)/32 17.53 16.94 -0.58 -3.34 std::set<std::string>::lower_bound(key)_(existent)/8192 32.79 33.28 0.49 1.49 std::set<std::string>::lower_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -0.06 std::set<std::string>::lower_bound(key)_(non-existent)/1024 25.23 24.38 -0.85 -3.38 std::set<std::string>::lower_bound(key)_(non-existent)/32 11.45 11.68 0.24 2.07 std::set<std::string>::lower_bound(key)_(non-existent)/8192 32.30 36.80 4.50 13.95 std::set<std::string>::upper_bound(key)_(existent)/0 0.04 0.04 -0.00 -0.14 std::set<std::string>::upper_bound(key)_(existent)/1024 26.71 26.37 -0.34 -1.27 std::set<std::string>::upper_bound(key)_(existent)/32 20.07 19.06 -1.02 -5.06 std::set<std::string>::upper_bound(key)_(existent)/8192 36.69 35.50 -1.19 -3.25 std::set<std::string>::upper_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -0.16 std::set<std::string>::upper_bound(key)_(non-existent)/1024 24.48 24.90 0.42 1.73 std::set<std::string>::upper_bound(key)_(non-existent)/32 11.68 11.77 0.09 0.77 std::set<std::string>::upper_bound(key)_(non-existent)/8192 33.16 34.12 0.96 2.89 ```
2025-10-08 22:45:03 -08:00
template <class _Key>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator __lower_bound_unique(const _Key& __v) {
#if defined(_LIBCPP_ENABLE_LEGACY_TREE_LOWER_UPPER_BOUND)
return iterator(__lower_bound_unique_compat_impl(__v));
#else
[libc++] Optimize {set,map}::{lower,upper}_bound (#161366) Apple M4: ``` Benchmark Baseline Candidate Difference % Difference --------------------------------------------------------------------------------------------- ---------- ----------- ------------ -------------- std::map<int,_int>::lower_bound(key)_(existent)/0 0.01 0.01 -0.00 -25.78 std::map<int,_int>::lower_bound(key)_(existent)/1024 7.94 4.28 -3.66 -46.09 std::map<int,_int>::lower_bound(key)_(existent)/32 2.73 1.69 -1.03 -37.89 std::map<int,_int>::lower_bound(key)_(existent)/8192 11.63 5.52 -6.11 -52.55 std::map<int,_int>::lower_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -1.35 std::map<int,_int>::lower_bound(key)_(non-existent)/1024 17.21 7.63 -9.58 -55.67 std::map<int,_int>::lower_bound(key)_(non-existent)/32 4.71 3.26 -1.45 -30.71 std::map<int,_int>::lower_bound(key)_(non-existent)/8192 26.82 10.58 -16.24 -60.55 std::map<int,_int>::upper_bound(key)_(existent)/0 0.01 0.01 0.00 20.62 std::map<int,_int>::upper_bound(key)_(existent)/1024 7.93 3.61 -4.32 -54.49 std::map<int,_int>::upper_bound(key)_(existent)/32 2.83 1.98 -0.85 -30.01 std::map<int,_int>::upper_bound(key)_(existent)/8192 11.69 5.72 -5.97 -51.06 std::map<int,_int>::upper_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -1.36 std::map<int,_int>::upper_bound(key)_(non-existent)/1024 17.21 8.00 -9.21 -53.53 std::map<int,_int>::upper_bound(key)_(non-existent)/32 4.70 2.93 -1.78 -37.76 std::map<int,_int>::upper_bound(key)_(non-existent)/8192 26.54 11.18 -15.36 -57.89 std::map<std::string,_int>::lower_bound(key)_(existent)/0 0.04 0.04 -0.00 -3.26 std::map<std::string,_int>::lower_bound(key)_(existent)/1024 27.46 26.25 -1.22 -4.43 std::map<std::string,_int>::lower_bound(key)_(existent)/32 19.17 15.71 -3.46 -18.07 std::map<std::string,_int>::lower_bound(key)_(existent)/8192 35.33 35.03 -0.30 -0.84 std::map<std::string,_int>::lower_bound(key)_(non-existent)/0 0.27 0.27 -0.00 -1.45 std::map<std::string,_int>::lower_bound(key)_(non-existent)/1024 24.88 24.17 -0.70 -2.83 std::map<std::string,_int>::lower_bound(key)_(non-existent)/32 11.67 11.63 -0.04 -0.32 std::map<std::string,_int>::lower_bound(key)_(non-existent)/8192 31.81 32.33 0.52 1.64 std::map<std::string,_int>::upper_bound(key)_(existent)/0 0.04 0.04 -0.00 -2.22 std::map<std::string,_int>::upper_bound(key)_(existent)/1024 29.91 26.51 -3.40 -11.38 std::map<std::string,_int>::upper_bound(key)_(existent)/32 19.69 17.74 -1.95 -9.92 std::map<std::string,_int>::upper_bound(key)_(existent)/8192 32.55 35.24 2.69 8.25 std::map<std::string,_int>::upper_bound(key)_(non-existent)/0 0.27 0.27 -0.00 -1.74 std::map<std::string,_int>::upper_bound(key)_(non-existent)/1024 23.87 26.77 2.91 12.18 std::map<std::string,_int>::upper_bound(key)_(non-existent)/32 11.44 11.81 0.37 3.24 std::map<std::string,_int>::upper_bound(key)_(non-existent)/8192 33.02 32.59 -0.43 -1.29 std::set<int>::lower_bound(key)_(existent)/0 0.01 0.01 0.00 0.48 std::set<int>::lower_bound(key)_(existent)/1024 7.83 4.21 -3.62 -46.23 std::set<int>::lower_bound(key)_(existent)/32 2.74 1.68 -1.06 -38.81 std::set<int>::lower_bound(key)_(existent)/8192 22.75 11.12 -11.63 -51.12 std::set<int>::lower_bound(key)_(non-existent)/0 0.28 0.27 -0.01 -3.52 std::set<int>::lower_bound(key)_(non-existent)/1024 17.15 8.40 -8.75 -51.03 std::set<int>::lower_bound(key)_(non-existent)/32 4.63 2.50 -2.13 -46.03 std::set<int>::lower_bound(key)_(non-existent)/8192 28.88 11.05 -17.82 -61.72 std::set<int>::upper_bound(key)_(existent)/0 0.01 0.01 -0.00 -7.79 std::set<int>::upper_bound(key)_(existent)/1024 7.80 3.63 -4.16 -53.42 std::set<int>::upper_bound(key)_(existent)/32 2.81 1.90 -0.91 -32.44 std::set<int>::upper_bound(key)_(existent)/8192 21.93 11.35 -10.58 -48.26 std::set<int>::upper_bound(key)_(non-existent)/0 0.28 0.27 -0.01 -3.81 std::set<int>::upper_bound(key)_(non-existent)/1024 16.76 7.38 -9.38 -55.98 std::set<int>::upper_bound(key)_(non-existent)/32 4.58 3.10 -1.48 -32.31 std::set<int>::upper_bound(key)_(non-existent)/8192 26.95 10.70 -16.25 -60.29 std::set<std::string>::lower_bound(key)_(existent)/0 0.04 0.04 0.00 0.02 std::set<std::string>::lower_bound(key)_(existent)/1024 28.08 27.04 -1.04 -3.71 std::set<std::string>::lower_bound(key)_(existent)/32 17.53 16.94 -0.58 -3.34 std::set<std::string>::lower_bound(key)_(existent)/8192 32.79 33.28 0.49 1.49 std::set<std::string>::lower_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -0.06 std::set<std::string>::lower_bound(key)_(non-existent)/1024 25.23 24.38 -0.85 -3.38 std::set<std::string>::lower_bound(key)_(non-existent)/32 11.45 11.68 0.24 2.07 std::set<std::string>::lower_bound(key)_(non-existent)/8192 32.30 36.80 4.50 13.95 std::set<std::string>::upper_bound(key)_(existent)/0 0.04 0.04 -0.00 -0.14 std::set<std::string>::upper_bound(key)_(existent)/1024 26.71 26.37 -0.34 -1.27 std::set<std::string>::upper_bound(key)_(existent)/32 20.07 19.06 -1.02 -5.06 std::set<std::string>::upper_bound(key)_(existent)/8192 36.69 35.50 -1.19 -3.25 std::set<std::string>::upper_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -0.16 std::set<std::string>::upper_bound(key)_(non-existent)/1024 24.48 24.90 0.42 1.73 std::set<std::string>::upper_bound(key)_(non-existent)/32 11.68 11.77 0.09 0.77 std::set<std::string>::upper_bound(key)_(non-existent)/8192 33.16 34.12 0.96 2.89 ```
2025-10-08 22:45:03 -08:00
return iterator(__lower_upper_bound_unique_impl<true>(__v));
#endif
[libc++] Optimize {set,map}::{lower,upper}_bound (#161366) Apple M4: ``` Benchmark Baseline Candidate Difference % Difference --------------------------------------------------------------------------------------------- ---------- ----------- ------------ -------------- std::map<int,_int>::lower_bound(key)_(existent)/0 0.01 0.01 -0.00 -25.78 std::map<int,_int>::lower_bound(key)_(existent)/1024 7.94 4.28 -3.66 -46.09 std::map<int,_int>::lower_bound(key)_(existent)/32 2.73 1.69 -1.03 -37.89 std::map<int,_int>::lower_bound(key)_(existent)/8192 11.63 5.52 -6.11 -52.55 std::map<int,_int>::lower_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -1.35 std::map<int,_int>::lower_bound(key)_(non-existent)/1024 17.21 7.63 -9.58 -55.67 std::map<int,_int>::lower_bound(key)_(non-existent)/32 4.71 3.26 -1.45 -30.71 std::map<int,_int>::lower_bound(key)_(non-existent)/8192 26.82 10.58 -16.24 -60.55 std::map<int,_int>::upper_bound(key)_(existent)/0 0.01 0.01 0.00 20.62 std::map<int,_int>::upper_bound(key)_(existent)/1024 7.93 3.61 -4.32 -54.49 std::map<int,_int>::upper_bound(key)_(existent)/32 2.83 1.98 -0.85 -30.01 std::map<int,_int>::upper_bound(key)_(existent)/8192 11.69 5.72 -5.97 -51.06 std::map<int,_int>::upper_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -1.36 std::map<int,_int>::upper_bound(key)_(non-existent)/1024 17.21 8.00 -9.21 -53.53 std::map<int,_int>::upper_bound(key)_(non-existent)/32 4.70 2.93 -1.78 -37.76 std::map<int,_int>::upper_bound(key)_(non-existent)/8192 26.54 11.18 -15.36 -57.89 std::map<std::string,_int>::lower_bound(key)_(existent)/0 0.04 0.04 -0.00 -3.26 std::map<std::string,_int>::lower_bound(key)_(existent)/1024 27.46 26.25 -1.22 -4.43 std::map<std::string,_int>::lower_bound(key)_(existent)/32 19.17 15.71 -3.46 -18.07 std::map<std::string,_int>::lower_bound(key)_(existent)/8192 35.33 35.03 -0.30 -0.84 std::map<std::string,_int>::lower_bound(key)_(non-existent)/0 0.27 0.27 -0.00 -1.45 std::map<std::string,_int>::lower_bound(key)_(non-existent)/1024 24.88 24.17 -0.70 -2.83 std::map<std::string,_int>::lower_bound(key)_(non-existent)/32 11.67 11.63 -0.04 -0.32 std::map<std::string,_int>::lower_bound(key)_(non-existent)/8192 31.81 32.33 0.52 1.64 std::map<std::string,_int>::upper_bound(key)_(existent)/0 0.04 0.04 -0.00 -2.22 std::map<std::string,_int>::upper_bound(key)_(existent)/1024 29.91 26.51 -3.40 -11.38 std::map<std::string,_int>::upper_bound(key)_(existent)/32 19.69 17.74 -1.95 -9.92 std::map<std::string,_int>::upper_bound(key)_(existent)/8192 32.55 35.24 2.69 8.25 std::map<std::string,_int>::upper_bound(key)_(non-existent)/0 0.27 0.27 -0.00 -1.74 std::map<std::string,_int>::upper_bound(key)_(non-existent)/1024 23.87 26.77 2.91 12.18 std::map<std::string,_int>::upper_bound(key)_(non-existent)/32 11.44 11.81 0.37 3.24 std::map<std::string,_int>::upper_bound(key)_(non-existent)/8192 33.02 32.59 -0.43 -1.29 std::set<int>::lower_bound(key)_(existent)/0 0.01 0.01 0.00 0.48 std::set<int>::lower_bound(key)_(existent)/1024 7.83 4.21 -3.62 -46.23 std::set<int>::lower_bound(key)_(existent)/32 2.74 1.68 -1.06 -38.81 std::set<int>::lower_bound(key)_(existent)/8192 22.75 11.12 -11.63 -51.12 std::set<int>::lower_bound(key)_(non-existent)/0 0.28 0.27 -0.01 -3.52 std::set<int>::lower_bound(key)_(non-existent)/1024 17.15 8.40 -8.75 -51.03 std::set<int>::lower_bound(key)_(non-existent)/32 4.63 2.50 -2.13 -46.03 std::set<int>::lower_bound(key)_(non-existent)/8192 28.88 11.05 -17.82 -61.72 std::set<int>::upper_bound(key)_(existent)/0 0.01 0.01 -0.00 -7.79 std::set<int>::upper_bound(key)_(existent)/1024 7.80 3.63 -4.16 -53.42 std::set<int>::upper_bound(key)_(existent)/32 2.81 1.90 -0.91 -32.44 std::set<int>::upper_bound(key)_(existent)/8192 21.93 11.35 -10.58 -48.26 std::set<int>::upper_bound(key)_(non-existent)/0 0.28 0.27 -0.01 -3.81 std::set<int>::upper_bound(key)_(non-existent)/1024 16.76 7.38 -9.38 -55.98 std::set<int>::upper_bound(key)_(non-existent)/32 4.58 3.10 -1.48 -32.31 std::set<int>::upper_bound(key)_(non-existent)/8192 26.95 10.70 -16.25 -60.29 std::set<std::string>::lower_bound(key)_(existent)/0 0.04 0.04 0.00 0.02 std::set<std::string>::lower_bound(key)_(existent)/1024 28.08 27.04 -1.04 -3.71 std::set<std::string>::lower_bound(key)_(existent)/32 17.53 16.94 -0.58 -3.34 std::set<std::string>::lower_bound(key)_(existent)/8192 32.79 33.28 0.49 1.49 std::set<std::string>::lower_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -0.06 std::set<std::string>::lower_bound(key)_(non-existent)/1024 25.23 24.38 -0.85 -3.38 std::set<std::string>::lower_bound(key)_(non-existent)/32 11.45 11.68 0.24 2.07 std::set<std::string>::lower_bound(key)_(non-existent)/8192 32.30 36.80 4.50 13.95 std::set<std::string>::upper_bound(key)_(existent)/0 0.04 0.04 -0.00 -0.14 std::set<std::string>::upper_bound(key)_(existent)/1024 26.71 26.37 -0.34 -1.27 std::set<std::string>::upper_bound(key)_(existent)/32 20.07 19.06 -1.02 -5.06 std::set<std::string>::upper_bound(key)_(existent)/8192 36.69 35.50 -1.19 -3.25 std::set<std::string>::upper_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -0.16 std::set<std::string>::upper_bound(key)_(non-existent)/1024 24.48 24.90 0.42 1.73 std::set<std::string>::upper_bound(key)_(non-existent)/32 11.68 11.77 0.09 0.77 std::set<std::string>::upper_bound(key)_(non-existent)/8192 33.16 34.12 0.96 2.89 ```
2025-10-08 22:45:03 -08:00
}
2010-05-11 11:42:16 -08:00
template <class _Key>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator __lower_bound_unique(const _Key& __v) const {
#if defined(_LIBCPP_ENABLE_LEGACY_TREE_LOWER_UPPER_BOUND)
return const_iterator(__lower_bound_unique_compat_impl(__v));
#else
[libc++] Optimize {set,map}::{lower,upper}_bound (#161366) Apple M4: ``` Benchmark Baseline Candidate Difference % Difference --------------------------------------------------------------------------------------------- ---------- ----------- ------------ -------------- std::map<int,_int>::lower_bound(key)_(existent)/0 0.01 0.01 -0.00 -25.78 std::map<int,_int>::lower_bound(key)_(existent)/1024 7.94 4.28 -3.66 -46.09 std::map<int,_int>::lower_bound(key)_(existent)/32 2.73 1.69 -1.03 -37.89 std::map<int,_int>::lower_bound(key)_(existent)/8192 11.63 5.52 -6.11 -52.55 std::map<int,_int>::lower_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -1.35 std::map<int,_int>::lower_bound(key)_(non-existent)/1024 17.21 7.63 -9.58 -55.67 std::map<int,_int>::lower_bound(key)_(non-existent)/32 4.71 3.26 -1.45 -30.71 std::map<int,_int>::lower_bound(key)_(non-existent)/8192 26.82 10.58 -16.24 -60.55 std::map<int,_int>::upper_bound(key)_(existent)/0 0.01 0.01 0.00 20.62 std::map<int,_int>::upper_bound(key)_(existent)/1024 7.93 3.61 -4.32 -54.49 std::map<int,_int>::upper_bound(key)_(existent)/32 2.83 1.98 -0.85 -30.01 std::map<int,_int>::upper_bound(key)_(existent)/8192 11.69 5.72 -5.97 -51.06 std::map<int,_int>::upper_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -1.36 std::map<int,_int>::upper_bound(key)_(non-existent)/1024 17.21 8.00 -9.21 -53.53 std::map<int,_int>::upper_bound(key)_(non-existent)/32 4.70 2.93 -1.78 -37.76 std::map<int,_int>::upper_bound(key)_(non-existent)/8192 26.54 11.18 -15.36 -57.89 std::map<std::string,_int>::lower_bound(key)_(existent)/0 0.04 0.04 -0.00 -3.26 std::map<std::string,_int>::lower_bound(key)_(existent)/1024 27.46 26.25 -1.22 -4.43 std::map<std::string,_int>::lower_bound(key)_(existent)/32 19.17 15.71 -3.46 -18.07 std::map<std::string,_int>::lower_bound(key)_(existent)/8192 35.33 35.03 -0.30 -0.84 std::map<std::string,_int>::lower_bound(key)_(non-existent)/0 0.27 0.27 -0.00 -1.45 std::map<std::string,_int>::lower_bound(key)_(non-existent)/1024 24.88 24.17 -0.70 -2.83 std::map<std::string,_int>::lower_bound(key)_(non-existent)/32 11.67 11.63 -0.04 -0.32 std::map<std::string,_int>::lower_bound(key)_(non-existent)/8192 31.81 32.33 0.52 1.64 std::map<std::string,_int>::upper_bound(key)_(existent)/0 0.04 0.04 -0.00 -2.22 std::map<std::string,_int>::upper_bound(key)_(existent)/1024 29.91 26.51 -3.40 -11.38 std::map<std::string,_int>::upper_bound(key)_(existent)/32 19.69 17.74 -1.95 -9.92 std::map<std::string,_int>::upper_bound(key)_(existent)/8192 32.55 35.24 2.69 8.25 std::map<std::string,_int>::upper_bound(key)_(non-existent)/0 0.27 0.27 -0.00 -1.74 std::map<std::string,_int>::upper_bound(key)_(non-existent)/1024 23.87 26.77 2.91 12.18 std::map<std::string,_int>::upper_bound(key)_(non-existent)/32 11.44 11.81 0.37 3.24 std::map<std::string,_int>::upper_bound(key)_(non-existent)/8192 33.02 32.59 -0.43 -1.29 std::set<int>::lower_bound(key)_(existent)/0 0.01 0.01 0.00 0.48 std::set<int>::lower_bound(key)_(existent)/1024 7.83 4.21 -3.62 -46.23 std::set<int>::lower_bound(key)_(existent)/32 2.74 1.68 -1.06 -38.81 std::set<int>::lower_bound(key)_(existent)/8192 22.75 11.12 -11.63 -51.12 std::set<int>::lower_bound(key)_(non-existent)/0 0.28 0.27 -0.01 -3.52 std::set<int>::lower_bound(key)_(non-existent)/1024 17.15 8.40 -8.75 -51.03 std::set<int>::lower_bound(key)_(non-existent)/32 4.63 2.50 -2.13 -46.03 std::set<int>::lower_bound(key)_(non-existent)/8192 28.88 11.05 -17.82 -61.72 std::set<int>::upper_bound(key)_(existent)/0 0.01 0.01 -0.00 -7.79 std::set<int>::upper_bound(key)_(existent)/1024 7.80 3.63 -4.16 -53.42 std::set<int>::upper_bound(key)_(existent)/32 2.81 1.90 -0.91 -32.44 std::set<int>::upper_bound(key)_(existent)/8192 21.93 11.35 -10.58 -48.26 std::set<int>::upper_bound(key)_(non-existent)/0 0.28 0.27 -0.01 -3.81 std::set<int>::upper_bound(key)_(non-existent)/1024 16.76 7.38 -9.38 -55.98 std::set<int>::upper_bound(key)_(non-existent)/32 4.58 3.10 -1.48 -32.31 std::set<int>::upper_bound(key)_(non-existent)/8192 26.95 10.70 -16.25 -60.29 std::set<std::string>::lower_bound(key)_(existent)/0 0.04 0.04 0.00 0.02 std::set<std::string>::lower_bound(key)_(existent)/1024 28.08 27.04 -1.04 -3.71 std::set<std::string>::lower_bound(key)_(existent)/32 17.53 16.94 -0.58 -3.34 std::set<std::string>::lower_bound(key)_(existent)/8192 32.79 33.28 0.49 1.49 std::set<std::string>::lower_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -0.06 std::set<std::string>::lower_bound(key)_(non-existent)/1024 25.23 24.38 -0.85 -3.38 std::set<std::string>::lower_bound(key)_(non-existent)/32 11.45 11.68 0.24 2.07 std::set<std::string>::lower_bound(key)_(non-existent)/8192 32.30 36.80 4.50 13.95 std::set<std::string>::upper_bound(key)_(existent)/0 0.04 0.04 -0.00 -0.14 std::set<std::string>::upper_bound(key)_(existent)/1024 26.71 26.37 -0.34 -1.27 std::set<std::string>::upper_bound(key)_(existent)/32 20.07 19.06 -1.02 -5.06 std::set<std::string>::upper_bound(key)_(existent)/8192 36.69 35.50 -1.19 -3.25 std::set<std::string>::upper_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -0.16 std::set<std::string>::upper_bound(key)_(non-existent)/1024 24.48 24.90 0.42 1.73 std::set<std::string>::upper_bound(key)_(non-existent)/32 11.68 11.77 0.09 0.77 std::set<std::string>::upper_bound(key)_(non-existent)/8192 33.16 34.12 0.96 2.89 ```
2025-10-08 22:45:03 -08:00
return const_iterator(__lower_upper_bound_unique_impl<true>(__v));
#endif
2010-05-11 11:42:16 -08:00
}
[libc++] Optimize {set,map}::{lower,upper}_bound (#161366) Apple M4: ``` Benchmark Baseline Candidate Difference % Difference --------------------------------------------------------------------------------------------- ---------- ----------- ------------ -------------- std::map<int,_int>::lower_bound(key)_(existent)/0 0.01 0.01 -0.00 -25.78 std::map<int,_int>::lower_bound(key)_(existent)/1024 7.94 4.28 -3.66 -46.09 std::map<int,_int>::lower_bound(key)_(existent)/32 2.73 1.69 -1.03 -37.89 std::map<int,_int>::lower_bound(key)_(existent)/8192 11.63 5.52 -6.11 -52.55 std::map<int,_int>::lower_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -1.35 std::map<int,_int>::lower_bound(key)_(non-existent)/1024 17.21 7.63 -9.58 -55.67 std::map<int,_int>::lower_bound(key)_(non-existent)/32 4.71 3.26 -1.45 -30.71 std::map<int,_int>::lower_bound(key)_(non-existent)/8192 26.82 10.58 -16.24 -60.55 std::map<int,_int>::upper_bound(key)_(existent)/0 0.01 0.01 0.00 20.62 std::map<int,_int>::upper_bound(key)_(existent)/1024 7.93 3.61 -4.32 -54.49 std::map<int,_int>::upper_bound(key)_(existent)/32 2.83 1.98 -0.85 -30.01 std::map<int,_int>::upper_bound(key)_(existent)/8192 11.69 5.72 -5.97 -51.06 std::map<int,_int>::upper_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -1.36 std::map<int,_int>::upper_bound(key)_(non-existent)/1024 17.21 8.00 -9.21 -53.53 std::map<int,_int>::upper_bound(key)_(non-existent)/32 4.70 2.93 -1.78 -37.76 std::map<int,_int>::upper_bound(key)_(non-existent)/8192 26.54 11.18 -15.36 -57.89 std::map<std::string,_int>::lower_bound(key)_(existent)/0 0.04 0.04 -0.00 -3.26 std::map<std::string,_int>::lower_bound(key)_(existent)/1024 27.46 26.25 -1.22 -4.43 std::map<std::string,_int>::lower_bound(key)_(existent)/32 19.17 15.71 -3.46 -18.07 std::map<std::string,_int>::lower_bound(key)_(existent)/8192 35.33 35.03 -0.30 -0.84 std::map<std::string,_int>::lower_bound(key)_(non-existent)/0 0.27 0.27 -0.00 -1.45 std::map<std::string,_int>::lower_bound(key)_(non-existent)/1024 24.88 24.17 -0.70 -2.83 std::map<std::string,_int>::lower_bound(key)_(non-existent)/32 11.67 11.63 -0.04 -0.32 std::map<std::string,_int>::lower_bound(key)_(non-existent)/8192 31.81 32.33 0.52 1.64 std::map<std::string,_int>::upper_bound(key)_(existent)/0 0.04 0.04 -0.00 -2.22 std::map<std::string,_int>::upper_bound(key)_(existent)/1024 29.91 26.51 -3.40 -11.38 std::map<std::string,_int>::upper_bound(key)_(existent)/32 19.69 17.74 -1.95 -9.92 std::map<std::string,_int>::upper_bound(key)_(existent)/8192 32.55 35.24 2.69 8.25 std::map<std::string,_int>::upper_bound(key)_(non-existent)/0 0.27 0.27 -0.00 -1.74 std::map<std::string,_int>::upper_bound(key)_(non-existent)/1024 23.87 26.77 2.91 12.18 std::map<std::string,_int>::upper_bound(key)_(non-existent)/32 11.44 11.81 0.37 3.24 std::map<std::string,_int>::upper_bound(key)_(non-existent)/8192 33.02 32.59 -0.43 -1.29 std::set<int>::lower_bound(key)_(existent)/0 0.01 0.01 0.00 0.48 std::set<int>::lower_bound(key)_(existent)/1024 7.83 4.21 -3.62 -46.23 std::set<int>::lower_bound(key)_(existent)/32 2.74 1.68 -1.06 -38.81 std::set<int>::lower_bound(key)_(existent)/8192 22.75 11.12 -11.63 -51.12 std::set<int>::lower_bound(key)_(non-existent)/0 0.28 0.27 -0.01 -3.52 std::set<int>::lower_bound(key)_(non-existent)/1024 17.15 8.40 -8.75 -51.03 std::set<int>::lower_bound(key)_(non-existent)/32 4.63 2.50 -2.13 -46.03 std::set<int>::lower_bound(key)_(non-existent)/8192 28.88 11.05 -17.82 -61.72 std::set<int>::upper_bound(key)_(existent)/0 0.01 0.01 -0.00 -7.79 std::set<int>::upper_bound(key)_(existent)/1024 7.80 3.63 -4.16 -53.42 std::set<int>::upper_bound(key)_(existent)/32 2.81 1.90 -0.91 -32.44 std::set<int>::upper_bound(key)_(existent)/8192 21.93 11.35 -10.58 -48.26 std::set<int>::upper_bound(key)_(non-existent)/0 0.28 0.27 -0.01 -3.81 std::set<int>::upper_bound(key)_(non-existent)/1024 16.76 7.38 -9.38 -55.98 std::set<int>::upper_bound(key)_(non-existent)/32 4.58 3.10 -1.48 -32.31 std::set<int>::upper_bound(key)_(non-existent)/8192 26.95 10.70 -16.25 -60.29 std::set<std::string>::lower_bound(key)_(existent)/0 0.04 0.04 0.00 0.02 std::set<std::string>::lower_bound(key)_(existent)/1024 28.08 27.04 -1.04 -3.71 std::set<std::string>::lower_bound(key)_(existent)/32 17.53 16.94 -0.58 -3.34 std::set<std::string>::lower_bound(key)_(existent)/8192 32.79 33.28 0.49 1.49 std::set<std::string>::lower_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -0.06 std::set<std::string>::lower_bound(key)_(non-existent)/1024 25.23 24.38 -0.85 -3.38 std::set<std::string>::lower_bound(key)_(non-existent)/32 11.45 11.68 0.24 2.07 std::set<std::string>::lower_bound(key)_(non-existent)/8192 32.30 36.80 4.50 13.95 std::set<std::string>::upper_bound(key)_(existent)/0 0.04 0.04 -0.00 -0.14 std::set<std::string>::upper_bound(key)_(existent)/1024 26.71 26.37 -0.34 -1.27 std::set<std::string>::upper_bound(key)_(existent)/32 20.07 19.06 -1.02 -5.06 std::set<std::string>::upper_bound(key)_(existent)/8192 36.69 35.50 -1.19 -3.25 std::set<std::string>::upper_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -0.16 std::set<std::string>::upper_bound(key)_(non-existent)/1024 24.48 24.90 0.42 1.73 std::set<std::string>::upper_bound(key)_(non-existent)/32 11.68 11.77 0.09 0.77 std::set<std::string>::upper_bound(key)_(non-existent)/8192 33.16 34.12 0.96 2.89 ```
2025-10-08 22:45:03 -08:00
2010-05-11 11:42:16 -08:00
template <class _Key>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator __upper_bound_unique(const _Key& __v) {
#if defined(_LIBCPP_ENABLE_LEGACY_TREE_LOWER_UPPER_BOUND)
return iterator(__upper_bound_unique_compat_impl(__v));
#else
[libc++] Optimize {set,map}::{lower,upper}_bound (#161366) Apple M4: ``` Benchmark Baseline Candidate Difference % Difference --------------------------------------------------------------------------------------------- ---------- ----------- ------------ -------------- std::map<int,_int>::lower_bound(key)_(existent)/0 0.01 0.01 -0.00 -25.78 std::map<int,_int>::lower_bound(key)_(existent)/1024 7.94 4.28 -3.66 -46.09 std::map<int,_int>::lower_bound(key)_(existent)/32 2.73 1.69 -1.03 -37.89 std::map<int,_int>::lower_bound(key)_(existent)/8192 11.63 5.52 -6.11 -52.55 std::map<int,_int>::lower_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -1.35 std::map<int,_int>::lower_bound(key)_(non-existent)/1024 17.21 7.63 -9.58 -55.67 std::map<int,_int>::lower_bound(key)_(non-existent)/32 4.71 3.26 -1.45 -30.71 std::map<int,_int>::lower_bound(key)_(non-existent)/8192 26.82 10.58 -16.24 -60.55 std::map<int,_int>::upper_bound(key)_(existent)/0 0.01 0.01 0.00 20.62 std::map<int,_int>::upper_bound(key)_(existent)/1024 7.93 3.61 -4.32 -54.49 std::map<int,_int>::upper_bound(key)_(existent)/32 2.83 1.98 -0.85 -30.01 std::map<int,_int>::upper_bound(key)_(existent)/8192 11.69 5.72 -5.97 -51.06 std::map<int,_int>::upper_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -1.36 std::map<int,_int>::upper_bound(key)_(non-existent)/1024 17.21 8.00 -9.21 -53.53 std::map<int,_int>::upper_bound(key)_(non-existent)/32 4.70 2.93 -1.78 -37.76 std::map<int,_int>::upper_bound(key)_(non-existent)/8192 26.54 11.18 -15.36 -57.89 std::map<std::string,_int>::lower_bound(key)_(existent)/0 0.04 0.04 -0.00 -3.26 std::map<std::string,_int>::lower_bound(key)_(existent)/1024 27.46 26.25 -1.22 -4.43 std::map<std::string,_int>::lower_bound(key)_(existent)/32 19.17 15.71 -3.46 -18.07 std::map<std::string,_int>::lower_bound(key)_(existent)/8192 35.33 35.03 -0.30 -0.84 std::map<std::string,_int>::lower_bound(key)_(non-existent)/0 0.27 0.27 -0.00 -1.45 std::map<std::string,_int>::lower_bound(key)_(non-existent)/1024 24.88 24.17 -0.70 -2.83 std::map<std::string,_int>::lower_bound(key)_(non-existent)/32 11.67 11.63 -0.04 -0.32 std::map<std::string,_int>::lower_bound(key)_(non-existent)/8192 31.81 32.33 0.52 1.64 std::map<std::string,_int>::upper_bound(key)_(existent)/0 0.04 0.04 -0.00 -2.22 std::map<std::string,_int>::upper_bound(key)_(existent)/1024 29.91 26.51 -3.40 -11.38 std::map<std::string,_int>::upper_bound(key)_(existent)/32 19.69 17.74 -1.95 -9.92 std::map<std::string,_int>::upper_bound(key)_(existent)/8192 32.55 35.24 2.69 8.25 std::map<std::string,_int>::upper_bound(key)_(non-existent)/0 0.27 0.27 -0.00 -1.74 std::map<std::string,_int>::upper_bound(key)_(non-existent)/1024 23.87 26.77 2.91 12.18 std::map<std::string,_int>::upper_bound(key)_(non-existent)/32 11.44 11.81 0.37 3.24 std::map<std::string,_int>::upper_bound(key)_(non-existent)/8192 33.02 32.59 -0.43 -1.29 std::set<int>::lower_bound(key)_(existent)/0 0.01 0.01 0.00 0.48 std::set<int>::lower_bound(key)_(existent)/1024 7.83 4.21 -3.62 -46.23 std::set<int>::lower_bound(key)_(existent)/32 2.74 1.68 -1.06 -38.81 std::set<int>::lower_bound(key)_(existent)/8192 22.75 11.12 -11.63 -51.12 std::set<int>::lower_bound(key)_(non-existent)/0 0.28 0.27 -0.01 -3.52 std::set<int>::lower_bound(key)_(non-existent)/1024 17.15 8.40 -8.75 -51.03 std::set<int>::lower_bound(key)_(non-existent)/32 4.63 2.50 -2.13 -46.03 std::set<int>::lower_bound(key)_(non-existent)/8192 28.88 11.05 -17.82 -61.72 std::set<int>::upper_bound(key)_(existent)/0 0.01 0.01 -0.00 -7.79 std::set<int>::upper_bound(key)_(existent)/1024 7.80 3.63 -4.16 -53.42 std::set<int>::upper_bound(key)_(existent)/32 2.81 1.90 -0.91 -32.44 std::set<int>::upper_bound(key)_(existent)/8192 21.93 11.35 -10.58 -48.26 std::set<int>::upper_bound(key)_(non-existent)/0 0.28 0.27 -0.01 -3.81 std::set<int>::upper_bound(key)_(non-existent)/1024 16.76 7.38 -9.38 -55.98 std::set<int>::upper_bound(key)_(non-existent)/32 4.58 3.10 -1.48 -32.31 std::set<int>::upper_bound(key)_(non-existent)/8192 26.95 10.70 -16.25 -60.29 std::set<std::string>::lower_bound(key)_(existent)/0 0.04 0.04 0.00 0.02 std::set<std::string>::lower_bound(key)_(existent)/1024 28.08 27.04 -1.04 -3.71 std::set<std::string>::lower_bound(key)_(existent)/32 17.53 16.94 -0.58 -3.34 std::set<std::string>::lower_bound(key)_(existent)/8192 32.79 33.28 0.49 1.49 std::set<std::string>::lower_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -0.06 std::set<std::string>::lower_bound(key)_(non-existent)/1024 25.23 24.38 -0.85 -3.38 std::set<std::string>::lower_bound(key)_(non-existent)/32 11.45 11.68 0.24 2.07 std::set<std::string>::lower_bound(key)_(non-existent)/8192 32.30 36.80 4.50 13.95 std::set<std::string>::upper_bound(key)_(existent)/0 0.04 0.04 -0.00 -0.14 std::set<std::string>::upper_bound(key)_(existent)/1024 26.71 26.37 -0.34 -1.27 std::set<std::string>::upper_bound(key)_(existent)/32 20.07 19.06 -1.02 -5.06 std::set<std::string>::upper_bound(key)_(existent)/8192 36.69 35.50 -1.19 -3.25 std::set<std::string>::upper_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -0.16 std::set<std::string>::upper_bound(key)_(non-existent)/1024 24.48 24.90 0.42 1.73 std::set<std::string>::upper_bound(key)_(non-existent)/32 11.68 11.77 0.09 0.77 std::set<std::string>::upper_bound(key)_(non-existent)/8192 33.16 34.12 0.96 2.89 ```
2025-10-08 22:45:03 -08:00
return iterator(__lower_upper_bound_unique_impl<false>(__v));
#endif
[libc++] Optimize {set,map}::{lower,upper}_bound (#161366) Apple M4: ``` Benchmark Baseline Candidate Difference % Difference --------------------------------------------------------------------------------------------- ---------- ----------- ------------ -------------- std::map<int,_int>::lower_bound(key)_(existent)/0 0.01 0.01 -0.00 -25.78 std::map<int,_int>::lower_bound(key)_(existent)/1024 7.94 4.28 -3.66 -46.09 std::map<int,_int>::lower_bound(key)_(existent)/32 2.73 1.69 -1.03 -37.89 std::map<int,_int>::lower_bound(key)_(existent)/8192 11.63 5.52 -6.11 -52.55 std::map<int,_int>::lower_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -1.35 std::map<int,_int>::lower_bound(key)_(non-existent)/1024 17.21 7.63 -9.58 -55.67 std::map<int,_int>::lower_bound(key)_(non-existent)/32 4.71 3.26 -1.45 -30.71 std::map<int,_int>::lower_bound(key)_(non-existent)/8192 26.82 10.58 -16.24 -60.55 std::map<int,_int>::upper_bound(key)_(existent)/0 0.01 0.01 0.00 20.62 std::map<int,_int>::upper_bound(key)_(existent)/1024 7.93 3.61 -4.32 -54.49 std::map<int,_int>::upper_bound(key)_(existent)/32 2.83 1.98 -0.85 -30.01 std::map<int,_int>::upper_bound(key)_(existent)/8192 11.69 5.72 -5.97 -51.06 std::map<int,_int>::upper_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -1.36 std::map<int,_int>::upper_bound(key)_(non-existent)/1024 17.21 8.00 -9.21 -53.53 std::map<int,_int>::upper_bound(key)_(non-existent)/32 4.70 2.93 -1.78 -37.76 std::map<int,_int>::upper_bound(key)_(non-existent)/8192 26.54 11.18 -15.36 -57.89 std::map<std::string,_int>::lower_bound(key)_(existent)/0 0.04 0.04 -0.00 -3.26 std::map<std::string,_int>::lower_bound(key)_(existent)/1024 27.46 26.25 -1.22 -4.43 std::map<std::string,_int>::lower_bound(key)_(existent)/32 19.17 15.71 -3.46 -18.07 std::map<std::string,_int>::lower_bound(key)_(existent)/8192 35.33 35.03 -0.30 -0.84 std::map<std::string,_int>::lower_bound(key)_(non-existent)/0 0.27 0.27 -0.00 -1.45 std::map<std::string,_int>::lower_bound(key)_(non-existent)/1024 24.88 24.17 -0.70 -2.83 std::map<std::string,_int>::lower_bound(key)_(non-existent)/32 11.67 11.63 -0.04 -0.32 std::map<std::string,_int>::lower_bound(key)_(non-existent)/8192 31.81 32.33 0.52 1.64 std::map<std::string,_int>::upper_bound(key)_(existent)/0 0.04 0.04 -0.00 -2.22 std::map<std::string,_int>::upper_bound(key)_(existent)/1024 29.91 26.51 -3.40 -11.38 std::map<std::string,_int>::upper_bound(key)_(existent)/32 19.69 17.74 -1.95 -9.92 std::map<std::string,_int>::upper_bound(key)_(existent)/8192 32.55 35.24 2.69 8.25 std::map<std::string,_int>::upper_bound(key)_(non-existent)/0 0.27 0.27 -0.00 -1.74 std::map<std::string,_int>::upper_bound(key)_(non-existent)/1024 23.87 26.77 2.91 12.18 std::map<std::string,_int>::upper_bound(key)_(non-existent)/32 11.44 11.81 0.37 3.24 std::map<std::string,_int>::upper_bound(key)_(non-existent)/8192 33.02 32.59 -0.43 -1.29 std::set<int>::lower_bound(key)_(existent)/0 0.01 0.01 0.00 0.48 std::set<int>::lower_bound(key)_(existent)/1024 7.83 4.21 -3.62 -46.23 std::set<int>::lower_bound(key)_(existent)/32 2.74 1.68 -1.06 -38.81 std::set<int>::lower_bound(key)_(existent)/8192 22.75 11.12 -11.63 -51.12 std::set<int>::lower_bound(key)_(non-existent)/0 0.28 0.27 -0.01 -3.52 std::set<int>::lower_bound(key)_(non-existent)/1024 17.15 8.40 -8.75 -51.03 std::set<int>::lower_bound(key)_(non-existent)/32 4.63 2.50 -2.13 -46.03 std::set<int>::lower_bound(key)_(non-existent)/8192 28.88 11.05 -17.82 -61.72 std::set<int>::upper_bound(key)_(existent)/0 0.01 0.01 -0.00 -7.79 std::set<int>::upper_bound(key)_(existent)/1024 7.80 3.63 -4.16 -53.42 std::set<int>::upper_bound(key)_(existent)/32 2.81 1.90 -0.91 -32.44 std::set<int>::upper_bound(key)_(existent)/8192 21.93 11.35 -10.58 -48.26 std::set<int>::upper_bound(key)_(non-existent)/0 0.28 0.27 -0.01 -3.81 std::set<int>::upper_bound(key)_(non-existent)/1024 16.76 7.38 -9.38 -55.98 std::set<int>::upper_bound(key)_(non-existent)/32 4.58 3.10 -1.48 -32.31 std::set<int>::upper_bound(key)_(non-existent)/8192 26.95 10.70 -16.25 -60.29 std::set<std::string>::lower_bound(key)_(existent)/0 0.04 0.04 0.00 0.02 std::set<std::string>::lower_bound(key)_(existent)/1024 28.08 27.04 -1.04 -3.71 std::set<std::string>::lower_bound(key)_(existent)/32 17.53 16.94 -0.58 -3.34 std::set<std::string>::lower_bound(key)_(existent)/8192 32.79 33.28 0.49 1.49 std::set<std::string>::lower_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -0.06 std::set<std::string>::lower_bound(key)_(non-existent)/1024 25.23 24.38 -0.85 -3.38 std::set<std::string>::lower_bound(key)_(non-existent)/32 11.45 11.68 0.24 2.07 std::set<std::string>::lower_bound(key)_(non-existent)/8192 32.30 36.80 4.50 13.95 std::set<std::string>::upper_bound(key)_(existent)/0 0.04 0.04 -0.00 -0.14 std::set<std::string>::upper_bound(key)_(existent)/1024 26.71 26.37 -0.34 -1.27 std::set<std::string>::upper_bound(key)_(existent)/32 20.07 19.06 -1.02 -5.06 std::set<std::string>::upper_bound(key)_(existent)/8192 36.69 35.50 -1.19 -3.25 std::set<std::string>::upper_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -0.16 std::set<std::string>::upper_bound(key)_(non-existent)/1024 24.48 24.90 0.42 1.73 std::set<std::string>::upper_bound(key)_(non-existent)/32 11.68 11.77 0.09 0.77 std::set<std::string>::upper_bound(key)_(non-existent)/8192 33.16 34.12 0.96 2.89 ```
2025-10-08 22:45:03 -08:00
}
2010-05-11 11:42:16 -08:00
template <class _Key>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator __upper_bound_unique(const _Key& __v) const {
#if defined(_LIBCPP_ENABLE_LEGACY_TREE_LOWER_UPPER_BOUND)
return iterator(__upper_bound_unique_compat_impl(__v));
#else
[libc++] Optimize {set,map}::{lower,upper}_bound (#161366) Apple M4: ``` Benchmark Baseline Candidate Difference % Difference --------------------------------------------------------------------------------------------- ---------- ----------- ------------ -------------- std::map<int,_int>::lower_bound(key)_(existent)/0 0.01 0.01 -0.00 -25.78 std::map<int,_int>::lower_bound(key)_(existent)/1024 7.94 4.28 -3.66 -46.09 std::map<int,_int>::lower_bound(key)_(existent)/32 2.73 1.69 -1.03 -37.89 std::map<int,_int>::lower_bound(key)_(existent)/8192 11.63 5.52 -6.11 -52.55 std::map<int,_int>::lower_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -1.35 std::map<int,_int>::lower_bound(key)_(non-existent)/1024 17.21 7.63 -9.58 -55.67 std::map<int,_int>::lower_bound(key)_(non-existent)/32 4.71 3.26 -1.45 -30.71 std::map<int,_int>::lower_bound(key)_(non-existent)/8192 26.82 10.58 -16.24 -60.55 std::map<int,_int>::upper_bound(key)_(existent)/0 0.01 0.01 0.00 20.62 std::map<int,_int>::upper_bound(key)_(existent)/1024 7.93 3.61 -4.32 -54.49 std::map<int,_int>::upper_bound(key)_(existent)/32 2.83 1.98 -0.85 -30.01 std::map<int,_int>::upper_bound(key)_(existent)/8192 11.69 5.72 -5.97 -51.06 std::map<int,_int>::upper_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -1.36 std::map<int,_int>::upper_bound(key)_(non-existent)/1024 17.21 8.00 -9.21 -53.53 std::map<int,_int>::upper_bound(key)_(non-existent)/32 4.70 2.93 -1.78 -37.76 std::map<int,_int>::upper_bound(key)_(non-existent)/8192 26.54 11.18 -15.36 -57.89 std::map<std::string,_int>::lower_bound(key)_(existent)/0 0.04 0.04 -0.00 -3.26 std::map<std::string,_int>::lower_bound(key)_(existent)/1024 27.46 26.25 -1.22 -4.43 std::map<std::string,_int>::lower_bound(key)_(existent)/32 19.17 15.71 -3.46 -18.07 std::map<std::string,_int>::lower_bound(key)_(existent)/8192 35.33 35.03 -0.30 -0.84 std::map<std::string,_int>::lower_bound(key)_(non-existent)/0 0.27 0.27 -0.00 -1.45 std::map<std::string,_int>::lower_bound(key)_(non-existent)/1024 24.88 24.17 -0.70 -2.83 std::map<std::string,_int>::lower_bound(key)_(non-existent)/32 11.67 11.63 -0.04 -0.32 std::map<std::string,_int>::lower_bound(key)_(non-existent)/8192 31.81 32.33 0.52 1.64 std::map<std::string,_int>::upper_bound(key)_(existent)/0 0.04 0.04 -0.00 -2.22 std::map<std::string,_int>::upper_bound(key)_(existent)/1024 29.91 26.51 -3.40 -11.38 std::map<std::string,_int>::upper_bound(key)_(existent)/32 19.69 17.74 -1.95 -9.92 std::map<std::string,_int>::upper_bound(key)_(existent)/8192 32.55 35.24 2.69 8.25 std::map<std::string,_int>::upper_bound(key)_(non-existent)/0 0.27 0.27 -0.00 -1.74 std::map<std::string,_int>::upper_bound(key)_(non-existent)/1024 23.87 26.77 2.91 12.18 std::map<std::string,_int>::upper_bound(key)_(non-existent)/32 11.44 11.81 0.37 3.24 std::map<std::string,_int>::upper_bound(key)_(non-existent)/8192 33.02 32.59 -0.43 -1.29 std::set<int>::lower_bound(key)_(existent)/0 0.01 0.01 0.00 0.48 std::set<int>::lower_bound(key)_(existent)/1024 7.83 4.21 -3.62 -46.23 std::set<int>::lower_bound(key)_(existent)/32 2.74 1.68 -1.06 -38.81 std::set<int>::lower_bound(key)_(existent)/8192 22.75 11.12 -11.63 -51.12 std::set<int>::lower_bound(key)_(non-existent)/0 0.28 0.27 -0.01 -3.52 std::set<int>::lower_bound(key)_(non-existent)/1024 17.15 8.40 -8.75 -51.03 std::set<int>::lower_bound(key)_(non-existent)/32 4.63 2.50 -2.13 -46.03 std::set<int>::lower_bound(key)_(non-existent)/8192 28.88 11.05 -17.82 -61.72 std::set<int>::upper_bound(key)_(existent)/0 0.01 0.01 -0.00 -7.79 std::set<int>::upper_bound(key)_(existent)/1024 7.80 3.63 -4.16 -53.42 std::set<int>::upper_bound(key)_(existent)/32 2.81 1.90 -0.91 -32.44 std::set<int>::upper_bound(key)_(existent)/8192 21.93 11.35 -10.58 -48.26 std::set<int>::upper_bound(key)_(non-existent)/0 0.28 0.27 -0.01 -3.81 std::set<int>::upper_bound(key)_(non-existent)/1024 16.76 7.38 -9.38 -55.98 std::set<int>::upper_bound(key)_(non-existent)/32 4.58 3.10 -1.48 -32.31 std::set<int>::upper_bound(key)_(non-existent)/8192 26.95 10.70 -16.25 -60.29 std::set<std::string>::lower_bound(key)_(existent)/0 0.04 0.04 0.00 0.02 std::set<std::string>::lower_bound(key)_(existent)/1024 28.08 27.04 -1.04 -3.71 std::set<std::string>::lower_bound(key)_(existent)/32 17.53 16.94 -0.58 -3.34 std::set<std::string>::lower_bound(key)_(existent)/8192 32.79 33.28 0.49 1.49 std::set<std::string>::lower_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -0.06 std::set<std::string>::lower_bound(key)_(non-existent)/1024 25.23 24.38 -0.85 -3.38 std::set<std::string>::lower_bound(key)_(non-existent)/32 11.45 11.68 0.24 2.07 std::set<std::string>::lower_bound(key)_(non-existent)/8192 32.30 36.80 4.50 13.95 std::set<std::string>::upper_bound(key)_(existent)/0 0.04 0.04 -0.00 -0.14 std::set<std::string>::upper_bound(key)_(existent)/1024 26.71 26.37 -0.34 -1.27 std::set<std::string>::upper_bound(key)_(existent)/32 20.07 19.06 -1.02 -5.06 std::set<std::string>::upper_bound(key)_(existent)/8192 36.69 35.50 -1.19 -3.25 std::set<std::string>::upper_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -0.16 std::set<std::string>::upper_bound(key)_(non-existent)/1024 24.48 24.90 0.42 1.73 std::set<std::string>::upper_bound(key)_(non-existent)/32 11.68 11.77 0.09 0.77 std::set<std::string>::upper_bound(key)_(non-existent)/8192 33.16 34.12 0.96 2.89 ```
2025-10-08 22:45:03 -08:00
return iterator(__lower_upper_bound_unique_impl<false>(__v));
#endif
2010-05-11 11:42:16 -08:00
}
[libc++] Optimize {set,map}::{lower,upper}_bound (#161366) Apple M4: ``` Benchmark Baseline Candidate Difference % Difference --------------------------------------------------------------------------------------------- ---------- ----------- ------------ -------------- std::map<int,_int>::lower_bound(key)_(existent)/0 0.01 0.01 -0.00 -25.78 std::map<int,_int>::lower_bound(key)_(existent)/1024 7.94 4.28 -3.66 -46.09 std::map<int,_int>::lower_bound(key)_(existent)/32 2.73 1.69 -1.03 -37.89 std::map<int,_int>::lower_bound(key)_(existent)/8192 11.63 5.52 -6.11 -52.55 std::map<int,_int>::lower_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -1.35 std::map<int,_int>::lower_bound(key)_(non-existent)/1024 17.21 7.63 -9.58 -55.67 std::map<int,_int>::lower_bound(key)_(non-existent)/32 4.71 3.26 -1.45 -30.71 std::map<int,_int>::lower_bound(key)_(non-existent)/8192 26.82 10.58 -16.24 -60.55 std::map<int,_int>::upper_bound(key)_(existent)/0 0.01 0.01 0.00 20.62 std::map<int,_int>::upper_bound(key)_(existent)/1024 7.93 3.61 -4.32 -54.49 std::map<int,_int>::upper_bound(key)_(existent)/32 2.83 1.98 -0.85 -30.01 std::map<int,_int>::upper_bound(key)_(existent)/8192 11.69 5.72 -5.97 -51.06 std::map<int,_int>::upper_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -1.36 std::map<int,_int>::upper_bound(key)_(non-existent)/1024 17.21 8.00 -9.21 -53.53 std::map<int,_int>::upper_bound(key)_(non-existent)/32 4.70 2.93 -1.78 -37.76 std::map<int,_int>::upper_bound(key)_(non-existent)/8192 26.54 11.18 -15.36 -57.89 std::map<std::string,_int>::lower_bound(key)_(existent)/0 0.04 0.04 -0.00 -3.26 std::map<std::string,_int>::lower_bound(key)_(existent)/1024 27.46 26.25 -1.22 -4.43 std::map<std::string,_int>::lower_bound(key)_(existent)/32 19.17 15.71 -3.46 -18.07 std::map<std::string,_int>::lower_bound(key)_(existent)/8192 35.33 35.03 -0.30 -0.84 std::map<std::string,_int>::lower_bound(key)_(non-existent)/0 0.27 0.27 -0.00 -1.45 std::map<std::string,_int>::lower_bound(key)_(non-existent)/1024 24.88 24.17 -0.70 -2.83 std::map<std::string,_int>::lower_bound(key)_(non-existent)/32 11.67 11.63 -0.04 -0.32 std::map<std::string,_int>::lower_bound(key)_(non-existent)/8192 31.81 32.33 0.52 1.64 std::map<std::string,_int>::upper_bound(key)_(existent)/0 0.04 0.04 -0.00 -2.22 std::map<std::string,_int>::upper_bound(key)_(existent)/1024 29.91 26.51 -3.40 -11.38 std::map<std::string,_int>::upper_bound(key)_(existent)/32 19.69 17.74 -1.95 -9.92 std::map<std::string,_int>::upper_bound(key)_(existent)/8192 32.55 35.24 2.69 8.25 std::map<std::string,_int>::upper_bound(key)_(non-existent)/0 0.27 0.27 -0.00 -1.74 std::map<std::string,_int>::upper_bound(key)_(non-existent)/1024 23.87 26.77 2.91 12.18 std::map<std::string,_int>::upper_bound(key)_(non-existent)/32 11.44 11.81 0.37 3.24 std::map<std::string,_int>::upper_bound(key)_(non-existent)/8192 33.02 32.59 -0.43 -1.29 std::set<int>::lower_bound(key)_(existent)/0 0.01 0.01 0.00 0.48 std::set<int>::lower_bound(key)_(existent)/1024 7.83 4.21 -3.62 -46.23 std::set<int>::lower_bound(key)_(existent)/32 2.74 1.68 -1.06 -38.81 std::set<int>::lower_bound(key)_(existent)/8192 22.75 11.12 -11.63 -51.12 std::set<int>::lower_bound(key)_(non-existent)/0 0.28 0.27 -0.01 -3.52 std::set<int>::lower_bound(key)_(non-existent)/1024 17.15 8.40 -8.75 -51.03 std::set<int>::lower_bound(key)_(non-existent)/32 4.63 2.50 -2.13 -46.03 std::set<int>::lower_bound(key)_(non-existent)/8192 28.88 11.05 -17.82 -61.72 std::set<int>::upper_bound(key)_(existent)/0 0.01 0.01 -0.00 -7.79 std::set<int>::upper_bound(key)_(existent)/1024 7.80 3.63 -4.16 -53.42 std::set<int>::upper_bound(key)_(existent)/32 2.81 1.90 -0.91 -32.44 std::set<int>::upper_bound(key)_(existent)/8192 21.93 11.35 -10.58 -48.26 std::set<int>::upper_bound(key)_(non-existent)/0 0.28 0.27 -0.01 -3.81 std::set<int>::upper_bound(key)_(non-existent)/1024 16.76 7.38 -9.38 -55.98 std::set<int>::upper_bound(key)_(non-existent)/32 4.58 3.10 -1.48 -32.31 std::set<int>::upper_bound(key)_(non-existent)/8192 26.95 10.70 -16.25 -60.29 std::set<std::string>::lower_bound(key)_(existent)/0 0.04 0.04 0.00 0.02 std::set<std::string>::lower_bound(key)_(existent)/1024 28.08 27.04 -1.04 -3.71 std::set<std::string>::lower_bound(key)_(existent)/32 17.53 16.94 -0.58 -3.34 std::set<std::string>::lower_bound(key)_(existent)/8192 32.79 33.28 0.49 1.49 std::set<std::string>::lower_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -0.06 std::set<std::string>::lower_bound(key)_(non-existent)/1024 25.23 24.38 -0.85 -3.38 std::set<std::string>::lower_bound(key)_(non-existent)/32 11.45 11.68 0.24 2.07 std::set<std::string>::lower_bound(key)_(non-existent)/8192 32.30 36.80 4.50 13.95 std::set<std::string>::upper_bound(key)_(existent)/0 0.04 0.04 -0.00 -0.14 std::set<std::string>::upper_bound(key)_(existent)/1024 26.71 26.37 -0.34 -1.27 std::set<std::string>::upper_bound(key)_(existent)/32 20.07 19.06 -1.02 -5.06 std::set<std::string>::upper_bound(key)_(existent)/8192 36.69 35.50 -1.19 -3.25 std::set<std::string>::upper_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -0.16 std::set<std::string>::upper_bound(key)_(non-existent)/1024 24.48 24.90 0.42 1.73 std::set<std::string>::upper_bound(key)_(non-existent)/32 11.68 11.77 0.09 0.77 std::set<std::string>::upper_bound(key)_(non-existent)/8192 33.16 34.12 0.96 2.89 ```
2025-10-08 22:45:03 -08:00
private:
template <class _Key>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator
[libc++] Optimize {set,map}::{lower,upper}_bound (#161366) Apple M4: ``` Benchmark Baseline Candidate Difference % Difference --------------------------------------------------------------------------------------------- ---------- ----------- ------------ -------------- std::map<int,_int>::lower_bound(key)_(existent)/0 0.01 0.01 -0.00 -25.78 std::map<int,_int>::lower_bound(key)_(existent)/1024 7.94 4.28 -3.66 -46.09 std::map<int,_int>::lower_bound(key)_(existent)/32 2.73 1.69 -1.03 -37.89 std::map<int,_int>::lower_bound(key)_(existent)/8192 11.63 5.52 -6.11 -52.55 std::map<int,_int>::lower_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -1.35 std::map<int,_int>::lower_bound(key)_(non-existent)/1024 17.21 7.63 -9.58 -55.67 std::map<int,_int>::lower_bound(key)_(non-existent)/32 4.71 3.26 -1.45 -30.71 std::map<int,_int>::lower_bound(key)_(non-existent)/8192 26.82 10.58 -16.24 -60.55 std::map<int,_int>::upper_bound(key)_(existent)/0 0.01 0.01 0.00 20.62 std::map<int,_int>::upper_bound(key)_(existent)/1024 7.93 3.61 -4.32 -54.49 std::map<int,_int>::upper_bound(key)_(existent)/32 2.83 1.98 -0.85 -30.01 std::map<int,_int>::upper_bound(key)_(existent)/8192 11.69 5.72 -5.97 -51.06 std::map<int,_int>::upper_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -1.36 std::map<int,_int>::upper_bound(key)_(non-existent)/1024 17.21 8.00 -9.21 -53.53 std::map<int,_int>::upper_bound(key)_(non-existent)/32 4.70 2.93 -1.78 -37.76 std::map<int,_int>::upper_bound(key)_(non-existent)/8192 26.54 11.18 -15.36 -57.89 std::map<std::string,_int>::lower_bound(key)_(existent)/0 0.04 0.04 -0.00 -3.26 std::map<std::string,_int>::lower_bound(key)_(existent)/1024 27.46 26.25 -1.22 -4.43 std::map<std::string,_int>::lower_bound(key)_(existent)/32 19.17 15.71 -3.46 -18.07 std::map<std::string,_int>::lower_bound(key)_(existent)/8192 35.33 35.03 -0.30 -0.84 std::map<std::string,_int>::lower_bound(key)_(non-existent)/0 0.27 0.27 -0.00 -1.45 std::map<std::string,_int>::lower_bound(key)_(non-existent)/1024 24.88 24.17 -0.70 -2.83 std::map<std::string,_int>::lower_bound(key)_(non-existent)/32 11.67 11.63 -0.04 -0.32 std::map<std::string,_int>::lower_bound(key)_(non-existent)/8192 31.81 32.33 0.52 1.64 std::map<std::string,_int>::upper_bound(key)_(existent)/0 0.04 0.04 -0.00 -2.22 std::map<std::string,_int>::upper_bound(key)_(existent)/1024 29.91 26.51 -3.40 -11.38 std::map<std::string,_int>::upper_bound(key)_(existent)/32 19.69 17.74 -1.95 -9.92 std::map<std::string,_int>::upper_bound(key)_(existent)/8192 32.55 35.24 2.69 8.25 std::map<std::string,_int>::upper_bound(key)_(non-existent)/0 0.27 0.27 -0.00 -1.74 std::map<std::string,_int>::upper_bound(key)_(non-existent)/1024 23.87 26.77 2.91 12.18 std::map<std::string,_int>::upper_bound(key)_(non-existent)/32 11.44 11.81 0.37 3.24 std::map<std::string,_int>::upper_bound(key)_(non-existent)/8192 33.02 32.59 -0.43 -1.29 std::set<int>::lower_bound(key)_(existent)/0 0.01 0.01 0.00 0.48 std::set<int>::lower_bound(key)_(existent)/1024 7.83 4.21 -3.62 -46.23 std::set<int>::lower_bound(key)_(existent)/32 2.74 1.68 -1.06 -38.81 std::set<int>::lower_bound(key)_(existent)/8192 22.75 11.12 -11.63 -51.12 std::set<int>::lower_bound(key)_(non-existent)/0 0.28 0.27 -0.01 -3.52 std::set<int>::lower_bound(key)_(non-existent)/1024 17.15 8.40 -8.75 -51.03 std::set<int>::lower_bound(key)_(non-existent)/32 4.63 2.50 -2.13 -46.03 std::set<int>::lower_bound(key)_(non-existent)/8192 28.88 11.05 -17.82 -61.72 std::set<int>::upper_bound(key)_(existent)/0 0.01 0.01 -0.00 -7.79 std::set<int>::upper_bound(key)_(existent)/1024 7.80 3.63 -4.16 -53.42 std::set<int>::upper_bound(key)_(existent)/32 2.81 1.90 -0.91 -32.44 std::set<int>::upper_bound(key)_(existent)/8192 21.93 11.35 -10.58 -48.26 std::set<int>::upper_bound(key)_(non-existent)/0 0.28 0.27 -0.01 -3.81 std::set<int>::upper_bound(key)_(non-existent)/1024 16.76 7.38 -9.38 -55.98 std::set<int>::upper_bound(key)_(non-existent)/32 4.58 3.10 -1.48 -32.31 std::set<int>::upper_bound(key)_(non-existent)/8192 26.95 10.70 -16.25 -60.29 std::set<std::string>::lower_bound(key)_(existent)/0 0.04 0.04 0.00 0.02 std::set<std::string>::lower_bound(key)_(existent)/1024 28.08 27.04 -1.04 -3.71 std::set<std::string>::lower_bound(key)_(existent)/32 17.53 16.94 -0.58 -3.34 std::set<std::string>::lower_bound(key)_(existent)/8192 32.79 33.28 0.49 1.49 std::set<std::string>::lower_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -0.06 std::set<std::string>::lower_bound(key)_(non-existent)/1024 25.23 24.38 -0.85 -3.38 std::set<std::string>::lower_bound(key)_(non-existent)/32 11.45 11.68 0.24 2.07 std::set<std::string>::lower_bound(key)_(non-existent)/8192 32.30 36.80 4.50 13.95 std::set<std::string>::upper_bound(key)_(existent)/0 0.04 0.04 -0.00 -0.14 std::set<std::string>::upper_bound(key)_(existent)/1024 26.71 26.37 -0.34 -1.27 std::set<std::string>::upper_bound(key)_(existent)/32 20.07 19.06 -1.02 -5.06 std::set<std::string>::upper_bound(key)_(existent)/8192 36.69 35.50 -1.19 -3.25 std::set<std::string>::upper_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -0.16 std::set<std::string>::upper_bound(key)_(non-existent)/1024 24.48 24.90 0.42 1.73 std::set<std::string>::upper_bound(key)_(non-existent)/32 11.68 11.77 0.09 0.77 std::set<std::string>::upper_bound(key)_(non-existent)/8192 33.16 34.12 0.96 2.89 ```
2025-10-08 22:45:03 -08:00
__lower_bound_multi(const _Key& __v, __node_pointer __root, __end_node_pointer __result);
2010-05-11 11:42:16 -08:00
template <class _Key>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator
[libc++] Optimize {set,map}::{lower,upper}_bound (#161366) Apple M4: ``` Benchmark Baseline Candidate Difference % Difference --------------------------------------------------------------------------------------------- ---------- ----------- ------------ -------------- std::map<int,_int>::lower_bound(key)_(existent)/0 0.01 0.01 -0.00 -25.78 std::map<int,_int>::lower_bound(key)_(existent)/1024 7.94 4.28 -3.66 -46.09 std::map<int,_int>::lower_bound(key)_(existent)/32 2.73 1.69 -1.03 -37.89 std::map<int,_int>::lower_bound(key)_(existent)/8192 11.63 5.52 -6.11 -52.55 std::map<int,_int>::lower_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -1.35 std::map<int,_int>::lower_bound(key)_(non-existent)/1024 17.21 7.63 -9.58 -55.67 std::map<int,_int>::lower_bound(key)_(non-existent)/32 4.71 3.26 -1.45 -30.71 std::map<int,_int>::lower_bound(key)_(non-existent)/8192 26.82 10.58 -16.24 -60.55 std::map<int,_int>::upper_bound(key)_(existent)/0 0.01 0.01 0.00 20.62 std::map<int,_int>::upper_bound(key)_(existent)/1024 7.93 3.61 -4.32 -54.49 std::map<int,_int>::upper_bound(key)_(existent)/32 2.83 1.98 -0.85 -30.01 std::map<int,_int>::upper_bound(key)_(existent)/8192 11.69 5.72 -5.97 -51.06 std::map<int,_int>::upper_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -1.36 std::map<int,_int>::upper_bound(key)_(non-existent)/1024 17.21 8.00 -9.21 -53.53 std::map<int,_int>::upper_bound(key)_(non-existent)/32 4.70 2.93 -1.78 -37.76 std::map<int,_int>::upper_bound(key)_(non-existent)/8192 26.54 11.18 -15.36 -57.89 std::map<std::string,_int>::lower_bound(key)_(existent)/0 0.04 0.04 -0.00 -3.26 std::map<std::string,_int>::lower_bound(key)_(existent)/1024 27.46 26.25 -1.22 -4.43 std::map<std::string,_int>::lower_bound(key)_(existent)/32 19.17 15.71 -3.46 -18.07 std::map<std::string,_int>::lower_bound(key)_(existent)/8192 35.33 35.03 -0.30 -0.84 std::map<std::string,_int>::lower_bound(key)_(non-existent)/0 0.27 0.27 -0.00 -1.45 std::map<std::string,_int>::lower_bound(key)_(non-existent)/1024 24.88 24.17 -0.70 -2.83 std::map<std::string,_int>::lower_bound(key)_(non-existent)/32 11.67 11.63 -0.04 -0.32 std::map<std::string,_int>::lower_bound(key)_(non-existent)/8192 31.81 32.33 0.52 1.64 std::map<std::string,_int>::upper_bound(key)_(existent)/0 0.04 0.04 -0.00 -2.22 std::map<std::string,_int>::upper_bound(key)_(existent)/1024 29.91 26.51 -3.40 -11.38 std::map<std::string,_int>::upper_bound(key)_(existent)/32 19.69 17.74 -1.95 -9.92 std::map<std::string,_int>::upper_bound(key)_(existent)/8192 32.55 35.24 2.69 8.25 std::map<std::string,_int>::upper_bound(key)_(non-existent)/0 0.27 0.27 -0.00 -1.74 std::map<std::string,_int>::upper_bound(key)_(non-existent)/1024 23.87 26.77 2.91 12.18 std::map<std::string,_int>::upper_bound(key)_(non-existent)/32 11.44 11.81 0.37 3.24 std::map<std::string,_int>::upper_bound(key)_(non-existent)/8192 33.02 32.59 -0.43 -1.29 std::set<int>::lower_bound(key)_(existent)/0 0.01 0.01 0.00 0.48 std::set<int>::lower_bound(key)_(existent)/1024 7.83 4.21 -3.62 -46.23 std::set<int>::lower_bound(key)_(existent)/32 2.74 1.68 -1.06 -38.81 std::set<int>::lower_bound(key)_(existent)/8192 22.75 11.12 -11.63 -51.12 std::set<int>::lower_bound(key)_(non-existent)/0 0.28 0.27 -0.01 -3.52 std::set<int>::lower_bound(key)_(non-existent)/1024 17.15 8.40 -8.75 -51.03 std::set<int>::lower_bound(key)_(non-existent)/32 4.63 2.50 -2.13 -46.03 std::set<int>::lower_bound(key)_(non-existent)/8192 28.88 11.05 -17.82 -61.72 std::set<int>::upper_bound(key)_(existent)/0 0.01 0.01 -0.00 -7.79 std::set<int>::upper_bound(key)_(existent)/1024 7.80 3.63 -4.16 -53.42 std::set<int>::upper_bound(key)_(existent)/32 2.81 1.90 -0.91 -32.44 std::set<int>::upper_bound(key)_(existent)/8192 21.93 11.35 -10.58 -48.26 std::set<int>::upper_bound(key)_(non-existent)/0 0.28 0.27 -0.01 -3.81 std::set<int>::upper_bound(key)_(non-existent)/1024 16.76 7.38 -9.38 -55.98 std::set<int>::upper_bound(key)_(non-existent)/32 4.58 3.10 -1.48 -32.31 std::set<int>::upper_bound(key)_(non-existent)/8192 26.95 10.70 -16.25 -60.29 std::set<std::string>::lower_bound(key)_(existent)/0 0.04 0.04 0.00 0.02 std::set<std::string>::lower_bound(key)_(existent)/1024 28.08 27.04 -1.04 -3.71 std::set<std::string>::lower_bound(key)_(existent)/32 17.53 16.94 -0.58 -3.34 std::set<std::string>::lower_bound(key)_(existent)/8192 32.79 33.28 0.49 1.49 std::set<std::string>::lower_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -0.06 std::set<std::string>::lower_bound(key)_(non-existent)/1024 25.23 24.38 -0.85 -3.38 std::set<std::string>::lower_bound(key)_(non-existent)/32 11.45 11.68 0.24 2.07 std::set<std::string>::lower_bound(key)_(non-existent)/8192 32.30 36.80 4.50 13.95 std::set<std::string>::upper_bound(key)_(existent)/0 0.04 0.04 -0.00 -0.14 std::set<std::string>::upper_bound(key)_(existent)/1024 26.71 26.37 -0.34 -1.27 std::set<std::string>::upper_bound(key)_(existent)/32 20.07 19.06 -1.02 -5.06 std::set<std::string>::upper_bound(key)_(existent)/8192 36.69 35.50 -1.19 -3.25 std::set<std::string>::upper_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -0.16 std::set<std::string>::upper_bound(key)_(non-existent)/1024 24.48 24.90 0.42 1.73 std::set<std::string>::upper_bound(key)_(non-existent)/32 11.68 11.77 0.09 0.77 std::set<std::string>::upper_bound(key)_(non-existent)/8192 33.16 34.12 0.96 2.89 ```
2025-10-08 22:45:03 -08:00
__lower_bound_multi(const _Key& __v, __node_pointer __root, __end_node_pointer __result) const;
public:
template <class _Key>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator __lower_bound_multi(const _Key& __v) {
[libc++] Optimize {set,map}::{lower,upper}_bound (#161366) Apple M4: ``` Benchmark Baseline Candidate Difference % Difference --------------------------------------------------------------------------------------------- ---------- ----------- ------------ -------------- std::map<int,_int>::lower_bound(key)_(existent)/0 0.01 0.01 -0.00 -25.78 std::map<int,_int>::lower_bound(key)_(existent)/1024 7.94 4.28 -3.66 -46.09 std::map<int,_int>::lower_bound(key)_(existent)/32 2.73 1.69 -1.03 -37.89 std::map<int,_int>::lower_bound(key)_(existent)/8192 11.63 5.52 -6.11 -52.55 std::map<int,_int>::lower_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -1.35 std::map<int,_int>::lower_bound(key)_(non-existent)/1024 17.21 7.63 -9.58 -55.67 std::map<int,_int>::lower_bound(key)_(non-existent)/32 4.71 3.26 -1.45 -30.71 std::map<int,_int>::lower_bound(key)_(non-existent)/8192 26.82 10.58 -16.24 -60.55 std::map<int,_int>::upper_bound(key)_(existent)/0 0.01 0.01 0.00 20.62 std::map<int,_int>::upper_bound(key)_(existent)/1024 7.93 3.61 -4.32 -54.49 std::map<int,_int>::upper_bound(key)_(existent)/32 2.83 1.98 -0.85 -30.01 std::map<int,_int>::upper_bound(key)_(existent)/8192 11.69 5.72 -5.97 -51.06 std::map<int,_int>::upper_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -1.36 std::map<int,_int>::upper_bound(key)_(non-existent)/1024 17.21 8.00 -9.21 -53.53 std::map<int,_int>::upper_bound(key)_(non-existent)/32 4.70 2.93 -1.78 -37.76 std::map<int,_int>::upper_bound(key)_(non-existent)/8192 26.54 11.18 -15.36 -57.89 std::map<std::string,_int>::lower_bound(key)_(existent)/0 0.04 0.04 -0.00 -3.26 std::map<std::string,_int>::lower_bound(key)_(existent)/1024 27.46 26.25 -1.22 -4.43 std::map<std::string,_int>::lower_bound(key)_(existent)/32 19.17 15.71 -3.46 -18.07 std::map<std::string,_int>::lower_bound(key)_(existent)/8192 35.33 35.03 -0.30 -0.84 std::map<std::string,_int>::lower_bound(key)_(non-existent)/0 0.27 0.27 -0.00 -1.45 std::map<std::string,_int>::lower_bound(key)_(non-existent)/1024 24.88 24.17 -0.70 -2.83 std::map<std::string,_int>::lower_bound(key)_(non-existent)/32 11.67 11.63 -0.04 -0.32 std::map<std::string,_int>::lower_bound(key)_(non-existent)/8192 31.81 32.33 0.52 1.64 std::map<std::string,_int>::upper_bound(key)_(existent)/0 0.04 0.04 -0.00 -2.22 std::map<std::string,_int>::upper_bound(key)_(existent)/1024 29.91 26.51 -3.40 -11.38 std::map<std::string,_int>::upper_bound(key)_(existent)/32 19.69 17.74 -1.95 -9.92 std::map<std::string,_int>::upper_bound(key)_(existent)/8192 32.55 35.24 2.69 8.25 std::map<std::string,_int>::upper_bound(key)_(non-existent)/0 0.27 0.27 -0.00 -1.74 std::map<std::string,_int>::upper_bound(key)_(non-existent)/1024 23.87 26.77 2.91 12.18 std::map<std::string,_int>::upper_bound(key)_(non-existent)/32 11.44 11.81 0.37 3.24 std::map<std::string,_int>::upper_bound(key)_(non-existent)/8192 33.02 32.59 -0.43 -1.29 std::set<int>::lower_bound(key)_(existent)/0 0.01 0.01 0.00 0.48 std::set<int>::lower_bound(key)_(existent)/1024 7.83 4.21 -3.62 -46.23 std::set<int>::lower_bound(key)_(existent)/32 2.74 1.68 -1.06 -38.81 std::set<int>::lower_bound(key)_(existent)/8192 22.75 11.12 -11.63 -51.12 std::set<int>::lower_bound(key)_(non-existent)/0 0.28 0.27 -0.01 -3.52 std::set<int>::lower_bound(key)_(non-existent)/1024 17.15 8.40 -8.75 -51.03 std::set<int>::lower_bound(key)_(non-existent)/32 4.63 2.50 -2.13 -46.03 std::set<int>::lower_bound(key)_(non-existent)/8192 28.88 11.05 -17.82 -61.72 std::set<int>::upper_bound(key)_(existent)/0 0.01 0.01 -0.00 -7.79 std::set<int>::upper_bound(key)_(existent)/1024 7.80 3.63 -4.16 -53.42 std::set<int>::upper_bound(key)_(existent)/32 2.81 1.90 -0.91 -32.44 std::set<int>::upper_bound(key)_(existent)/8192 21.93 11.35 -10.58 -48.26 std::set<int>::upper_bound(key)_(non-existent)/0 0.28 0.27 -0.01 -3.81 std::set<int>::upper_bound(key)_(non-existent)/1024 16.76 7.38 -9.38 -55.98 std::set<int>::upper_bound(key)_(non-existent)/32 4.58 3.10 -1.48 -32.31 std::set<int>::upper_bound(key)_(non-existent)/8192 26.95 10.70 -16.25 -60.29 std::set<std::string>::lower_bound(key)_(existent)/0 0.04 0.04 0.00 0.02 std::set<std::string>::lower_bound(key)_(existent)/1024 28.08 27.04 -1.04 -3.71 std::set<std::string>::lower_bound(key)_(existent)/32 17.53 16.94 -0.58 -3.34 std::set<std::string>::lower_bound(key)_(existent)/8192 32.79 33.28 0.49 1.49 std::set<std::string>::lower_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -0.06 std::set<std::string>::lower_bound(key)_(non-existent)/1024 25.23 24.38 -0.85 -3.38 std::set<std::string>::lower_bound(key)_(non-existent)/32 11.45 11.68 0.24 2.07 std::set<std::string>::lower_bound(key)_(non-existent)/8192 32.30 36.80 4.50 13.95 std::set<std::string>::upper_bound(key)_(existent)/0 0.04 0.04 -0.00 -0.14 std::set<std::string>::upper_bound(key)_(existent)/1024 26.71 26.37 -0.34 -1.27 std::set<std::string>::upper_bound(key)_(existent)/32 20.07 19.06 -1.02 -5.06 std::set<std::string>::upper_bound(key)_(existent)/8192 36.69 35.50 -1.19 -3.25 std::set<std::string>::upper_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -0.16 std::set<std::string>::upper_bound(key)_(non-existent)/1024 24.48 24.90 0.42 1.73 std::set<std::string>::upper_bound(key)_(non-existent)/32 11.68 11.77 0.09 0.77 std::set<std::string>::upper_bound(key)_(non-existent)/8192 33.16 34.12 0.96 2.89 ```
2025-10-08 22:45:03 -08:00
return __lower_bound_multi(__v, __root(), __end_node());
}
2010-05-11 11:42:16 -08:00
template <class _Key>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator __lower_bound_multi(const _Key& __v) const {
[libc++] Optimize {set,map}::{lower,upper}_bound (#161366) Apple M4: ``` Benchmark Baseline Candidate Difference % Difference --------------------------------------------------------------------------------------------- ---------- ----------- ------------ -------------- std::map<int,_int>::lower_bound(key)_(existent)/0 0.01 0.01 -0.00 -25.78 std::map<int,_int>::lower_bound(key)_(existent)/1024 7.94 4.28 -3.66 -46.09 std::map<int,_int>::lower_bound(key)_(existent)/32 2.73 1.69 -1.03 -37.89 std::map<int,_int>::lower_bound(key)_(existent)/8192 11.63 5.52 -6.11 -52.55 std::map<int,_int>::lower_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -1.35 std::map<int,_int>::lower_bound(key)_(non-existent)/1024 17.21 7.63 -9.58 -55.67 std::map<int,_int>::lower_bound(key)_(non-existent)/32 4.71 3.26 -1.45 -30.71 std::map<int,_int>::lower_bound(key)_(non-existent)/8192 26.82 10.58 -16.24 -60.55 std::map<int,_int>::upper_bound(key)_(existent)/0 0.01 0.01 0.00 20.62 std::map<int,_int>::upper_bound(key)_(existent)/1024 7.93 3.61 -4.32 -54.49 std::map<int,_int>::upper_bound(key)_(existent)/32 2.83 1.98 -0.85 -30.01 std::map<int,_int>::upper_bound(key)_(existent)/8192 11.69 5.72 -5.97 -51.06 std::map<int,_int>::upper_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -1.36 std::map<int,_int>::upper_bound(key)_(non-existent)/1024 17.21 8.00 -9.21 -53.53 std::map<int,_int>::upper_bound(key)_(non-existent)/32 4.70 2.93 -1.78 -37.76 std::map<int,_int>::upper_bound(key)_(non-existent)/8192 26.54 11.18 -15.36 -57.89 std::map<std::string,_int>::lower_bound(key)_(existent)/0 0.04 0.04 -0.00 -3.26 std::map<std::string,_int>::lower_bound(key)_(existent)/1024 27.46 26.25 -1.22 -4.43 std::map<std::string,_int>::lower_bound(key)_(existent)/32 19.17 15.71 -3.46 -18.07 std::map<std::string,_int>::lower_bound(key)_(existent)/8192 35.33 35.03 -0.30 -0.84 std::map<std::string,_int>::lower_bound(key)_(non-existent)/0 0.27 0.27 -0.00 -1.45 std::map<std::string,_int>::lower_bound(key)_(non-existent)/1024 24.88 24.17 -0.70 -2.83 std::map<std::string,_int>::lower_bound(key)_(non-existent)/32 11.67 11.63 -0.04 -0.32 std::map<std::string,_int>::lower_bound(key)_(non-existent)/8192 31.81 32.33 0.52 1.64 std::map<std::string,_int>::upper_bound(key)_(existent)/0 0.04 0.04 -0.00 -2.22 std::map<std::string,_int>::upper_bound(key)_(existent)/1024 29.91 26.51 -3.40 -11.38 std::map<std::string,_int>::upper_bound(key)_(existent)/32 19.69 17.74 -1.95 -9.92 std::map<std::string,_int>::upper_bound(key)_(existent)/8192 32.55 35.24 2.69 8.25 std::map<std::string,_int>::upper_bound(key)_(non-existent)/0 0.27 0.27 -0.00 -1.74 std::map<std::string,_int>::upper_bound(key)_(non-existent)/1024 23.87 26.77 2.91 12.18 std::map<std::string,_int>::upper_bound(key)_(non-existent)/32 11.44 11.81 0.37 3.24 std::map<std::string,_int>::upper_bound(key)_(non-existent)/8192 33.02 32.59 -0.43 -1.29 std::set<int>::lower_bound(key)_(existent)/0 0.01 0.01 0.00 0.48 std::set<int>::lower_bound(key)_(existent)/1024 7.83 4.21 -3.62 -46.23 std::set<int>::lower_bound(key)_(existent)/32 2.74 1.68 -1.06 -38.81 std::set<int>::lower_bound(key)_(existent)/8192 22.75 11.12 -11.63 -51.12 std::set<int>::lower_bound(key)_(non-existent)/0 0.28 0.27 -0.01 -3.52 std::set<int>::lower_bound(key)_(non-existent)/1024 17.15 8.40 -8.75 -51.03 std::set<int>::lower_bound(key)_(non-existent)/32 4.63 2.50 -2.13 -46.03 std::set<int>::lower_bound(key)_(non-existent)/8192 28.88 11.05 -17.82 -61.72 std::set<int>::upper_bound(key)_(existent)/0 0.01 0.01 -0.00 -7.79 std::set<int>::upper_bound(key)_(existent)/1024 7.80 3.63 -4.16 -53.42 std::set<int>::upper_bound(key)_(existent)/32 2.81 1.90 -0.91 -32.44 std::set<int>::upper_bound(key)_(existent)/8192 21.93 11.35 -10.58 -48.26 std::set<int>::upper_bound(key)_(non-existent)/0 0.28 0.27 -0.01 -3.81 std::set<int>::upper_bound(key)_(non-existent)/1024 16.76 7.38 -9.38 -55.98 std::set<int>::upper_bound(key)_(non-existent)/32 4.58 3.10 -1.48 -32.31 std::set<int>::upper_bound(key)_(non-existent)/8192 26.95 10.70 -16.25 -60.29 std::set<std::string>::lower_bound(key)_(existent)/0 0.04 0.04 0.00 0.02 std::set<std::string>::lower_bound(key)_(existent)/1024 28.08 27.04 -1.04 -3.71 std::set<std::string>::lower_bound(key)_(existent)/32 17.53 16.94 -0.58 -3.34 std::set<std::string>::lower_bound(key)_(existent)/8192 32.79 33.28 0.49 1.49 std::set<std::string>::lower_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -0.06 std::set<std::string>::lower_bound(key)_(non-existent)/1024 25.23 24.38 -0.85 -3.38 std::set<std::string>::lower_bound(key)_(non-existent)/32 11.45 11.68 0.24 2.07 std::set<std::string>::lower_bound(key)_(non-existent)/8192 32.30 36.80 4.50 13.95 std::set<std::string>::upper_bound(key)_(existent)/0 0.04 0.04 -0.00 -0.14 std::set<std::string>::upper_bound(key)_(existent)/1024 26.71 26.37 -0.34 -1.27 std::set<std::string>::upper_bound(key)_(existent)/32 20.07 19.06 -1.02 -5.06 std::set<std::string>::upper_bound(key)_(existent)/8192 36.69 35.50 -1.19 -3.25 std::set<std::string>::upper_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -0.16 std::set<std::string>::upper_bound(key)_(non-existent)/1024 24.48 24.90 0.42 1.73 std::set<std::string>::upper_bound(key)_(non-existent)/32 11.68 11.77 0.09 0.77 std::set<std::string>::upper_bound(key)_(non-existent)/8192 33.16 34.12 0.96 2.89 ```
2025-10-08 22:45:03 -08:00
return __lower_bound_multi(__v, __root(), __end_node());
}
[libc++] Optimize {set,map}::{lower,upper}_bound (#161366) Apple M4: ``` Benchmark Baseline Candidate Difference % Difference --------------------------------------------------------------------------------------------- ---------- ----------- ------------ -------------- std::map<int,_int>::lower_bound(key)_(existent)/0 0.01 0.01 -0.00 -25.78 std::map<int,_int>::lower_bound(key)_(existent)/1024 7.94 4.28 -3.66 -46.09 std::map<int,_int>::lower_bound(key)_(existent)/32 2.73 1.69 -1.03 -37.89 std::map<int,_int>::lower_bound(key)_(existent)/8192 11.63 5.52 -6.11 -52.55 std::map<int,_int>::lower_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -1.35 std::map<int,_int>::lower_bound(key)_(non-existent)/1024 17.21 7.63 -9.58 -55.67 std::map<int,_int>::lower_bound(key)_(non-existent)/32 4.71 3.26 -1.45 -30.71 std::map<int,_int>::lower_bound(key)_(non-existent)/8192 26.82 10.58 -16.24 -60.55 std::map<int,_int>::upper_bound(key)_(existent)/0 0.01 0.01 0.00 20.62 std::map<int,_int>::upper_bound(key)_(existent)/1024 7.93 3.61 -4.32 -54.49 std::map<int,_int>::upper_bound(key)_(existent)/32 2.83 1.98 -0.85 -30.01 std::map<int,_int>::upper_bound(key)_(existent)/8192 11.69 5.72 -5.97 -51.06 std::map<int,_int>::upper_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -1.36 std::map<int,_int>::upper_bound(key)_(non-existent)/1024 17.21 8.00 -9.21 -53.53 std::map<int,_int>::upper_bound(key)_(non-existent)/32 4.70 2.93 -1.78 -37.76 std::map<int,_int>::upper_bound(key)_(non-existent)/8192 26.54 11.18 -15.36 -57.89 std::map<std::string,_int>::lower_bound(key)_(existent)/0 0.04 0.04 -0.00 -3.26 std::map<std::string,_int>::lower_bound(key)_(existent)/1024 27.46 26.25 -1.22 -4.43 std::map<std::string,_int>::lower_bound(key)_(existent)/32 19.17 15.71 -3.46 -18.07 std::map<std::string,_int>::lower_bound(key)_(existent)/8192 35.33 35.03 -0.30 -0.84 std::map<std::string,_int>::lower_bound(key)_(non-existent)/0 0.27 0.27 -0.00 -1.45 std::map<std::string,_int>::lower_bound(key)_(non-existent)/1024 24.88 24.17 -0.70 -2.83 std::map<std::string,_int>::lower_bound(key)_(non-existent)/32 11.67 11.63 -0.04 -0.32 std::map<std::string,_int>::lower_bound(key)_(non-existent)/8192 31.81 32.33 0.52 1.64 std::map<std::string,_int>::upper_bound(key)_(existent)/0 0.04 0.04 -0.00 -2.22 std::map<std::string,_int>::upper_bound(key)_(existent)/1024 29.91 26.51 -3.40 -11.38 std::map<std::string,_int>::upper_bound(key)_(existent)/32 19.69 17.74 -1.95 -9.92 std::map<std::string,_int>::upper_bound(key)_(existent)/8192 32.55 35.24 2.69 8.25 std::map<std::string,_int>::upper_bound(key)_(non-existent)/0 0.27 0.27 -0.00 -1.74 std::map<std::string,_int>::upper_bound(key)_(non-existent)/1024 23.87 26.77 2.91 12.18 std::map<std::string,_int>::upper_bound(key)_(non-existent)/32 11.44 11.81 0.37 3.24 std::map<std::string,_int>::upper_bound(key)_(non-existent)/8192 33.02 32.59 -0.43 -1.29 std::set<int>::lower_bound(key)_(existent)/0 0.01 0.01 0.00 0.48 std::set<int>::lower_bound(key)_(existent)/1024 7.83 4.21 -3.62 -46.23 std::set<int>::lower_bound(key)_(existent)/32 2.74 1.68 -1.06 -38.81 std::set<int>::lower_bound(key)_(existent)/8192 22.75 11.12 -11.63 -51.12 std::set<int>::lower_bound(key)_(non-existent)/0 0.28 0.27 -0.01 -3.52 std::set<int>::lower_bound(key)_(non-existent)/1024 17.15 8.40 -8.75 -51.03 std::set<int>::lower_bound(key)_(non-existent)/32 4.63 2.50 -2.13 -46.03 std::set<int>::lower_bound(key)_(non-existent)/8192 28.88 11.05 -17.82 -61.72 std::set<int>::upper_bound(key)_(existent)/0 0.01 0.01 -0.00 -7.79 std::set<int>::upper_bound(key)_(existent)/1024 7.80 3.63 -4.16 -53.42 std::set<int>::upper_bound(key)_(existent)/32 2.81 1.90 -0.91 -32.44 std::set<int>::upper_bound(key)_(existent)/8192 21.93 11.35 -10.58 -48.26 std::set<int>::upper_bound(key)_(non-existent)/0 0.28 0.27 -0.01 -3.81 std::set<int>::upper_bound(key)_(non-existent)/1024 16.76 7.38 -9.38 -55.98 std::set<int>::upper_bound(key)_(non-existent)/32 4.58 3.10 -1.48 -32.31 std::set<int>::upper_bound(key)_(non-existent)/8192 26.95 10.70 -16.25 -60.29 std::set<std::string>::lower_bound(key)_(existent)/0 0.04 0.04 0.00 0.02 std::set<std::string>::lower_bound(key)_(existent)/1024 28.08 27.04 -1.04 -3.71 std::set<std::string>::lower_bound(key)_(existent)/32 17.53 16.94 -0.58 -3.34 std::set<std::string>::lower_bound(key)_(existent)/8192 32.79 33.28 0.49 1.49 std::set<std::string>::lower_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -0.06 std::set<std::string>::lower_bound(key)_(non-existent)/1024 25.23 24.38 -0.85 -3.38 std::set<std::string>::lower_bound(key)_(non-existent)/32 11.45 11.68 0.24 2.07 std::set<std::string>::lower_bound(key)_(non-existent)/8192 32.30 36.80 4.50 13.95 std::set<std::string>::upper_bound(key)_(existent)/0 0.04 0.04 -0.00 -0.14 std::set<std::string>::upper_bound(key)_(existent)/1024 26.71 26.37 -0.34 -1.27 std::set<std::string>::upper_bound(key)_(existent)/32 20.07 19.06 -1.02 -5.06 std::set<std::string>::upper_bound(key)_(existent)/8192 36.69 35.50 -1.19 -3.25 std::set<std::string>::upper_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -0.16 std::set<std::string>::upper_bound(key)_(non-existent)/1024 24.48 24.90 0.42 1.73 std::set<std::string>::upper_bound(key)_(non-existent)/32 11.68 11.77 0.09 0.77 std::set<std::string>::upper_bound(key)_(non-existent)/8192 33.16 34.12 0.96 2.89 ```
2025-10-08 22:45:03 -08:00
2010-05-11 11:42:16 -08:00
template <class _Key>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator __upper_bound_multi(const _Key& __v) {
[libc++] Optimize {set,map}::{lower,upper}_bound (#161366) Apple M4: ``` Benchmark Baseline Candidate Difference % Difference --------------------------------------------------------------------------------------------- ---------- ----------- ------------ -------------- std::map<int,_int>::lower_bound(key)_(existent)/0 0.01 0.01 -0.00 -25.78 std::map<int,_int>::lower_bound(key)_(existent)/1024 7.94 4.28 -3.66 -46.09 std::map<int,_int>::lower_bound(key)_(existent)/32 2.73 1.69 -1.03 -37.89 std::map<int,_int>::lower_bound(key)_(existent)/8192 11.63 5.52 -6.11 -52.55 std::map<int,_int>::lower_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -1.35 std::map<int,_int>::lower_bound(key)_(non-existent)/1024 17.21 7.63 -9.58 -55.67 std::map<int,_int>::lower_bound(key)_(non-existent)/32 4.71 3.26 -1.45 -30.71 std::map<int,_int>::lower_bound(key)_(non-existent)/8192 26.82 10.58 -16.24 -60.55 std::map<int,_int>::upper_bound(key)_(existent)/0 0.01 0.01 0.00 20.62 std::map<int,_int>::upper_bound(key)_(existent)/1024 7.93 3.61 -4.32 -54.49 std::map<int,_int>::upper_bound(key)_(existent)/32 2.83 1.98 -0.85 -30.01 std::map<int,_int>::upper_bound(key)_(existent)/8192 11.69 5.72 -5.97 -51.06 std::map<int,_int>::upper_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -1.36 std::map<int,_int>::upper_bound(key)_(non-existent)/1024 17.21 8.00 -9.21 -53.53 std::map<int,_int>::upper_bound(key)_(non-existent)/32 4.70 2.93 -1.78 -37.76 std::map<int,_int>::upper_bound(key)_(non-existent)/8192 26.54 11.18 -15.36 -57.89 std::map<std::string,_int>::lower_bound(key)_(existent)/0 0.04 0.04 -0.00 -3.26 std::map<std::string,_int>::lower_bound(key)_(existent)/1024 27.46 26.25 -1.22 -4.43 std::map<std::string,_int>::lower_bound(key)_(existent)/32 19.17 15.71 -3.46 -18.07 std::map<std::string,_int>::lower_bound(key)_(existent)/8192 35.33 35.03 -0.30 -0.84 std::map<std::string,_int>::lower_bound(key)_(non-existent)/0 0.27 0.27 -0.00 -1.45 std::map<std::string,_int>::lower_bound(key)_(non-existent)/1024 24.88 24.17 -0.70 -2.83 std::map<std::string,_int>::lower_bound(key)_(non-existent)/32 11.67 11.63 -0.04 -0.32 std::map<std::string,_int>::lower_bound(key)_(non-existent)/8192 31.81 32.33 0.52 1.64 std::map<std::string,_int>::upper_bound(key)_(existent)/0 0.04 0.04 -0.00 -2.22 std::map<std::string,_int>::upper_bound(key)_(existent)/1024 29.91 26.51 -3.40 -11.38 std::map<std::string,_int>::upper_bound(key)_(existent)/32 19.69 17.74 -1.95 -9.92 std::map<std::string,_int>::upper_bound(key)_(existent)/8192 32.55 35.24 2.69 8.25 std::map<std::string,_int>::upper_bound(key)_(non-existent)/0 0.27 0.27 -0.00 -1.74 std::map<std::string,_int>::upper_bound(key)_(non-existent)/1024 23.87 26.77 2.91 12.18 std::map<std::string,_int>::upper_bound(key)_(non-existent)/32 11.44 11.81 0.37 3.24 std::map<std::string,_int>::upper_bound(key)_(non-existent)/8192 33.02 32.59 -0.43 -1.29 std::set<int>::lower_bound(key)_(existent)/0 0.01 0.01 0.00 0.48 std::set<int>::lower_bound(key)_(existent)/1024 7.83 4.21 -3.62 -46.23 std::set<int>::lower_bound(key)_(existent)/32 2.74 1.68 -1.06 -38.81 std::set<int>::lower_bound(key)_(existent)/8192 22.75 11.12 -11.63 -51.12 std::set<int>::lower_bound(key)_(non-existent)/0 0.28 0.27 -0.01 -3.52 std::set<int>::lower_bound(key)_(non-existent)/1024 17.15 8.40 -8.75 -51.03 std::set<int>::lower_bound(key)_(non-existent)/32 4.63 2.50 -2.13 -46.03 std::set<int>::lower_bound(key)_(non-existent)/8192 28.88 11.05 -17.82 -61.72 std::set<int>::upper_bound(key)_(existent)/0 0.01 0.01 -0.00 -7.79 std::set<int>::upper_bound(key)_(existent)/1024 7.80 3.63 -4.16 -53.42 std::set<int>::upper_bound(key)_(existent)/32 2.81 1.90 -0.91 -32.44 std::set<int>::upper_bound(key)_(existent)/8192 21.93 11.35 -10.58 -48.26 std::set<int>::upper_bound(key)_(non-existent)/0 0.28 0.27 -0.01 -3.81 std::set<int>::upper_bound(key)_(non-existent)/1024 16.76 7.38 -9.38 -55.98 std::set<int>::upper_bound(key)_(non-existent)/32 4.58 3.10 -1.48 -32.31 std::set<int>::upper_bound(key)_(non-existent)/8192 26.95 10.70 -16.25 -60.29 std::set<std::string>::lower_bound(key)_(existent)/0 0.04 0.04 0.00 0.02 std::set<std::string>::lower_bound(key)_(existent)/1024 28.08 27.04 -1.04 -3.71 std::set<std::string>::lower_bound(key)_(existent)/32 17.53 16.94 -0.58 -3.34 std::set<std::string>::lower_bound(key)_(existent)/8192 32.79 33.28 0.49 1.49 std::set<std::string>::lower_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -0.06 std::set<std::string>::lower_bound(key)_(non-existent)/1024 25.23 24.38 -0.85 -3.38 std::set<std::string>::lower_bound(key)_(non-existent)/32 11.45 11.68 0.24 2.07 std::set<std::string>::lower_bound(key)_(non-existent)/8192 32.30 36.80 4.50 13.95 std::set<std::string>::upper_bound(key)_(existent)/0 0.04 0.04 -0.00 -0.14 std::set<std::string>::upper_bound(key)_(existent)/1024 26.71 26.37 -0.34 -1.27 std::set<std::string>::upper_bound(key)_(existent)/32 20.07 19.06 -1.02 -5.06 std::set<std::string>::upper_bound(key)_(existent)/8192 36.69 35.50 -1.19 -3.25 std::set<std::string>::upper_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -0.16 std::set<std::string>::upper_bound(key)_(non-existent)/1024 24.48 24.90 0.42 1.73 std::set<std::string>::upper_bound(key)_(non-existent)/32 11.68 11.77 0.09 0.77 std::set<std::string>::upper_bound(key)_(non-existent)/8192 33.16 34.12 0.96 2.89 ```
2025-10-08 22:45:03 -08:00
return __upper_bound_multi(__v, __root(), __end_node());
}
2010-05-11 11:42:16 -08:00
template <class _Key>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator __upper_bound_multi(const _Key& __v) const {
[libc++] Optimize {set,map}::{lower,upper}_bound (#161366) Apple M4: ``` Benchmark Baseline Candidate Difference % Difference --------------------------------------------------------------------------------------------- ---------- ----------- ------------ -------------- std::map<int,_int>::lower_bound(key)_(existent)/0 0.01 0.01 -0.00 -25.78 std::map<int,_int>::lower_bound(key)_(existent)/1024 7.94 4.28 -3.66 -46.09 std::map<int,_int>::lower_bound(key)_(existent)/32 2.73 1.69 -1.03 -37.89 std::map<int,_int>::lower_bound(key)_(existent)/8192 11.63 5.52 -6.11 -52.55 std::map<int,_int>::lower_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -1.35 std::map<int,_int>::lower_bound(key)_(non-existent)/1024 17.21 7.63 -9.58 -55.67 std::map<int,_int>::lower_bound(key)_(non-existent)/32 4.71 3.26 -1.45 -30.71 std::map<int,_int>::lower_bound(key)_(non-existent)/8192 26.82 10.58 -16.24 -60.55 std::map<int,_int>::upper_bound(key)_(existent)/0 0.01 0.01 0.00 20.62 std::map<int,_int>::upper_bound(key)_(existent)/1024 7.93 3.61 -4.32 -54.49 std::map<int,_int>::upper_bound(key)_(existent)/32 2.83 1.98 -0.85 -30.01 std::map<int,_int>::upper_bound(key)_(existent)/8192 11.69 5.72 -5.97 -51.06 std::map<int,_int>::upper_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -1.36 std::map<int,_int>::upper_bound(key)_(non-existent)/1024 17.21 8.00 -9.21 -53.53 std::map<int,_int>::upper_bound(key)_(non-existent)/32 4.70 2.93 -1.78 -37.76 std::map<int,_int>::upper_bound(key)_(non-existent)/8192 26.54 11.18 -15.36 -57.89 std::map<std::string,_int>::lower_bound(key)_(existent)/0 0.04 0.04 -0.00 -3.26 std::map<std::string,_int>::lower_bound(key)_(existent)/1024 27.46 26.25 -1.22 -4.43 std::map<std::string,_int>::lower_bound(key)_(existent)/32 19.17 15.71 -3.46 -18.07 std::map<std::string,_int>::lower_bound(key)_(existent)/8192 35.33 35.03 -0.30 -0.84 std::map<std::string,_int>::lower_bound(key)_(non-existent)/0 0.27 0.27 -0.00 -1.45 std::map<std::string,_int>::lower_bound(key)_(non-existent)/1024 24.88 24.17 -0.70 -2.83 std::map<std::string,_int>::lower_bound(key)_(non-existent)/32 11.67 11.63 -0.04 -0.32 std::map<std::string,_int>::lower_bound(key)_(non-existent)/8192 31.81 32.33 0.52 1.64 std::map<std::string,_int>::upper_bound(key)_(existent)/0 0.04 0.04 -0.00 -2.22 std::map<std::string,_int>::upper_bound(key)_(existent)/1024 29.91 26.51 -3.40 -11.38 std::map<std::string,_int>::upper_bound(key)_(existent)/32 19.69 17.74 -1.95 -9.92 std::map<std::string,_int>::upper_bound(key)_(existent)/8192 32.55 35.24 2.69 8.25 std::map<std::string,_int>::upper_bound(key)_(non-existent)/0 0.27 0.27 -0.00 -1.74 std::map<std::string,_int>::upper_bound(key)_(non-existent)/1024 23.87 26.77 2.91 12.18 std::map<std::string,_int>::upper_bound(key)_(non-existent)/32 11.44 11.81 0.37 3.24 std::map<std::string,_int>::upper_bound(key)_(non-existent)/8192 33.02 32.59 -0.43 -1.29 std::set<int>::lower_bound(key)_(existent)/0 0.01 0.01 0.00 0.48 std::set<int>::lower_bound(key)_(existent)/1024 7.83 4.21 -3.62 -46.23 std::set<int>::lower_bound(key)_(existent)/32 2.74 1.68 -1.06 -38.81 std::set<int>::lower_bound(key)_(existent)/8192 22.75 11.12 -11.63 -51.12 std::set<int>::lower_bound(key)_(non-existent)/0 0.28 0.27 -0.01 -3.52 std::set<int>::lower_bound(key)_(non-existent)/1024 17.15 8.40 -8.75 -51.03 std::set<int>::lower_bound(key)_(non-existent)/32 4.63 2.50 -2.13 -46.03 std::set<int>::lower_bound(key)_(non-existent)/8192 28.88 11.05 -17.82 -61.72 std::set<int>::upper_bound(key)_(existent)/0 0.01 0.01 -0.00 -7.79 std::set<int>::upper_bound(key)_(existent)/1024 7.80 3.63 -4.16 -53.42 std::set<int>::upper_bound(key)_(existent)/32 2.81 1.90 -0.91 -32.44 std::set<int>::upper_bound(key)_(existent)/8192 21.93 11.35 -10.58 -48.26 std::set<int>::upper_bound(key)_(non-existent)/0 0.28 0.27 -0.01 -3.81 std::set<int>::upper_bound(key)_(non-existent)/1024 16.76 7.38 -9.38 -55.98 std::set<int>::upper_bound(key)_(non-existent)/32 4.58 3.10 -1.48 -32.31 std::set<int>::upper_bound(key)_(non-existent)/8192 26.95 10.70 -16.25 -60.29 std::set<std::string>::lower_bound(key)_(existent)/0 0.04 0.04 0.00 0.02 std::set<std::string>::lower_bound(key)_(existent)/1024 28.08 27.04 -1.04 -3.71 std::set<std::string>::lower_bound(key)_(existent)/32 17.53 16.94 -0.58 -3.34 std::set<std::string>::lower_bound(key)_(existent)/8192 32.79 33.28 0.49 1.49 std::set<std::string>::lower_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -0.06 std::set<std::string>::lower_bound(key)_(non-existent)/1024 25.23 24.38 -0.85 -3.38 std::set<std::string>::lower_bound(key)_(non-existent)/32 11.45 11.68 0.24 2.07 std::set<std::string>::lower_bound(key)_(non-existent)/8192 32.30 36.80 4.50 13.95 std::set<std::string>::upper_bound(key)_(existent)/0 0.04 0.04 -0.00 -0.14 std::set<std::string>::upper_bound(key)_(existent)/1024 26.71 26.37 -0.34 -1.27 std::set<std::string>::upper_bound(key)_(existent)/32 20.07 19.06 -1.02 -5.06 std::set<std::string>::upper_bound(key)_(existent)/8192 36.69 35.50 -1.19 -3.25 std::set<std::string>::upper_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -0.16 std::set<std::string>::upper_bound(key)_(non-existent)/1024 24.48 24.90 0.42 1.73 std::set<std::string>::upper_bound(key)_(non-existent)/32 11.68 11.77 0.09 0.77 std::set<std::string>::upper_bound(key)_(non-existent)/8192 33.16 34.12 0.96 2.89 ```
2025-10-08 22:45:03 -08:00
return __upper_bound_multi(__v, __root(), __end_node());
2010-05-11 11:42:16 -08:00
}
[libc++] Optimize {set,map}::{lower,upper}_bound (#161366) Apple M4: ``` Benchmark Baseline Candidate Difference % Difference --------------------------------------------------------------------------------------------- ---------- ----------- ------------ -------------- std::map<int,_int>::lower_bound(key)_(existent)/0 0.01 0.01 -0.00 -25.78 std::map<int,_int>::lower_bound(key)_(existent)/1024 7.94 4.28 -3.66 -46.09 std::map<int,_int>::lower_bound(key)_(existent)/32 2.73 1.69 -1.03 -37.89 std::map<int,_int>::lower_bound(key)_(existent)/8192 11.63 5.52 -6.11 -52.55 std::map<int,_int>::lower_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -1.35 std::map<int,_int>::lower_bound(key)_(non-existent)/1024 17.21 7.63 -9.58 -55.67 std::map<int,_int>::lower_bound(key)_(non-existent)/32 4.71 3.26 -1.45 -30.71 std::map<int,_int>::lower_bound(key)_(non-existent)/8192 26.82 10.58 -16.24 -60.55 std::map<int,_int>::upper_bound(key)_(existent)/0 0.01 0.01 0.00 20.62 std::map<int,_int>::upper_bound(key)_(existent)/1024 7.93 3.61 -4.32 -54.49 std::map<int,_int>::upper_bound(key)_(existent)/32 2.83 1.98 -0.85 -30.01 std::map<int,_int>::upper_bound(key)_(existent)/8192 11.69 5.72 -5.97 -51.06 std::map<int,_int>::upper_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -1.36 std::map<int,_int>::upper_bound(key)_(non-existent)/1024 17.21 8.00 -9.21 -53.53 std::map<int,_int>::upper_bound(key)_(non-existent)/32 4.70 2.93 -1.78 -37.76 std::map<int,_int>::upper_bound(key)_(non-existent)/8192 26.54 11.18 -15.36 -57.89 std::map<std::string,_int>::lower_bound(key)_(existent)/0 0.04 0.04 -0.00 -3.26 std::map<std::string,_int>::lower_bound(key)_(existent)/1024 27.46 26.25 -1.22 -4.43 std::map<std::string,_int>::lower_bound(key)_(existent)/32 19.17 15.71 -3.46 -18.07 std::map<std::string,_int>::lower_bound(key)_(existent)/8192 35.33 35.03 -0.30 -0.84 std::map<std::string,_int>::lower_bound(key)_(non-existent)/0 0.27 0.27 -0.00 -1.45 std::map<std::string,_int>::lower_bound(key)_(non-existent)/1024 24.88 24.17 -0.70 -2.83 std::map<std::string,_int>::lower_bound(key)_(non-existent)/32 11.67 11.63 -0.04 -0.32 std::map<std::string,_int>::lower_bound(key)_(non-existent)/8192 31.81 32.33 0.52 1.64 std::map<std::string,_int>::upper_bound(key)_(existent)/0 0.04 0.04 -0.00 -2.22 std::map<std::string,_int>::upper_bound(key)_(existent)/1024 29.91 26.51 -3.40 -11.38 std::map<std::string,_int>::upper_bound(key)_(existent)/32 19.69 17.74 -1.95 -9.92 std::map<std::string,_int>::upper_bound(key)_(existent)/8192 32.55 35.24 2.69 8.25 std::map<std::string,_int>::upper_bound(key)_(non-existent)/0 0.27 0.27 -0.00 -1.74 std::map<std::string,_int>::upper_bound(key)_(non-existent)/1024 23.87 26.77 2.91 12.18 std::map<std::string,_int>::upper_bound(key)_(non-existent)/32 11.44 11.81 0.37 3.24 std::map<std::string,_int>::upper_bound(key)_(non-existent)/8192 33.02 32.59 -0.43 -1.29 std::set<int>::lower_bound(key)_(existent)/0 0.01 0.01 0.00 0.48 std::set<int>::lower_bound(key)_(existent)/1024 7.83 4.21 -3.62 -46.23 std::set<int>::lower_bound(key)_(existent)/32 2.74 1.68 -1.06 -38.81 std::set<int>::lower_bound(key)_(existent)/8192 22.75 11.12 -11.63 -51.12 std::set<int>::lower_bound(key)_(non-existent)/0 0.28 0.27 -0.01 -3.52 std::set<int>::lower_bound(key)_(non-existent)/1024 17.15 8.40 -8.75 -51.03 std::set<int>::lower_bound(key)_(non-existent)/32 4.63 2.50 -2.13 -46.03 std::set<int>::lower_bound(key)_(non-existent)/8192 28.88 11.05 -17.82 -61.72 std::set<int>::upper_bound(key)_(existent)/0 0.01 0.01 -0.00 -7.79 std::set<int>::upper_bound(key)_(existent)/1024 7.80 3.63 -4.16 -53.42 std::set<int>::upper_bound(key)_(existent)/32 2.81 1.90 -0.91 -32.44 std::set<int>::upper_bound(key)_(existent)/8192 21.93 11.35 -10.58 -48.26 std::set<int>::upper_bound(key)_(non-existent)/0 0.28 0.27 -0.01 -3.81 std::set<int>::upper_bound(key)_(non-existent)/1024 16.76 7.38 -9.38 -55.98 std::set<int>::upper_bound(key)_(non-existent)/32 4.58 3.10 -1.48 -32.31 std::set<int>::upper_bound(key)_(non-existent)/8192 26.95 10.70 -16.25 -60.29 std::set<std::string>::lower_bound(key)_(existent)/0 0.04 0.04 0.00 0.02 std::set<std::string>::lower_bound(key)_(existent)/1024 28.08 27.04 -1.04 -3.71 std::set<std::string>::lower_bound(key)_(existent)/32 17.53 16.94 -0.58 -3.34 std::set<std::string>::lower_bound(key)_(existent)/8192 32.79 33.28 0.49 1.49 std::set<std::string>::lower_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -0.06 std::set<std::string>::lower_bound(key)_(non-existent)/1024 25.23 24.38 -0.85 -3.38 std::set<std::string>::lower_bound(key)_(non-existent)/32 11.45 11.68 0.24 2.07 std::set<std::string>::lower_bound(key)_(non-existent)/8192 32.30 36.80 4.50 13.95 std::set<std::string>::upper_bound(key)_(existent)/0 0.04 0.04 -0.00 -0.14 std::set<std::string>::upper_bound(key)_(existent)/1024 26.71 26.37 -0.34 -1.27 std::set<std::string>::upper_bound(key)_(existent)/32 20.07 19.06 -1.02 -5.06 std::set<std::string>::upper_bound(key)_(existent)/8192 36.69 35.50 -1.19 -3.25 std::set<std::string>::upper_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -0.16 std::set<std::string>::upper_bound(key)_(non-existent)/1024 24.48 24.90 0.42 1.73 std::set<std::string>::upper_bound(key)_(non-existent)/32 11.68 11.77 0.09 0.77 std::set<std::string>::upper_bound(key)_(non-existent)/8192 33.16 34.12 0.96 2.89 ```
2025-10-08 22:45:03 -08:00
private:
template <class _Key>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator
[libc++] Optimize {set,map}::{lower,upper}_bound (#161366) Apple M4: ``` Benchmark Baseline Candidate Difference % Difference --------------------------------------------------------------------------------------------- ---------- ----------- ------------ -------------- std::map<int,_int>::lower_bound(key)_(existent)/0 0.01 0.01 -0.00 -25.78 std::map<int,_int>::lower_bound(key)_(existent)/1024 7.94 4.28 -3.66 -46.09 std::map<int,_int>::lower_bound(key)_(existent)/32 2.73 1.69 -1.03 -37.89 std::map<int,_int>::lower_bound(key)_(existent)/8192 11.63 5.52 -6.11 -52.55 std::map<int,_int>::lower_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -1.35 std::map<int,_int>::lower_bound(key)_(non-existent)/1024 17.21 7.63 -9.58 -55.67 std::map<int,_int>::lower_bound(key)_(non-existent)/32 4.71 3.26 -1.45 -30.71 std::map<int,_int>::lower_bound(key)_(non-existent)/8192 26.82 10.58 -16.24 -60.55 std::map<int,_int>::upper_bound(key)_(existent)/0 0.01 0.01 0.00 20.62 std::map<int,_int>::upper_bound(key)_(existent)/1024 7.93 3.61 -4.32 -54.49 std::map<int,_int>::upper_bound(key)_(existent)/32 2.83 1.98 -0.85 -30.01 std::map<int,_int>::upper_bound(key)_(existent)/8192 11.69 5.72 -5.97 -51.06 std::map<int,_int>::upper_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -1.36 std::map<int,_int>::upper_bound(key)_(non-existent)/1024 17.21 8.00 -9.21 -53.53 std::map<int,_int>::upper_bound(key)_(non-existent)/32 4.70 2.93 -1.78 -37.76 std::map<int,_int>::upper_bound(key)_(non-existent)/8192 26.54 11.18 -15.36 -57.89 std::map<std::string,_int>::lower_bound(key)_(existent)/0 0.04 0.04 -0.00 -3.26 std::map<std::string,_int>::lower_bound(key)_(existent)/1024 27.46 26.25 -1.22 -4.43 std::map<std::string,_int>::lower_bound(key)_(existent)/32 19.17 15.71 -3.46 -18.07 std::map<std::string,_int>::lower_bound(key)_(existent)/8192 35.33 35.03 -0.30 -0.84 std::map<std::string,_int>::lower_bound(key)_(non-existent)/0 0.27 0.27 -0.00 -1.45 std::map<std::string,_int>::lower_bound(key)_(non-existent)/1024 24.88 24.17 -0.70 -2.83 std::map<std::string,_int>::lower_bound(key)_(non-existent)/32 11.67 11.63 -0.04 -0.32 std::map<std::string,_int>::lower_bound(key)_(non-existent)/8192 31.81 32.33 0.52 1.64 std::map<std::string,_int>::upper_bound(key)_(existent)/0 0.04 0.04 -0.00 -2.22 std::map<std::string,_int>::upper_bound(key)_(existent)/1024 29.91 26.51 -3.40 -11.38 std::map<std::string,_int>::upper_bound(key)_(existent)/32 19.69 17.74 -1.95 -9.92 std::map<std::string,_int>::upper_bound(key)_(existent)/8192 32.55 35.24 2.69 8.25 std::map<std::string,_int>::upper_bound(key)_(non-existent)/0 0.27 0.27 -0.00 -1.74 std::map<std::string,_int>::upper_bound(key)_(non-existent)/1024 23.87 26.77 2.91 12.18 std::map<std::string,_int>::upper_bound(key)_(non-existent)/32 11.44 11.81 0.37 3.24 std::map<std::string,_int>::upper_bound(key)_(non-existent)/8192 33.02 32.59 -0.43 -1.29 std::set<int>::lower_bound(key)_(existent)/0 0.01 0.01 0.00 0.48 std::set<int>::lower_bound(key)_(existent)/1024 7.83 4.21 -3.62 -46.23 std::set<int>::lower_bound(key)_(existent)/32 2.74 1.68 -1.06 -38.81 std::set<int>::lower_bound(key)_(existent)/8192 22.75 11.12 -11.63 -51.12 std::set<int>::lower_bound(key)_(non-existent)/0 0.28 0.27 -0.01 -3.52 std::set<int>::lower_bound(key)_(non-existent)/1024 17.15 8.40 -8.75 -51.03 std::set<int>::lower_bound(key)_(non-existent)/32 4.63 2.50 -2.13 -46.03 std::set<int>::lower_bound(key)_(non-existent)/8192 28.88 11.05 -17.82 -61.72 std::set<int>::upper_bound(key)_(existent)/0 0.01 0.01 -0.00 -7.79 std::set<int>::upper_bound(key)_(existent)/1024 7.80 3.63 -4.16 -53.42 std::set<int>::upper_bound(key)_(existent)/32 2.81 1.90 -0.91 -32.44 std::set<int>::upper_bound(key)_(existent)/8192 21.93 11.35 -10.58 -48.26 std::set<int>::upper_bound(key)_(non-existent)/0 0.28 0.27 -0.01 -3.81 std::set<int>::upper_bound(key)_(non-existent)/1024 16.76 7.38 -9.38 -55.98 std::set<int>::upper_bound(key)_(non-existent)/32 4.58 3.10 -1.48 -32.31 std::set<int>::upper_bound(key)_(non-existent)/8192 26.95 10.70 -16.25 -60.29 std::set<std::string>::lower_bound(key)_(existent)/0 0.04 0.04 0.00 0.02 std::set<std::string>::lower_bound(key)_(existent)/1024 28.08 27.04 -1.04 -3.71 std::set<std::string>::lower_bound(key)_(existent)/32 17.53 16.94 -0.58 -3.34 std::set<std::string>::lower_bound(key)_(existent)/8192 32.79 33.28 0.49 1.49 std::set<std::string>::lower_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -0.06 std::set<std::string>::lower_bound(key)_(non-existent)/1024 25.23 24.38 -0.85 -3.38 std::set<std::string>::lower_bound(key)_(non-existent)/32 11.45 11.68 0.24 2.07 std::set<std::string>::lower_bound(key)_(non-existent)/8192 32.30 36.80 4.50 13.95 std::set<std::string>::upper_bound(key)_(existent)/0 0.04 0.04 -0.00 -0.14 std::set<std::string>::upper_bound(key)_(existent)/1024 26.71 26.37 -0.34 -1.27 std::set<std::string>::upper_bound(key)_(existent)/32 20.07 19.06 -1.02 -5.06 std::set<std::string>::upper_bound(key)_(existent)/8192 36.69 35.50 -1.19 -3.25 std::set<std::string>::upper_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -0.16 std::set<std::string>::upper_bound(key)_(non-existent)/1024 24.48 24.90 0.42 1.73 std::set<std::string>::upper_bound(key)_(non-existent)/32 11.68 11.77 0.09 0.77 std::set<std::string>::upper_bound(key)_(non-existent)/8192 33.16 34.12 0.96 2.89 ```
2025-10-08 22:45:03 -08:00
__upper_bound_multi(const _Key& __v, __node_pointer __root, __end_node_pointer __result);
2010-05-11 11:42:16 -08:00
template <class _Key>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 const_iterator
[libc++] Optimize {set,map}::{lower,upper}_bound (#161366) Apple M4: ``` Benchmark Baseline Candidate Difference % Difference --------------------------------------------------------------------------------------------- ---------- ----------- ------------ -------------- std::map<int,_int>::lower_bound(key)_(existent)/0 0.01 0.01 -0.00 -25.78 std::map<int,_int>::lower_bound(key)_(existent)/1024 7.94 4.28 -3.66 -46.09 std::map<int,_int>::lower_bound(key)_(existent)/32 2.73 1.69 -1.03 -37.89 std::map<int,_int>::lower_bound(key)_(existent)/8192 11.63 5.52 -6.11 -52.55 std::map<int,_int>::lower_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -1.35 std::map<int,_int>::lower_bound(key)_(non-existent)/1024 17.21 7.63 -9.58 -55.67 std::map<int,_int>::lower_bound(key)_(non-existent)/32 4.71 3.26 -1.45 -30.71 std::map<int,_int>::lower_bound(key)_(non-existent)/8192 26.82 10.58 -16.24 -60.55 std::map<int,_int>::upper_bound(key)_(existent)/0 0.01 0.01 0.00 20.62 std::map<int,_int>::upper_bound(key)_(existent)/1024 7.93 3.61 -4.32 -54.49 std::map<int,_int>::upper_bound(key)_(existent)/32 2.83 1.98 -0.85 -30.01 std::map<int,_int>::upper_bound(key)_(existent)/8192 11.69 5.72 -5.97 -51.06 std::map<int,_int>::upper_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -1.36 std::map<int,_int>::upper_bound(key)_(non-existent)/1024 17.21 8.00 -9.21 -53.53 std::map<int,_int>::upper_bound(key)_(non-existent)/32 4.70 2.93 -1.78 -37.76 std::map<int,_int>::upper_bound(key)_(non-existent)/8192 26.54 11.18 -15.36 -57.89 std::map<std::string,_int>::lower_bound(key)_(existent)/0 0.04 0.04 -0.00 -3.26 std::map<std::string,_int>::lower_bound(key)_(existent)/1024 27.46 26.25 -1.22 -4.43 std::map<std::string,_int>::lower_bound(key)_(existent)/32 19.17 15.71 -3.46 -18.07 std::map<std::string,_int>::lower_bound(key)_(existent)/8192 35.33 35.03 -0.30 -0.84 std::map<std::string,_int>::lower_bound(key)_(non-existent)/0 0.27 0.27 -0.00 -1.45 std::map<std::string,_int>::lower_bound(key)_(non-existent)/1024 24.88 24.17 -0.70 -2.83 std::map<std::string,_int>::lower_bound(key)_(non-existent)/32 11.67 11.63 -0.04 -0.32 std::map<std::string,_int>::lower_bound(key)_(non-existent)/8192 31.81 32.33 0.52 1.64 std::map<std::string,_int>::upper_bound(key)_(existent)/0 0.04 0.04 -0.00 -2.22 std::map<std::string,_int>::upper_bound(key)_(existent)/1024 29.91 26.51 -3.40 -11.38 std::map<std::string,_int>::upper_bound(key)_(existent)/32 19.69 17.74 -1.95 -9.92 std::map<std::string,_int>::upper_bound(key)_(existent)/8192 32.55 35.24 2.69 8.25 std::map<std::string,_int>::upper_bound(key)_(non-existent)/0 0.27 0.27 -0.00 -1.74 std::map<std::string,_int>::upper_bound(key)_(non-existent)/1024 23.87 26.77 2.91 12.18 std::map<std::string,_int>::upper_bound(key)_(non-existent)/32 11.44 11.81 0.37 3.24 std::map<std::string,_int>::upper_bound(key)_(non-existent)/8192 33.02 32.59 -0.43 -1.29 std::set<int>::lower_bound(key)_(existent)/0 0.01 0.01 0.00 0.48 std::set<int>::lower_bound(key)_(existent)/1024 7.83 4.21 -3.62 -46.23 std::set<int>::lower_bound(key)_(existent)/32 2.74 1.68 -1.06 -38.81 std::set<int>::lower_bound(key)_(existent)/8192 22.75 11.12 -11.63 -51.12 std::set<int>::lower_bound(key)_(non-existent)/0 0.28 0.27 -0.01 -3.52 std::set<int>::lower_bound(key)_(non-existent)/1024 17.15 8.40 -8.75 -51.03 std::set<int>::lower_bound(key)_(non-existent)/32 4.63 2.50 -2.13 -46.03 std::set<int>::lower_bound(key)_(non-existent)/8192 28.88 11.05 -17.82 -61.72 std::set<int>::upper_bound(key)_(existent)/0 0.01 0.01 -0.00 -7.79 std::set<int>::upper_bound(key)_(existent)/1024 7.80 3.63 -4.16 -53.42 std::set<int>::upper_bound(key)_(existent)/32 2.81 1.90 -0.91 -32.44 std::set<int>::upper_bound(key)_(existent)/8192 21.93 11.35 -10.58 -48.26 std::set<int>::upper_bound(key)_(non-existent)/0 0.28 0.27 -0.01 -3.81 std::set<int>::upper_bound(key)_(non-existent)/1024 16.76 7.38 -9.38 -55.98 std::set<int>::upper_bound(key)_(non-existent)/32 4.58 3.10 -1.48 -32.31 std::set<int>::upper_bound(key)_(non-existent)/8192 26.95 10.70 -16.25 -60.29 std::set<std::string>::lower_bound(key)_(existent)/0 0.04 0.04 0.00 0.02 std::set<std::string>::lower_bound(key)_(existent)/1024 28.08 27.04 -1.04 -3.71 std::set<std::string>::lower_bound(key)_(existent)/32 17.53 16.94 -0.58 -3.34 std::set<std::string>::lower_bound(key)_(existent)/8192 32.79 33.28 0.49 1.49 std::set<std::string>::lower_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -0.06 std::set<std::string>::lower_bound(key)_(non-existent)/1024 25.23 24.38 -0.85 -3.38 std::set<std::string>::lower_bound(key)_(non-existent)/32 11.45 11.68 0.24 2.07 std::set<std::string>::lower_bound(key)_(non-existent)/8192 32.30 36.80 4.50 13.95 std::set<std::string>::upper_bound(key)_(existent)/0 0.04 0.04 -0.00 -0.14 std::set<std::string>::upper_bound(key)_(existent)/1024 26.71 26.37 -0.34 -1.27 std::set<std::string>::upper_bound(key)_(existent)/32 20.07 19.06 -1.02 -5.06 std::set<std::string>::upper_bound(key)_(existent)/8192 36.69 35.50 -1.19 -3.25 std::set<std::string>::upper_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -0.16 std::set<std::string>::upper_bound(key)_(non-existent)/1024 24.48 24.90 0.42 1.73 std::set<std::string>::upper_bound(key)_(non-existent)/32 11.68 11.77 0.09 0.77 std::set<std::string>::upper_bound(key)_(non-existent)/8192 33.16 34.12 0.96 2.89 ```
2025-10-08 22:45:03 -08:00
__upper_bound_multi(const _Key& __v, __node_pointer __root, __end_node_pointer __result) const;
public:
2010-05-11 11:42:16 -08:00
template <class _Key>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<iterator, iterator> __equal_range_unique(const _Key& __k);
2010-05-11 11:42:16 -08:00
template <class _Key>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<const_iterator, const_iterator>
__equal_range_unique(const _Key& __k) const;
2010-05-11 11:42:16 -08:00
template <class _Key>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<iterator, iterator> __equal_range_multi(const _Key& __k);
2010-05-11 11:42:16 -08:00
template <class _Key>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<const_iterator, const_iterator>
__equal_range_multi(const _Key& __k) const;
using _Dp _LIBCPP_NODEBUG = __tree_node_destructor<__node_allocator>;
using __node_holder _LIBCPP_NODEBUG = unique_ptr<__node, _Dp>;
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __node_holder remove(const_iterator __p) _NOEXCEPT;
// FIXME: Make this function const qualified. Unfortunately doing so
// breaks existing code which uses non-const callable comparators.
2010-05-11 11:42:16 -08:00
template <class _Key>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<__end_node_pointer, __node_base_pointer&>
__find_equal(const _Key& __v);
2010-05-11 11:42:16 -08:00
template <class _Key>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<__end_node_pointer, __node_base_pointer&>
__find_equal(const _Key& __v) const {
return const_cast<__tree*>(this)->__find_equal(__v);
}
template <class _Key>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<__end_node_pointer, __node_base_pointer&>
__find_equal(const_iterator __hint, __node_base_pointer& __dummy, const _Key& __v);
2010-05-11 11:42:16 -08:00
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __copy_assign_alloc(const __tree& __t) {
2010-05-11 11:42:16 -08:00
__copy_assign_alloc(__t, integral_constant<bool, __node_traits::propagate_on_container_copy_assignment::value>());
}
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __copy_assign_alloc(const __tree& __t, true_type) {
2011-06-04 06:31:57 -08:00
if (__node_alloc() != __t.__node_alloc())
2010-05-11 11:42:16 -08:00
clear();
__node_alloc() = __t.__node_alloc();
}
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __copy_assign_alloc(const __tree&, false_type) {}
private:
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __node_base_pointer&
__find_leaf_low(__end_node_pointer& __parent, const value_type& __v);
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __node_base_pointer&
__find_leaf_high(__end_node_pointer& __parent, const value_type& __v);
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __node_base_pointer&
__find_leaf(const_iterator __hint, __end_node_pointer& __parent, const value_type& __v);
template <class... _Args>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __node_holder __construct_node(_Args&&... __args);
// TODO: Make this _LIBCPP_HIDE_FROM_ABI
_LIBCPP_HIDDEN _LIBCPP_CONSTEXPR_SINCE_CXX26 void destroy(__node_pointer __nd) _NOEXCEPT {
(__tree_deleter(__node_alloc_))(__nd);
}
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __move_assign(__tree& __t, false_type);
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __move_assign(__tree& __t, true_type) _NOEXCEPT_(
is_nothrow_move_assignable<value_compare>::value&& is_nothrow_move_assignable<__node_allocator>::value);
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __move_assign_alloc(__tree& __t)
_NOEXCEPT_(!__node_traits::propagate_on_container_move_assignment::value ||
is_nothrow_move_assignable<__node_allocator>::value) {
__move_assign_alloc(__t, integral_constant<bool, __node_traits::propagate_on_container_move_assignment::value>());
}
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __move_assign_alloc(__tree& __t, true_type)
2011-06-04 06:31:57 -08:00
_NOEXCEPT_(is_nothrow_move_assignable<__node_allocator>::value) {
__node_alloc() = std::move(__t.__node_alloc());
}
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __move_assign_alloc(__tree&, false_type) _NOEXCEPT {}
template <class _From, class _ValueT = _Tp, __enable_if_t<__is_tree_value_type_v<_ValueT>, int> = 0>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 static void
__assign_value(__get_node_value_type_t<value_type>& __lhs, _From&& __rhs) {
using __key_type = __remove_const_t<typename value_type::first_type>;
#if _LIBCPP_STD_VER >= 26
if constexpr (std::is_copy_constructible_v<decltype(__rhs.first)>) {
// We need to replace the `const key_type` subobject by reconstruction during constant evaluation to avoid
// undefined behavior. Currently, there is no constexpr-friend way to replacing a move-only key subobject.
if consteval {
std::destroy_at(std::addressof(__lhs));
std::construct_at(std::addressof(__lhs), __rhs.first, std::forward<_From>(__rhs).second);
return;
}
}
#endif
// This is technically UB, since the object was constructed as `const`.
// Clang doesn't optimize on this currently though.
const_cast<__key_type&>(__lhs.first) = const_cast<__copy_cvref_t<_From, __key_type>&&>(__rhs.first);
__lhs.second = std::forward<_From>(__rhs).second;
}
template <class _To, class _From, class _ValueT = _Tp, __enable_if_t<!__is_tree_value_type_v<_ValueT>, int> = 0>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 static void __assign_value(_To& __lhs, _From&& __rhs) {
__lhs = std::forward<_From>(__rhs);
}
[libc++] Optimize copy construction and assignment of __tree (#151304) ``` ---------------------------------------------------------------------------------------------------------- Benchmark old new ---------------------------------------------------------------------------------------------------------- std::map<int, int>::ctor(const&)/0 15.5 ns 14.9 ns std::map<int, int>::ctor(const&)/32 474 ns 321 ns std::map<int, int>::ctor(const&)/1024 24591 ns 11101 ns std::map<int, int>::ctor(const&)/8192 236153 ns 98868 ns std::map<std::string, int>::ctor(const&)/0 15.2 ns 14.9 ns std::map<std::string, int>::ctor(const&)/32 2673 ns 2340 ns std::map<std::string, int>::ctor(const&)/1024 115354 ns 86088 ns std::map<std::string, int>::ctor(const&)/8192 1298510 ns 626876 ns std::map<int, int>::operator=(const&) (into cleared Container)/0 16.5 ns 16.1 ns std::map<int, int>::operator=(const&) (into cleared Container)/32 548 ns 323 ns std::map<int, int>::operator=(const&) (into cleared Container)/1024 28418 ns 11026 ns std::map<int, int>::operator=(const&) (into cleared Container)/8192 281827 ns 97113 ns std::map<int, int>::operator=(const&) (into populated Container)/0 2.42 ns 1.85 ns std::map<int, int>::operator=(const&) (into populated Container)/32 369 ns 73.0 ns std::map<int, int>::operator=(const&) (into populated Container)/1024 24078 ns 2322 ns std::map<int, int>::operator=(const&) (into populated Container)/8192 266537 ns 22963 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/0 16.6 ns 16.2 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/32 2614 ns 1622 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/1024 116826 ns 63281 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/8192 1316655 ns 649177 ns std::map<std::string, int>::operator=(const&) (into populated Container)/0 2.42 ns 1.89 ns std::map<std::string, int>::operator=(const&) (into populated Container)/32 1264 ns 581 ns std::map<std::string, int>::operator=(const&) (into populated Container)/1024 238826 ns 39943 ns std::map<std::string, int>::operator=(const&) (into populated Container)/8192 2412327 ns 379456 ns ``` Fixes #77658 Fixes #62571
2025-08-04 23:49:40 -08:00
class __tree_deleter {
__node_allocator& __alloc_;
public:
using pointer = __node_pointer;
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __tree_deleter(__node_allocator& __alloc) : __alloc_(__alloc) {}
[libc++] Optimize copy construction and assignment of __tree (#151304) ``` ---------------------------------------------------------------------------------------------------------- Benchmark old new ---------------------------------------------------------------------------------------------------------- std::map<int, int>::ctor(const&)/0 15.5 ns 14.9 ns std::map<int, int>::ctor(const&)/32 474 ns 321 ns std::map<int, int>::ctor(const&)/1024 24591 ns 11101 ns std::map<int, int>::ctor(const&)/8192 236153 ns 98868 ns std::map<std::string, int>::ctor(const&)/0 15.2 ns 14.9 ns std::map<std::string, int>::ctor(const&)/32 2673 ns 2340 ns std::map<std::string, int>::ctor(const&)/1024 115354 ns 86088 ns std::map<std::string, int>::ctor(const&)/8192 1298510 ns 626876 ns std::map<int, int>::operator=(const&) (into cleared Container)/0 16.5 ns 16.1 ns std::map<int, int>::operator=(const&) (into cleared Container)/32 548 ns 323 ns std::map<int, int>::operator=(const&) (into cleared Container)/1024 28418 ns 11026 ns std::map<int, int>::operator=(const&) (into cleared Container)/8192 281827 ns 97113 ns std::map<int, int>::operator=(const&) (into populated Container)/0 2.42 ns 1.85 ns std::map<int, int>::operator=(const&) (into populated Container)/32 369 ns 73.0 ns std::map<int, int>::operator=(const&) (into populated Container)/1024 24078 ns 2322 ns std::map<int, int>::operator=(const&) (into populated Container)/8192 266537 ns 22963 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/0 16.6 ns 16.2 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/32 2614 ns 1622 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/1024 116826 ns 63281 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/8192 1316655 ns 649177 ns std::map<std::string, int>::operator=(const&) (into populated Container)/0 2.42 ns 1.89 ns std::map<std::string, int>::operator=(const&) (into populated Container)/32 1264 ns 581 ns std::map<std::string, int>::operator=(const&) (into populated Container)/1024 238826 ns 39943 ns std::map<std::string, int>::operator=(const&) (into populated Container)/8192 2412327 ns 379456 ns ``` Fixes #77658 Fixes #62571
2025-08-04 23:49:40 -08:00
#ifdef _LIBCPP_COMPILER_CLANG_BASED // FIXME: GCC complains about not being able to always_inline a recursive function
_LIBCPP_HIDE_FROM_ABI
#endif
_LIBCPP_CONSTEXPR_SINCE_CXX26 void operator()(__node_pointer __ptr) {
[libc++] Optimize copy construction and assignment of __tree (#151304) ``` ---------------------------------------------------------------------------------------------------------- Benchmark old new ---------------------------------------------------------------------------------------------------------- std::map<int, int>::ctor(const&)/0 15.5 ns 14.9 ns std::map<int, int>::ctor(const&)/32 474 ns 321 ns std::map<int, int>::ctor(const&)/1024 24591 ns 11101 ns std::map<int, int>::ctor(const&)/8192 236153 ns 98868 ns std::map<std::string, int>::ctor(const&)/0 15.2 ns 14.9 ns std::map<std::string, int>::ctor(const&)/32 2673 ns 2340 ns std::map<std::string, int>::ctor(const&)/1024 115354 ns 86088 ns std::map<std::string, int>::ctor(const&)/8192 1298510 ns 626876 ns std::map<int, int>::operator=(const&) (into cleared Container)/0 16.5 ns 16.1 ns std::map<int, int>::operator=(const&) (into cleared Container)/32 548 ns 323 ns std::map<int, int>::operator=(const&) (into cleared Container)/1024 28418 ns 11026 ns std::map<int, int>::operator=(const&) (into cleared Container)/8192 281827 ns 97113 ns std::map<int, int>::operator=(const&) (into populated Container)/0 2.42 ns 1.85 ns std::map<int, int>::operator=(const&) (into populated Container)/32 369 ns 73.0 ns std::map<int, int>::operator=(const&) (into populated Container)/1024 24078 ns 2322 ns std::map<int, int>::operator=(const&) (into populated Container)/8192 266537 ns 22963 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/0 16.6 ns 16.2 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/32 2614 ns 1622 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/1024 116826 ns 63281 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/8192 1316655 ns 649177 ns std::map<std::string, int>::operator=(const&) (into populated Container)/0 2.42 ns 1.89 ns std::map<std::string, int>::operator=(const&) (into populated Container)/32 1264 ns 581 ns std::map<std::string, int>::operator=(const&) (into populated Container)/1024 238826 ns 39943 ns std::map<std::string, int>::operator=(const&) (into populated Container)/8192 2412327 ns 379456 ns ``` Fixes #77658 Fixes #62571
2025-08-04 23:49:40 -08:00
if (!__ptr)
return;
(*this)(std::__static_fancy_pointer_cast<__node_pointer>(__ptr->__left_));
[libc++] Optimize copy construction and assignment of __tree (#151304) ``` ---------------------------------------------------------------------------------------------------------- Benchmark old new ---------------------------------------------------------------------------------------------------------- std::map<int, int>::ctor(const&)/0 15.5 ns 14.9 ns std::map<int, int>::ctor(const&)/32 474 ns 321 ns std::map<int, int>::ctor(const&)/1024 24591 ns 11101 ns std::map<int, int>::ctor(const&)/8192 236153 ns 98868 ns std::map<std::string, int>::ctor(const&)/0 15.2 ns 14.9 ns std::map<std::string, int>::ctor(const&)/32 2673 ns 2340 ns std::map<std::string, int>::ctor(const&)/1024 115354 ns 86088 ns std::map<std::string, int>::ctor(const&)/8192 1298510 ns 626876 ns std::map<int, int>::operator=(const&) (into cleared Container)/0 16.5 ns 16.1 ns std::map<int, int>::operator=(const&) (into cleared Container)/32 548 ns 323 ns std::map<int, int>::operator=(const&) (into cleared Container)/1024 28418 ns 11026 ns std::map<int, int>::operator=(const&) (into cleared Container)/8192 281827 ns 97113 ns std::map<int, int>::operator=(const&) (into populated Container)/0 2.42 ns 1.85 ns std::map<int, int>::operator=(const&) (into populated Container)/32 369 ns 73.0 ns std::map<int, int>::operator=(const&) (into populated Container)/1024 24078 ns 2322 ns std::map<int, int>::operator=(const&) (into populated Container)/8192 266537 ns 22963 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/0 16.6 ns 16.2 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/32 2614 ns 1622 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/1024 116826 ns 63281 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/8192 1316655 ns 649177 ns std::map<std::string, int>::operator=(const&) (into populated Container)/0 2.42 ns 1.89 ns std::map<std::string, int>::operator=(const&) (into populated Container)/32 1264 ns 581 ns std::map<std::string, int>::operator=(const&) (into populated Container)/1024 238826 ns 39943 ns std::map<std::string, int>::operator=(const&) (into populated Container)/8192 2412327 ns 379456 ns ``` Fixes #77658 Fixes #62571
2025-08-04 23:49:40 -08:00
auto __right = __ptr->__right_;
__node_traits::destroy(__alloc_, std::addressof(__ptr->__get_value()));
[libc++] Optimize copy construction and assignment of __tree (#151304) ``` ---------------------------------------------------------------------------------------------------------- Benchmark old new ---------------------------------------------------------------------------------------------------------- std::map<int, int>::ctor(const&)/0 15.5 ns 14.9 ns std::map<int, int>::ctor(const&)/32 474 ns 321 ns std::map<int, int>::ctor(const&)/1024 24591 ns 11101 ns std::map<int, int>::ctor(const&)/8192 236153 ns 98868 ns std::map<std::string, int>::ctor(const&)/0 15.2 ns 14.9 ns std::map<std::string, int>::ctor(const&)/32 2673 ns 2340 ns std::map<std::string, int>::ctor(const&)/1024 115354 ns 86088 ns std::map<std::string, int>::ctor(const&)/8192 1298510 ns 626876 ns std::map<int, int>::operator=(const&) (into cleared Container)/0 16.5 ns 16.1 ns std::map<int, int>::operator=(const&) (into cleared Container)/32 548 ns 323 ns std::map<int, int>::operator=(const&) (into cleared Container)/1024 28418 ns 11026 ns std::map<int, int>::operator=(const&) (into cleared Container)/8192 281827 ns 97113 ns std::map<int, int>::operator=(const&) (into populated Container)/0 2.42 ns 1.85 ns std::map<int, int>::operator=(const&) (into populated Container)/32 369 ns 73.0 ns std::map<int, int>::operator=(const&) (into populated Container)/1024 24078 ns 2322 ns std::map<int, int>::operator=(const&) (into populated Container)/8192 266537 ns 22963 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/0 16.6 ns 16.2 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/32 2614 ns 1622 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/1024 116826 ns 63281 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/8192 1316655 ns 649177 ns std::map<std::string, int>::operator=(const&) (into populated Container)/0 2.42 ns 1.89 ns std::map<std::string, int>::operator=(const&) (into populated Container)/32 1264 ns 581 ns std::map<std::string, int>::operator=(const&) (into populated Container)/1024 238826 ns 39943 ns std::map<std::string, int>::operator=(const&) (into populated Container)/8192 2412327 ns 379456 ns ``` Fixes #77658 Fixes #62571
2025-08-04 23:49:40 -08:00
__node_traits::deallocate(__alloc_, __ptr, 1);
(*this)(std::__static_fancy_pointer_cast<__node_pointer>(__right));
[libc++] Optimize copy construction and assignment of __tree (#151304) ``` ---------------------------------------------------------------------------------------------------------- Benchmark old new ---------------------------------------------------------------------------------------------------------- std::map<int, int>::ctor(const&)/0 15.5 ns 14.9 ns std::map<int, int>::ctor(const&)/32 474 ns 321 ns std::map<int, int>::ctor(const&)/1024 24591 ns 11101 ns std::map<int, int>::ctor(const&)/8192 236153 ns 98868 ns std::map<std::string, int>::ctor(const&)/0 15.2 ns 14.9 ns std::map<std::string, int>::ctor(const&)/32 2673 ns 2340 ns std::map<std::string, int>::ctor(const&)/1024 115354 ns 86088 ns std::map<std::string, int>::ctor(const&)/8192 1298510 ns 626876 ns std::map<int, int>::operator=(const&) (into cleared Container)/0 16.5 ns 16.1 ns std::map<int, int>::operator=(const&) (into cleared Container)/32 548 ns 323 ns std::map<int, int>::operator=(const&) (into cleared Container)/1024 28418 ns 11026 ns std::map<int, int>::operator=(const&) (into cleared Container)/8192 281827 ns 97113 ns std::map<int, int>::operator=(const&) (into populated Container)/0 2.42 ns 1.85 ns std::map<int, int>::operator=(const&) (into populated Container)/32 369 ns 73.0 ns std::map<int, int>::operator=(const&) (into populated Container)/1024 24078 ns 2322 ns std::map<int, int>::operator=(const&) (into populated Container)/8192 266537 ns 22963 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/0 16.6 ns 16.2 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/32 2614 ns 1622 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/1024 116826 ns 63281 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/8192 1316655 ns 649177 ns std::map<std::string, int>::operator=(const&) (into populated Container)/0 2.42 ns 1.89 ns std::map<std::string, int>::operator=(const&) (into populated Container)/32 1264 ns 581 ns std::map<std::string, int>::operator=(const&) (into populated Container)/1024 238826 ns 39943 ns std::map<std::string, int>::operator=(const&) (into populated Container)/8192 2412327 ns 379456 ns ``` Fixes #77658 Fixes #62571
2025-08-04 23:49:40 -08:00
}
};
// This copy construction will always produce a correct red-black-tree assuming the incoming tree is correct, since we
// copy the exact structure 1:1. Since this is for copy construction _only_ we know that we get a correct tree. If we
// didn't get a correct tree, the invariants of __tree are broken and we have a much bigger problem than an improperly
// balanced tree.
[libc++] Optimize __tree copy/move constructor/assignment with allocator (#163558) This patch applies the same optimization as implemented in #151304 to the overloads taking an allocator as the second argument. Apple M4: ``` Benchmark old new Difference % Difference ----------------------------------------------------------- -------------- -------------- ------------ -------------- std::map<int,_int>::ctor(&&,_different_allocs)/0 14.59 12.78 -1.81 -12.41% std::map<int,_int>::ctor(&&,_different_allocs)/1024 16407.05 6265.11 -10141.94 -61.81% std::map<int,_int>::ctor(&&,_different_allocs)/32 395.99 199.76 -196.23 -49.56% std::map<int,_int>::ctor(&&,_different_allocs)/8192 141478.67 53767.84 -87710.83 -62.00% std::map<int,_int>::ctor(const&,_alloc)/0 12.83 12.71 -0.12 -0.94% std::map<int,_int>::ctor(const&,_alloc)/1024 9979.71 7849.11 -2130.59 -21.35% std::map<int,_int>::ctor(const&,_alloc)/32 283.82 266.05 -17.77 -6.26% std::map<int,_int>::ctor(const&,_alloc)/8192 81418.63 63190.41 -18228.21 -22.39% std::map<std::string,_int>::ctor(&&,_different_allocs)/0 14.58 12.68 -1.90 -13.00% std::map<std::string,_int>::ctor(&&,_different_allocs)/1024 19513.56 7806.04 -11707.52 -60.00% std::map<std::string,_int>::ctor(&&,_different_allocs)/32 477.80 247.28 -230.52 -48.25% std::map<std::string,_int>::ctor(&&,_different_allocs)/8192 504558.78 69592.21 -434966.56 -86.21% std::map<std::string,_int>::ctor(const&,_alloc)/0 12.64 12.60 -0.04 -0.33% std::map<std::string,_int>::ctor(const&,_alloc)/1024 43198.53 37220.54 -5977.99 -13.84% std::map<std::string,_int>::ctor(const&,_alloc)/32 928.39 867.03 -61.36 -6.61% std::map<std::string,_int>::ctor(const&,_alloc)/8192 461313.81 389200.82 -72112.99 -15.63% ```
2025-11-12 05:53:37 -09:00
template <class _NodeConstructor>
[libc++] Optimize copy construction and assignment of __tree (#151304) ``` ---------------------------------------------------------------------------------------------------------- Benchmark old new ---------------------------------------------------------------------------------------------------------- std::map<int, int>::ctor(const&)/0 15.5 ns 14.9 ns std::map<int, int>::ctor(const&)/32 474 ns 321 ns std::map<int, int>::ctor(const&)/1024 24591 ns 11101 ns std::map<int, int>::ctor(const&)/8192 236153 ns 98868 ns std::map<std::string, int>::ctor(const&)/0 15.2 ns 14.9 ns std::map<std::string, int>::ctor(const&)/32 2673 ns 2340 ns std::map<std::string, int>::ctor(const&)/1024 115354 ns 86088 ns std::map<std::string, int>::ctor(const&)/8192 1298510 ns 626876 ns std::map<int, int>::operator=(const&) (into cleared Container)/0 16.5 ns 16.1 ns std::map<int, int>::operator=(const&) (into cleared Container)/32 548 ns 323 ns std::map<int, int>::operator=(const&) (into cleared Container)/1024 28418 ns 11026 ns std::map<int, int>::operator=(const&) (into cleared Container)/8192 281827 ns 97113 ns std::map<int, int>::operator=(const&) (into populated Container)/0 2.42 ns 1.85 ns std::map<int, int>::operator=(const&) (into populated Container)/32 369 ns 73.0 ns std::map<int, int>::operator=(const&) (into populated Container)/1024 24078 ns 2322 ns std::map<int, int>::operator=(const&) (into populated Container)/8192 266537 ns 22963 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/0 16.6 ns 16.2 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/32 2614 ns 1622 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/1024 116826 ns 63281 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/8192 1316655 ns 649177 ns std::map<std::string, int>::operator=(const&) (into populated Container)/0 2.42 ns 1.89 ns std::map<std::string, int>::operator=(const&) (into populated Container)/32 1264 ns 581 ns std::map<std::string, int>::operator=(const&) (into populated Container)/1024 238826 ns 39943 ns std::map<std::string, int>::operator=(const&) (into populated Container)/8192 2412327 ns 379456 ns ``` Fixes #77658 Fixes #62571
2025-08-04 23:49:40 -08:00
#ifdef _LIBCPP_COMPILER_CLANG_BASED // FIXME: GCC complains about not being able to always_inline a recursive function
_LIBCPP_HIDE_FROM_ABI
#endif
_LIBCPP_CONSTEXPR_SINCE_CXX26 __node_pointer
__construct_from_tree(__node_pointer __src, _NodeConstructor __construct) {
[libc++] Optimize copy construction and assignment of __tree (#151304) ``` ---------------------------------------------------------------------------------------------------------- Benchmark old new ---------------------------------------------------------------------------------------------------------- std::map<int, int>::ctor(const&)/0 15.5 ns 14.9 ns std::map<int, int>::ctor(const&)/32 474 ns 321 ns std::map<int, int>::ctor(const&)/1024 24591 ns 11101 ns std::map<int, int>::ctor(const&)/8192 236153 ns 98868 ns std::map<std::string, int>::ctor(const&)/0 15.2 ns 14.9 ns std::map<std::string, int>::ctor(const&)/32 2673 ns 2340 ns std::map<std::string, int>::ctor(const&)/1024 115354 ns 86088 ns std::map<std::string, int>::ctor(const&)/8192 1298510 ns 626876 ns std::map<int, int>::operator=(const&) (into cleared Container)/0 16.5 ns 16.1 ns std::map<int, int>::operator=(const&) (into cleared Container)/32 548 ns 323 ns std::map<int, int>::operator=(const&) (into cleared Container)/1024 28418 ns 11026 ns std::map<int, int>::operator=(const&) (into cleared Container)/8192 281827 ns 97113 ns std::map<int, int>::operator=(const&) (into populated Container)/0 2.42 ns 1.85 ns std::map<int, int>::operator=(const&) (into populated Container)/32 369 ns 73.0 ns std::map<int, int>::operator=(const&) (into populated Container)/1024 24078 ns 2322 ns std::map<int, int>::operator=(const&) (into populated Container)/8192 266537 ns 22963 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/0 16.6 ns 16.2 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/32 2614 ns 1622 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/1024 116826 ns 63281 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/8192 1316655 ns 649177 ns std::map<std::string, int>::operator=(const&) (into populated Container)/0 2.42 ns 1.89 ns std::map<std::string, int>::operator=(const&) (into populated Container)/32 1264 ns 581 ns std::map<std::string, int>::operator=(const&) (into populated Container)/1024 238826 ns 39943 ns std::map<std::string, int>::operator=(const&) (into populated Container)/8192 2412327 ns 379456 ns ``` Fixes #77658 Fixes #62571
2025-08-04 23:49:40 -08:00
if (!__src)
return nullptr;
[libc++] Optimize __tree copy/move constructor/assignment with allocator (#163558) This patch applies the same optimization as implemented in #151304 to the overloads taking an allocator as the second argument. Apple M4: ``` Benchmark old new Difference % Difference ----------------------------------------------------------- -------------- -------------- ------------ -------------- std::map<int,_int>::ctor(&&,_different_allocs)/0 14.59 12.78 -1.81 -12.41% std::map<int,_int>::ctor(&&,_different_allocs)/1024 16407.05 6265.11 -10141.94 -61.81% std::map<int,_int>::ctor(&&,_different_allocs)/32 395.99 199.76 -196.23 -49.56% std::map<int,_int>::ctor(&&,_different_allocs)/8192 141478.67 53767.84 -87710.83 -62.00% std::map<int,_int>::ctor(const&,_alloc)/0 12.83 12.71 -0.12 -0.94% std::map<int,_int>::ctor(const&,_alloc)/1024 9979.71 7849.11 -2130.59 -21.35% std::map<int,_int>::ctor(const&,_alloc)/32 283.82 266.05 -17.77 -6.26% std::map<int,_int>::ctor(const&,_alloc)/8192 81418.63 63190.41 -18228.21 -22.39% std::map<std::string,_int>::ctor(&&,_different_allocs)/0 14.58 12.68 -1.90 -13.00% std::map<std::string,_int>::ctor(&&,_different_allocs)/1024 19513.56 7806.04 -11707.52 -60.00% std::map<std::string,_int>::ctor(&&,_different_allocs)/32 477.80 247.28 -230.52 -48.25% std::map<std::string,_int>::ctor(&&,_different_allocs)/8192 504558.78 69592.21 -434966.56 -86.21% std::map<std::string,_int>::ctor(const&,_alloc)/0 12.64 12.60 -0.04 -0.33% std::map<std::string,_int>::ctor(const&,_alloc)/1024 43198.53 37220.54 -5977.99 -13.84% std::map<std::string,_int>::ctor(const&,_alloc)/32 928.39 867.03 -61.36 -6.61% std::map<std::string,_int>::ctor(const&,_alloc)/8192 461313.81 389200.82 -72112.99 -15.63% ```
2025-11-12 05:53:37 -09:00
__node_holder __new_node = __construct(__src->__get_value());
[libc++] Optimize copy construction and assignment of __tree (#151304) ``` ---------------------------------------------------------------------------------------------------------- Benchmark old new ---------------------------------------------------------------------------------------------------------- std::map<int, int>::ctor(const&)/0 15.5 ns 14.9 ns std::map<int, int>::ctor(const&)/32 474 ns 321 ns std::map<int, int>::ctor(const&)/1024 24591 ns 11101 ns std::map<int, int>::ctor(const&)/8192 236153 ns 98868 ns std::map<std::string, int>::ctor(const&)/0 15.2 ns 14.9 ns std::map<std::string, int>::ctor(const&)/32 2673 ns 2340 ns std::map<std::string, int>::ctor(const&)/1024 115354 ns 86088 ns std::map<std::string, int>::ctor(const&)/8192 1298510 ns 626876 ns std::map<int, int>::operator=(const&) (into cleared Container)/0 16.5 ns 16.1 ns std::map<int, int>::operator=(const&) (into cleared Container)/32 548 ns 323 ns std::map<int, int>::operator=(const&) (into cleared Container)/1024 28418 ns 11026 ns std::map<int, int>::operator=(const&) (into cleared Container)/8192 281827 ns 97113 ns std::map<int, int>::operator=(const&) (into populated Container)/0 2.42 ns 1.85 ns std::map<int, int>::operator=(const&) (into populated Container)/32 369 ns 73.0 ns std::map<int, int>::operator=(const&) (into populated Container)/1024 24078 ns 2322 ns std::map<int, int>::operator=(const&) (into populated Container)/8192 266537 ns 22963 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/0 16.6 ns 16.2 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/32 2614 ns 1622 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/1024 116826 ns 63281 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/8192 1316655 ns 649177 ns std::map<std::string, int>::operator=(const&) (into populated Container)/0 2.42 ns 1.89 ns std::map<std::string, int>::operator=(const&) (into populated Container)/32 1264 ns 581 ns std::map<std::string, int>::operator=(const&) (into populated Container)/1024 238826 ns 39943 ns std::map<std::string, int>::operator=(const&) (into populated Container)/8192 2412327 ns 379456 ns ``` Fixes #77658 Fixes #62571
2025-08-04 23:49:40 -08:00
unique_ptr<__node, __tree_deleter> __left(
__construct_from_tree(std::__static_fancy_pointer_cast<__node_pointer>(__src->__left_), __construct),
__node_alloc_);
__node_pointer __right =
__construct_from_tree(std::__static_fancy_pointer_cast<__node_pointer>(__src->__right_), __construct);
[libc++] Optimize copy construction and assignment of __tree (#151304) ``` ---------------------------------------------------------------------------------------------------------- Benchmark old new ---------------------------------------------------------------------------------------------------------- std::map<int, int>::ctor(const&)/0 15.5 ns 14.9 ns std::map<int, int>::ctor(const&)/32 474 ns 321 ns std::map<int, int>::ctor(const&)/1024 24591 ns 11101 ns std::map<int, int>::ctor(const&)/8192 236153 ns 98868 ns std::map<std::string, int>::ctor(const&)/0 15.2 ns 14.9 ns std::map<std::string, int>::ctor(const&)/32 2673 ns 2340 ns std::map<std::string, int>::ctor(const&)/1024 115354 ns 86088 ns std::map<std::string, int>::ctor(const&)/8192 1298510 ns 626876 ns std::map<int, int>::operator=(const&) (into cleared Container)/0 16.5 ns 16.1 ns std::map<int, int>::operator=(const&) (into cleared Container)/32 548 ns 323 ns std::map<int, int>::operator=(const&) (into cleared Container)/1024 28418 ns 11026 ns std::map<int, int>::operator=(const&) (into cleared Container)/8192 281827 ns 97113 ns std::map<int, int>::operator=(const&) (into populated Container)/0 2.42 ns 1.85 ns std::map<int, int>::operator=(const&) (into populated Container)/32 369 ns 73.0 ns std::map<int, int>::operator=(const&) (into populated Container)/1024 24078 ns 2322 ns std::map<int, int>::operator=(const&) (into populated Container)/8192 266537 ns 22963 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/0 16.6 ns 16.2 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/32 2614 ns 1622 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/1024 116826 ns 63281 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/8192 1316655 ns 649177 ns std::map<std::string, int>::operator=(const&) (into populated Container)/0 2.42 ns 1.89 ns std::map<std::string, int>::operator=(const&) (into populated Container)/32 1264 ns 581 ns std::map<std::string, int>::operator=(const&) (into populated Container)/1024 238826 ns 39943 ns std::map<std::string, int>::operator=(const&) (into populated Container)/8192 2412327 ns 379456 ns ``` Fixes #77658 Fixes #62571
2025-08-04 23:49:40 -08:00
__node_pointer __new_node_ptr = __new_node.release();
__new_node_ptr->__is_black_ = __src->__is_black_;
__new_node_ptr->__left_ = std::__static_fancy_pointer_cast<__node_base_pointer>(__left.release());
__new_node_ptr->__right_ = std::__static_fancy_pointer_cast<__node_base_pointer>(__right);
[libc++] Optimize copy construction and assignment of __tree (#151304) ``` ---------------------------------------------------------------------------------------------------------- Benchmark old new ---------------------------------------------------------------------------------------------------------- std::map<int, int>::ctor(const&)/0 15.5 ns 14.9 ns std::map<int, int>::ctor(const&)/32 474 ns 321 ns std::map<int, int>::ctor(const&)/1024 24591 ns 11101 ns std::map<int, int>::ctor(const&)/8192 236153 ns 98868 ns std::map<std::string, int>::ctor(const&)/0 15.2 ns 14.9 ns std::map<std::string, int>::ctor(const&)/32 2673 ns 2340 ns std::map<std::string, int>::ctor(const&)/1024 115354 ns 86088 ns std::map<std::string, int>::ctor(const&)/8192 1298510 ns 626876 ns std::map<int, int>::operator=(const&) (into cleared Container)/0 16.5 ns 16.1 ns std::map<int, int>::operator=(const&) (into cleared Container)/32 548 ns 323 ns std::map<int, int>::operator=(const&) (into cleared Container)/1024 28418 ns 11026 ns std::map<int, int>::operator=(const&) (into cleared Container)/8192 281827 ns 97113 ns std::map<int, int>::operator=(const&) (into populated Container)/0 2.42 ns 1.85 ns std::map<int, int>::operator=(const&) (into populated Container)/32 369 ns 73.0 ns std::map<int, int>::operator=(const&) (into populated Container)/1024 24078 ns 2322 ns std::map<int, int>::operator=(const&) (into populated Container)/8192 266537 ns 22963 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/0 16.6 ns 16.2 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/32 2614 ns 1622 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/1024 116826 ns 63281 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/8192 1316655 ns 649177 ns std::map<std::string, int>::operator=(const&) (into populated Container)/0 2.42 ns 1.89 ns std::map<std::string, int>::operator=(const&) (into populated Container)/32 1264 ns 581 ns std::map<std::string, int>::operator=(const&) (into populated Container)/1024 238826 ns 39943 ns std::map<std::string, int>::operator=(const&) (into populated Container)/8192 2412327 ns 379456 ns ``` Fixes #77658 Fixes #62571
2025-08-04 23:49:40 -08:00
if (__new_node_ptr->__left_)
__new_node_ptr->__left_->__parent_ = std::__static_fancy_pointer_cast<__end_node_pointer>(__new_node_ptr);
[libc++] Optimize copy construction and assignment of __tree (#151304) ``` ---------------------------------------------------------------------------------------------------------- Benchmark old new ---------------------------------------------------------------------------------------------------------- std::map<int, int>::ctor(const&)/0 15.5 ns 14.9 ns std::map<int, int>::ctor(const&)/32 474 ns 321 ns std::map<int, int>::ctor(const&)/1024 24591 ns 11101 ns std::map<int, int>::ctor(const&)/8192 236153 ns 98868 ns std::map<std::string, int>::ctor(const&)/0 15.2 ns 14.9 ns std::map<std::string, int>::ctor(const&)/32 2673 ns 2340 ns std::map<std::string, int>::ctor(const&)/1024 115354 ns 86088 ns std::map<std::string, int>::ctor(const&)/8192 1298510 ns 626876 ns std::map<int, int>::operator=(const&) (into cleared Container)/0 16.5 ns 16.1 ns std::map<int, int>::operator=(const&) (into cleared Container)/32 548 ns 323 ns std::map<int, int>::operator=(const&) (into cleared Container)/1024 28418 ns 11026 ns std::map<int, int>::operator=(const&) (into cleared Container)/8192 281827 ns 97113 ns std::map<int, int>::operator=(const&) (into populated Container)/0 2.42 ns 1.85 ns std::map<int, int>::operator=(const&) (into populated Container)/32 369 ns 73.0 ns std::map<int, int>::operator=(const&) (into populated Container)/1024 24078 ns 2322 ns std::map<int, int>::operator=(const&) (into populated Container)/8192 266537 ns 22963 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/0 16.6 ns 16.2 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/32 2614 ns 1622 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/1024 116826 ns 63281 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/8192 1316655 ns 649177 ns std::map<std::string, int>::operator=(const&) (into populated Container)/0 2.42 ns 1.89 ns std::map<std::string, int>::operator=(const&) (into populated Container)/32 1264 ns 581 ns std::map<std::string, int>::operator=(const&) (into populated Container)/1024 238826 ns 39943 ns std::map<std::string, int>::operator=(const&) (into populated Container)/8192 2412327 ns 379456 ns ``` Fixes #77658 Fixes #62571
2025-08-04 23:49:40 -08:00
if (__new_node_ptr->__right_)
__new_node_ptr->__right_->__parent_ = std::__static_fancy_pointer_cast<__end_node_pointer>(__new_node_ptr);
[libc++] Optimize copy construction and assignment of __tree (#151304) ``` ---------------------------------------------------------------------------------------------------------- Benchmark old new ---------------------------------------------------------------------------------------------------------- std::map<int, int>::ctor(const&)/0 15.5 ns 14.9 ns std::map<int, int>::ctor(const&)/32 474 ns 321 ns std::map<int, int>::ctor(const&)/1024 24591 ns 11101 ns std::map<int, int>::ctor(const&)/8192 236153 ns 98868 ns std::map<std::string, int>::ctor(const&)/0 15.2 ns 14.9 ns std::map<std::string, int>::ctor(const&)/32 2673 ns 2340 ns std::map<std::string, int>::ctor(const&)/1024 115354 ns 86088 ns std::map<std::string, int>::ctor(const&)/8192 1298510 ns 626876 ns std::map<int, int>::operator=(const&) (into cleared Container)/0 16.5 ns 16.1 ns std::map<int, int>::operator=(const&) (into cleared Container)/32 548 ns 323 ns std::map<int, int>::operator=(const&) (into cleared Container)/1024 28418 ns 11026 ns std::map<int, int>::operator=(const&) (into cleared Container)/8192 281827 ns 97113 ns std::map<int, int>::operator=(const&) (into populated Container)/0 2.42 ns 1.85 ns std::map<int, int>::operator=(const&) (into populated Container)/32 369 ns 73.0 ns std::map<int, int>::operator=(const&) (into populated Container)/1024 24078 ns 2322 ns std::map<int, int>::operator=(const&) (into populated Container)/8192 266537 ns 22963 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/0 16.6 ns 16.2 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/32 2614 ns 1622 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/1024 116826 ns 63281 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/8192 1316655 ns 649177 ns std::map<std::string, int>::operator=(const&) (into populated Container)/0 2.42 ns 1.89 ns std::map<std::string, int>::operator=(const&) (into populated Container)/32 1264 ns 581 ns std::map<std::string, int>::operator=(const&) (into populated Container)/1024 238826 ns 39943 ns std::map<std::string, int>::operator=(const&) (into populated Container)/8192 2412327 ns 379456 ns ``` Fixes #77658 Fixes #62571
2025-08-04 23:49:40 -08:00
return __new_node_ptr;
}
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __node_pointer __copy_construct_tree(__node_pointer __src) {
[libc++] Optimize __tree copy/move constructor/assignment with allocator (#163558) This patch applies the same optimization as implemented in #151304 to the overloads taking an allocator as the second argument. Apple M4: ``` Benchmark old new Difference % Difference ----------------------------------------------------------- -------------- -------------- ------------ -------------- std::map<int,_int>::ctor(&&,_different_allocs)/0 14.59 12.78 -1.81 -12.41% std::map<int,_int>::ctor(&&,_different_allocs)/1024 16407.05 6265.11 -10141.94 -61.81% std::map<int,_int>::ctor(&&,_different_allocs)/32 395.99 199.76 -196.23 -49.56% std::map<int,_int>::ctor(&&,_different_allocs)/8192 141478.67 53767.84 -87710.83 -62.00% std::map<int,_int>::ctor(const&,_alloc)/0 12.83 12.71 -0.12 -0.94% std::map<int,_int>::ctor(const&,_alloc)/1024 9979.71 7849.11 -2130.59 -21.35% std::map<int,_int>::ctor(const&,_alloc)/32 283.82 266.05 -17.77 -6.26% std::map<int,_int>::ctor(const&,_alloc)/8192 81418.63 63190.41 -18228.21 -22.39% std::map<std::string,_int>::ctor(&&,_different_allocs)/0 14.58 12.68 -1.90 -13.00% std::map<std::string,_int>::ctor(&&,_different_allocs)/1024 19513.56 7806.04 -11707.52 -60.00% std::map<std::string,_int>::ctor(&&,_different_allocs)/32 477.80 247.28 -230.52 -48.25% std::map<std::string,_int>::ctor(&&,_different_allocs)/8192 504558.78 69592.21 -434966.56 -86.21% std::map<std::string,_int>::ctor(const&,_alloc)/0 12.64 12.60 -0.04 -0.33% std::map<std::string,_int>::ctor(const&,_alloc)/1024 43198.53 37220.54 -5977.99 -13.84% std::map<std::string,_int>::ctor(const&,_alloc)/32 928.39 867.03 -61.36 -6.61% std::map<std::string,_int>::ctor(const&,_alloc)/8192 461313.81 389200.82 -72112.99 -15.63% ```
2025-11-12 05:53:37 -09:00
return __construct_from_tree(__src, [this](const value_type& __val) { return __construct_node(__val); });
}
template <class _ValueT = _Tp, __enable_if_t<__is_tree_value_type_v<_ValueT>, int> = 0>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __node_pointer __move_construct_tree(__node_pointer __src) {
[libc++] Optimize __tree copy/move constructor/assignment with allocator (#163558) This patch applies the same optimization as implemented in #151304 to the overloads taking an allocator as the second argument. Apple M4: ``` Benchmark old new Difference % Difference ----------------------------------------------------------- -------------- -------------- ------------ -------------- std::map<int,_int>::ctor(&&,_different_allocs)/0 14.59 12.78 -1.81 -12.41% std::map<int,_int>::ctor(&&,_different_allocs)/1024 16407.05 6265.11 -10141.94 -61.81% std::map<int,_int>::ctor(&&,_different_allocs)/32 395.99 199.76 -196.23 -49.56% std::map<int,_int>::ctor(&&,_different_allocs)/8192 141478.67 53767.84 -87710.83 -62.00% std::map<int,_int>::ctor(const&,_alloc)/0 12.83 12.71 -0.12 -0.94% std::map<int,_int>::ctor(const&,_alloc)/1024 9979.71 7849.11 -2130.59 -21.35% std::map<int,_int>::ctor(const&,_alloc)/32 283.82 266.05 -17.77 -6.26% std::map<int,_int>::ctor(const&,_alloc)/8192 81418.63 63190.41 -18228.21 -22.39% std::map<std::string,_int>::ctor(&&,_different_allocs)/0 14.58 12.68 -1.90 -13.00% std::map<std::string,_int>::ctor(&&,_different_allocs)/1024 19513.56 7806.04 -11707.52 -60.00% std::map<std::string,_int>::ctor(&&,_different_allocs)/32 477.80 247.28 -230.52 -48.25% std::map<std::string,_int>::ctor(&&,_different_allocs)/8192 504558.78 69592.21 -434966.56 -86.21% std::map<std::string,_int>::ctor(const&,_alloc)/0 12.64 12.60 -0.04 -0.33% std::map<std::string,_int>::ctor(const&,_alloc)/1024 43198.53 37220.54 -5977.99 -13.84% std::map<std::string,_int>::ctor(const&,_alloc)/32 928.39 867.03 -61.36 -6.61% std::map<std::string,_int>::ctor(const&,_alloc)/8192 461313.81 389200.82 -72112.99 -15.63% ```
2025-11-12 05:53:37 -09:00
return __construct_from_tree(__src, [this](value_type& __val) {
#if _LIBCPP_STD_VER >= 26
// We need to replace the `const key_type` subobject by reconstruction during constant evaluation to avoid
// undefined behavior. Currently, there is no constexpr-friend way to replacing a move-only key subobject.
if constexpr (std::is_copy_constructible_v<decltype(__val.first)>) {
if consteval {
return __construct_node(__val.first, std::move(__val.second));
}
}
#endif
[libc++] Optimize __tree copy/move constructor/assignment with allocator (#163558) This patch applies the same optimization as implemented in #151304 to the overloads taking an allocator as the second argument. Apple M4: ``` Benchmark old new Difference % Difference ----------------------------------------------------------- -------------- -------------- ------------ -------------- std::map<int,_int>::ctor(&&,_different_allocs)/0 14.59 12.78 -1.81 -12.41% std::map<int,_int>::ctor(&&,_different_allocs)/1024 16407.05 6265.11 -10141.94 -61.81% std::map<int,_int>::ctor(&&,_different_allocs)/32 395.99 199.76 -196.23 -49.56% std::map<int,_int>::ctor(&&,_different_allocs)/8192 141478.67 53767.84 -87710.83 -62.00% std::map<int,_int>::ctor(const&,_alloc)/0 12.83 12.71 -0.12 -0.94% std::map<int,_int>::ctor(const&,_alloc)/1024 9979.71 7849.11 -2130.59 -21.35% std::map<int,_int>::ctor(const&,_alloc)/32 283.82 266.05 -17.77 -6.26% std::map<int,_int>::ctor(const&,_alloc)/8192 81418.63 63190.41 -18228.21 -22.39% std::map<std::string,_int>::ctor(&&,_different_allocs)/0 14.58 12.68 -1.90 -13.00% std::map<std::string,_int>::ctor(&&,_different_allocs)/1024 19513.56 7806.04 -11707.52 -60.00% std::map<std::string,_int>::ctor(&&,_different_allocs)/32 477.80 247.28 -230.52 -48.25% std::map<std::string,_int>::ctor(&&,_different_allocs)/8192 504558.78 69592.21 -434966.56 -86.21% std::map<std::string,_int>::ctor(const&,_alloc)/0 12.64 12.60 -0.04 -0.33% std::map<std::string,_int>::ctor(const&,_alloc)/1024 43198.53 37220.54 -5977.99 -13.84% std::map<std::string,_int>::ctor(const&,_alloc)/32 928.39 867.03 -61.36 -6.61% std::map<std::string,_int>::ctor(const&,_alloc)/8192 461313.81 389200.82 -72112.99 -15.63% ```
2025-11-12 05:53:37 -09:00
return __construct_node(const_cast<key_type&&>(__val.first), std::move(__val.second));
});
}
template <class _ValueT = _Tp, __enable_if_t<!__is_tree_value_type_v<_ValueT>, int> = 0>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __node_pointer __move_construct_tree(__node_pointer __src) {
[libc++] Optimize __tree copy/move constructor/assignment with allocator (#163558) This patch applies the same optimization as implemented in #151304 to the overloads taking an allocator as the second argument. Apple M4: ``` Benchmark old new Difference % Difference ----------------------------------------------------------- -------------- -------------- ------------ -------------- std::map<int,_int>::ctor(&&,_different_allocs)/0 14.59 12.78 -1.81 -12.41% std::map<int,_int>::ctor(&&,_different_allocs)/1024 16407.05 6265.11 -10141.94 -61.81% std::map<int,_int>::ctor(&&,_different_allocs)/32 395.99 199.76 -196.23 -49.56% std::map<int,_int>::ctor(&&,_different_allocs)/8192 141478.67 53767.84 -87710.83 -62.00% std::map<int,_int>::ctor(const&,_alloc)/0 12.83 12.71 -0.12 -0.94% std::map<int,_int>::ctor(const&,_alloc)/1024 9979.71 7849.11 -2130.59 -21.35% std::map<int,_int>::ctor(const&,_alloc)/32 283.82 266.05 -17.77 -6.26% std::map<int,_int>::ctor(const&,_alloc)/8192 81418.63 63190.41 -18228.21 -22.39% std::map<std::string,_int>::ctor(&&,_different_allocs)/0 14.58 12.68 -1.90 -13.00% std::map<std::string,_int>::ctor(&&,_different_allocs)/1024 19513.56 7806.04 -11707.52 -60.00% std::map<std::string,_int>::ctor(&&,_different_allocs)/32 477.80 247.28 -230.52 -48.25% std::map<std::string,_int>::ctor(&&,_different_allocs)/8192 504558.78 69592.21 -434966.56 -86.21% std::map<std::string,_int>::ctor(const&,_alloc)/0 12.64 12.60 -0.04 -0.33% std::map<std::string,_int>::ctor(const&,_alloc)/1024 43198.53 37220.54 -5977.99 -13.84% std::map<std::string,_int>::ctor(const&,_alloc)/32 928.39 867.03 -61.36 -6.61% std::map<std::string,_int>::ctor(const&,_alloc)/8192 461313.81 389200.82 -72112.99 -15.63% ```
2025-11-12 05:53:37 -09:00
return __construct_from_tree(__src, [this](value_type& __val) { return __construct_node(std::move(__val)); });
}
template <class _Assignment, class _ConstructionAlg>
[libc++] Optimize copy construction and assignment of __tree (#151304) ``` ---------------------------------------------------------------------------------------------------------- Benchmark old new ---------------------------------------------------------------------------------------------------------- std::map<int, int>::ctor(const&)/0 15.5 ns 14.9 ns std::map<int, int>::ctor(const&)/32 474 ns 321 ns std::map<int, int>::ctor(const&)/1024 24591 ns 11101 ns std::map<int, int>::ctor(const&)/8192 236153 ns 98868 ns std::map<std::string, int>::ctor(const&)/0 15.2 ns 14.9 ns std::map<std::string, int>::ctor(const&)/32 2673 ns 2340 ns std::map<std::string, int>::ctor(const&)/1024 115354 ns 86088 ns std::map<std::string, int>::ctor(const&)/8192 1298510 ns 626876 ns std::map<int, int>::operator=(const&) (into cleared Container)/0 16.5 ns 16.1 ns std::map<int, int>::operator=(const&) (into cleared Container)/32 548 ns 323 ns std::map<int, int>::operator=(const&) (into cleared Container)/1024 28418 ns 11026 ns std::map<int, int>::operator=(const&) (into cleared Container)/8192 281827 ns 97113 ns std::map<int, int>::operator=(const&) (into populated Container)/0 2.42 ns 1.85 ns std::map<int, int>::operator=(const&) (into populated Container)/32 369 ns 73.0 ns std::map<int, int>::operator=(const&) (into populated Container)/1024 24078 ns 2322 ns std::map<int, int>::operator=(const&) (into populated Container)/8192 266537 ns 22963 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/0 16.6 ns 16.2 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/32 2614 ns 1622 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/1024 116826 ns 63281 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/8192 1316655 ns 649177 ns std::map<std::string, int>::operator=(const&) (into populated Container)/0 2.42 ns 1.89 ns std::map<std::string, int>::operator=(const&) (into populated Container)/32 1264 ns 581 ns std::map<std::string, int>::operator=(const&) (into populated Container)/1024 238826 ns 39943 ns std::map<std::string, int>::operator=(const&) (into populated Container)/8192 2412327 ns 379456 ns ``` Fixes #77658 Fixes #62571
2025-08-04 23:49:40 -08:00
// This copy assignment will always produce a correct red-black-tree assuming the incoming tree is correct, since our
// own tree is a red-black-tree and the incoming tree is a red-black-tree. The invariants of a red-black-tree are
// temporarily not met until all of the incoming red-black tree is copied.
#ifdef _LIBCPP_COMPILER_CLANG_BASED // FIXME: GCC complains about not being able to always_inline a recursive function
_LIBCPP_HIDE_FROM_ABI
#endif
_LIBCPP_CONSTEXPR_SINCE_CXX26 __node_pointer __assign_from_tree(
[libc++] Optimize __tree copy/move constructor/assignment with allocator (#163558) This patch applies the same optimization as implemented in #151304 to the overloads taking an allocator as the second argument. Apple M4: ``` Benchmark old new Difference % Difference ----------------------------------------------------------- -------------- -------------- ------------ -------------- std::map<int,_int>::ctor(&&,_different_allocs)/0 14.59 12.78 -1.81 -12.41% std::map<int,_int>::ctor(&&,_different_allocs)/1024 16407.05 6265.11 -10141.94 -61.81% std::map<int,_int>::ctor(&&,_different_allocs)/32 395.99 199.76 -196.23 -49.56% std::map<int,_int>::ctor(&&,_different_allocs)/8192 141478.67 53767.84 -87710.83 -62.00% std::map<int,_int>::ctor(const&,_alloc)/0 12.83 12.71 -0.12 -0.94% std::map<int,_int>::ctor(const&,_alloc)/1024 9979.71 7849.11 -2130.59 -21.35% std::map<int,_int>::ctor(const&,_alloc)/32 283.82 266.05 -17.77 -6.26% std::map<int,_int>::ctor(const&,_alloc)/8192 81418.63 63190.41 -18228.21 -22.39% std::map<std::string,_int>::ctor(&&,_different_allocs)/0 14.58 12.68 -1.90 -13.00% std::map<std::string,_int>::ctor(&&,_different_allocs)/1024 19513.56 7806.04 -11707.52 -60.00% std::map<std::string,_int>::ctor(&&,_different_allocs)/32 477.80 247.28 -230.52 -48.25% std::map<std::string,_int>::ctor(&&,_different_allocs)/8192 504558.78 69592.21 -434966.56 -86.21% std::map<std::string,_int>::ctor(const&,_alloc)/0 12.64 12.60 -0.04 -0.33% std::map<std::string,_int>::ctor(const&,_alloc)/1024 43198.53 37220.54 -5977.99 -13.84% std::map<std::string,_int>::ctor(const&,_alloc)/32 928.39 867.03 -61.36 -6.61% std::map<std::string,_int>::ctor(const&,_alloc)/8192 461313.81 389200.82 -72112.99 -15.63% ```
2025-11-12 05:53:37 -09:00
__node_pointer __dest, __node_pointer __src, _Assignment __assign, _ConstructionAlg __construct_subtree) {
[libc++] Optimize copy construction and assignment of __tree (#151304) ``` ---------------------------------------------------------------------------------------------------------- Benchmark old new ---------------------------------------------------------------------------------------------------------- std::map<int, int>::ctor(const&)/0 15.5 ns 14.9 ns std::map<int, int>::ctor(const&)/32 474 ns 321 ns std::map<int, int>::ctor(const&)/1024 24591 ns 11101 ns std::map<int, int>::ctor(const&)/8192 236153 ns 98868 ns std::map<std::string, int>::ctor(const&)/0 15.2 ns 14.9 ns std::map<std::string, int>::ctor(const&)/32 2673 ns 2340 ns std::map<std::string, int>::ctor(const&)/1024 115354 ns 86088 ns std::map<std::string, int>::ctor(const&)/8192 1298510 ns 626876 ns std::map<int, int>::operator=(const&) (into cleared Container)/0 16.5 ns 16.1 ns std::map<int, int>::operator=(const&) (into cleared Container)/32 548 ns 323 ns std::map<int, int>::operator=(const&) (into cleared Container)/1024 28418 ns 11026 ns std::map<int, int>::operator=(const&) (into cleared Container)/8192 281827 ns 97113 ns std::map<int, int>::operator=(const&) (into populated Container)/0 2.42 ns 1.85 ns std::map<int, int>::operator=(const&) (into populated Container)/32 369 ns 73.0 ns std::map<int, int>::operator=(const&) (into populated Container)/1024 24078 ns 2322 ns std::map<int, int>::operator=(const&) (into populated Container)/8192 266537 ns 22963 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/0 16.6 ns 16.2 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/32 2614 ns 1622 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/1024 116826 ns 63281 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/8192 1316655 ns 649177 ns std::map<std::string, int>::operator=(const&) (into populated Container)/0 2.42 ns 1.89 ns std::map<std::string, int>::operator=(const&) (into populated Container)/32 1264 ns 581 ns std::map<std::string, int>::operator=(const&) (into populated Container)/1024 238826 ns 39943 ns std::map<std::string, int>::operator=(const&) (into populated Container)/8192 2412327 ns 379456 ns ``` Fixes #77658 Fixes #62571
2025-08-04 23:49:40 -08:00
if (!__src) {
destroy(__dest);
return nullptr;
}
[libc++] Optimize __tree copy/move constructor/assignment with allocator (#163558) This patch applies the same optimization as implemented in #151304 to the overloads taking an allocator as the second argument. Apple M4: ``` Benchmark old new Difference % Difference ----------------------------------------------------------- -------------- -------------- ------------ -------------- std::map<int,_int>::ctor(&&,_different_allocs)/0 14.59 12.78 -1.81 -12.41% std::map<int,_int>::ctor(&&,_different_allocs)/1024 16407.05 6265.11 -10141.94 -61.81% std::map<int,_int>::ctor(&&,_different_allocs)/32 395.99 199.76 -196.23 -49.56% std::map<int,_int>::ctor(&&,_different_allocs)/8192 141478.67 53767.84 -87710.83 -62.00% std::map<int,_int>::ctor(const&,_alloc)/0 12.83 12.71 -0.12 -0.94% std::map<int,_int>::ctor(const&,_alloc)/1024 9979.71 7849.11 -2130.59 -21.35% std::map<int,_int>::ctor(const&,_alloc)/32 283.82 266.05 -17.77 -6.26% std::map<int,_int>::ctor(const&,_alloc)/8192 81418.63 63190.41 -18228.21 -22.39% std::map<std::string,_int>::ctor(&&,_different_allocs)/0 14.58 12.68 -1.90 -13.00% std::map<std::string,_int>::ctor(&&,_different_allocs)/1024 19513.56 7806.04 -11707.52 -60.00% std::map<std::string,_int>::ctor(&&,_different_allocs)/32 477.80 247.28 -230.52 -48.25% std::map<std::string,_int>::ctor(&&,_different_allocs)/8192 504558.78 69592.21 -434966.56 -86.21% std::map<std::string,_int>::ctor(const&,_alloc)/0 12.64 12.60 -0.04 -0.33% std::map<std::string,_int>::ctor(const&,_alloc)/1024 43198.53 37220.54 -5977.99 -13.84% std::map<std::string,_int>::ctor(const&,_alloc)/32 928.39 867.03 -61.36 -6.61% std::map<std::string,_int>::ctor(const&,_alloc)/8192 461313.81 389200.82 -72112.99 -15.63% ```
2025-11-12 05:53:37 -09:00
__assign(__dest->__get_value(), __src->__get_value());
[libc++] Optimize copy construction and assignment of __tree (#151304) ``` ---------------------------------------------------------------------------------------------------------- Benchmark old new ---------------------------------------------------------------------------------------------------------- std::map<int, int>::ctor(const&)/0 15.5 ns 14.9 ns std::map<int, int>::ctor(const&)/32 474 ns 321 ns std::map<int, int>::ctor(const&)/1024 24591 ns 11101 ns std::map<int, int>::ctor(const&)/8192 236153 ns 98868 ns std::map<std::string, int>::ctor(const&)/0 15.2 ns 14.9 ns std::map<std::string, int>::ctor(const&)/32 2673 ns 2340 ns std::map<std::string, int>::ctor(const&)/1024 115354 ns 86088 ns std::map<std::string, int>::ctor(const&)/8192 1298510 ns 626876 ns std::map<int, int>::operator=(const&) (into cleared Container)/0 16.5 ns 16.1 ns std::map<int, int>::operator=(const&) (into cleared Container)/32 548 ns 323 ns std::map<int, int>::operator=(const&) (into cleared Container)/1024 28418 ns 11026 ns std::map<int, int>::operator=(const&) (into cleared Container)/8192 281827 ns 97113 ns std::map<int, int>::operator=(const&) (into populated Container)/0 2.42 ns 1.85 ns std::map<int, int>::operator=(const&) (into populated Container)/32 369 ns 73.0 ns std::map<int, int>::operator=(const&) (into populated Container)/1024 24078 ns 2322 ns std::map<int, int>::operator=(const&) (into populated Container)/8192 266537 ns 22963 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/0 16.6 ns 16.2 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/32 2614 ns 1622 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/1024 116826 ns 63281 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/8192 1316655 ns 649177 ns std::map<std::string, int>::operator=(const&) (into populated Container)/0 2.42 ns 1.89 ns std::map<std::string, int>::operator=(const&) (into populated Container)/32 1264 ns 581 ns std::map<std::string, int>::operator=(const&) (into populated Container)/1024 238826 ns 39943 ns std::map<std::string, int>::operator=(const&) (into populated Container)/8192 2412327 ns 379456 ns ``` Fixes #77658 Fixes #62571
2025-08-04 23:49:40 -08:00
__dest->__is_black_ = __src->__is_black_;
// If we already have a left node in the destination tree, reuse it and copy-assign recursively
if (__dest->__left_) {
__dest->__left_ = std::__static_fancy_pointer_cast<__node_base_pointer>(__assign_from_tree(
std::__static_fancy_pointer_cast<__node_pointer>(__dest->__left_),
std::__static_fancy_pointer_cast<__node_pointer>(__src->__left_),
[libc++] Optimize __tree copy/move constructor/assignment with allocator (#163558) This patch applies the same optimization as implemented in #151304 to the overloads taking an allocator as the second argument. Apple M4: ``` Benchmark old new Difference % Difference ----------------------------------------------------------- -------------- -------------- ------------ -------------- std::map<int,_int>::ctor(&&,_different_allocs)/0 14.59 12.78 -1.81 -12.41% std::map<int,_int>::ctor(&&,_different_allocs)/1024 16407.05 6265.11 -10141.94 -61.81% std::map<int,_int>::ctor(&&,_different_allocs)/32 395.99 199.76 -196.23 -49.56% std::map<int,_int>::ctor(&&,_different_allocs)/8192 141478.67 53767.84 -87710.83 -62.00% std::map<int,_int>::ctor(const&,_alloc)/0 12.83 12.71 -0.12 -0.94% std::map<int,_int>::ctor(const&,_alloc)/1024 9979.71 7849.11 -2130.59 -21.35% std::map<int,_int>::ctor(const&,_alloc)/32 283.82 266.05 -17.77 -6.26% std::map<int,_int>::ctor(const&,_alloc)/8192 81418.63 63190.41 -18228.21 -22.39% std::map<std::string,_int>::ctor(&&,_different_allocs)/0 14.58 12.68 -1.90 -13.00% std::map<std::string,_int>::ctor(&&,_different_allocs)/1024 19513.56 7806.04 -11707.52 -60.00% std::map<std::string,_int>::ctor(&&,_different_allocs)/32 477.80 247.28 -230.52 -48.25% std::map<std::string,_int>::ctor(&&,_different_allocs)/8192 504558.78 69592.21 -434966.56 -86.21% std::map<std::string,_int>::ctor(const&,_alloc)/0 12.64 12.60 -0.04 -0.33% std::map<std::string,_int>::ctor(const&,_alloc)/1024 43198.53 37220.54 -5977.99 -13.84% std::map<std::string,_int>::ctor(const&,_alloc)/32 928.39 867.03 -61.36 -6.61% std::map<std::string,_int>::ctor(const&,_alloc)/8192 461313.81 389200.82 -72112.99 -15.63% ```
2025-11-12 05:53:37 -09:00
__assign,
__construct_subtree));
[libc++] Optimize copy construction and assignment of __tree (#151304) ``` ---------------------------------------------------------------------------------------------------------- Benchmark old new ---------------------------------------------------------------------------------------------------------- std::map<int, int>::ctor(const&)/0 15.5 ns 14.9 ns std::map<int, int>::ctor(const&)/32 474 ns 321 ns std::map<int, int>::ctor(const&)/1024 24591 ns 11101 ns std::map<int, int>::ctor(const&)/8192 236153 ns 98868 ns std::map<std::string, int>::ctor(const&)/0 15.2 ns 14.9 ns std::map<std::string, int>::ctor(const&)/32 2673 ns 2340 ns std::map<std::string, int>::ctor(const&)/1024 115354 ns 86088 ns std::map<std::string, int>::ctor(const&)/8192 1298510 ns 626876 ns std::map<int, int>::operator=(const&) (into cleared Container)/0 16.5 ns 16.1 ns std::map<int, int>::operator=(const&) (into cleared Container)/32 548 ns 323 ns std::map<int, int>::operator=(const&) (into cleared Container)/1024 28418 ns 11026 ns std::map<int, int>::operator=(const&) (into cleared Container)/8192 281827 ns 97113 ns std::map<int, int>::operator=(const&) (into populated Container)/0 2.42 ns 1.85 ns std::map<int, int>::operator=(const&) (into populated Container)/32 369 ns 73.0 ns std::map<int, int>::operator=(const&) (into populated Container)/1024 24078 ns 2322 ns std::map<int, int>::operator=(const&) (into populated Container)/8192 266537 ns 22963 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/0 16.6 ns 16.2 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/32 2614 ns 1622 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/1024 116826 ns 63281 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/8192 1316655 ns 649177 ns std::map<std::string, int>::operator=(const&) (into populated Container)/0 2.42 ns 1.89 ns std::map<std::string, int>::operator=(const&) (into populated Container)/32 1264 ns 581 ns std::map<std::string, int>::operator=(const&) (into populated Container)/1024 238826 ns 39943 ns std::map<std::string, int>::operator=(const&) (into populated Container)/8192 2412327 ns 379456 ns ``` Fixes #77658 Fixes #62571
2025-08-04 23:49:40 -08:00
// Otherwise, we must create new nodes; copy-construct from here on
} else if (__src->__left_) {
auto __new_left = __construct_subtree(std::__static_fancy_pointer_cast<__node_pointer>(__src->__left_));
__dest->__left_ = std::__static_fancy_pointer_cast<__node_base_pointer>(__new_left);
__new_left->__parent_ = std::__static_fancy_pointer_cast<__end_node_pointer>(__dest);
[libc++] Optimize copy construction and assignment of __tree (#151304) ``` ---------------------------------------------------------------------------------------------------------- Benchmark old new ---------------------------------------------------------------------------------------------------------- std::map<int, int>::ctor(const&)/0 15.5 ns 14.9 ns std::map<int, int>::ctor(const&)/32 474 ns 321 ns std::map<int, int>::ctor(const&)/1024 24591 ns 11101 ns std::map<int, int>::ctor(const&)/8192 236153 ns 98868 ns std::map<std::string, int>::ctor(const&)/0 15.2 ns 14.9 ns std::map<std::string, int>::ctor(const&)/32 2673 ns 2340 ns std::map<std::string, int>::ctor(const&)/1024 115354 ns 86088 ns std::map<std::string, int>::ctor(const&)/8192 1298510 ns 626876 ns std::map<int, int>::operator=(const&) (into cleared Container)/0 16.5 ns 16.1 ns std::map<int, int>::operator=(const&) (into cleared Container)/32 548 ns 323 ns std::map<int, int>::operator=(const&) (into cleared Container)/1024 28418 ns 11026 ns std::map<int, int>::operator=(const&) (into cleared Container)/8192 281827 ns 97113 ns std::map<int, int>::operator=(const&) (into populated Container)/0 2.42 ns 1.85 ns std::map<int, int>::operator=(const&) (into populated Container)/32 369 ns 73.0 ns std::map<int, int>::operator=(const&) (into populated Container)/1024 24078 ns 2322 ns std::map<int, int>::operator=(const&) (into populated Container)/8192 266537 ns 22963 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/0 16.6 ns 16.2 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/32 2614 ns 1622 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/1024 116826 ns 63281 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/8192 1316655 ns 649177 ns std::map<std::string, int>::operator=(const&) (into populated Container)/0 2.42 ns 1.89 ns std::map<std::string, int>::operator=(const&) (into populated Container)/32 1264 ns 581 ns std::map<std::string, int>::operator=(const&) (into populated Container)/1024 238826 ns 39943 ns std::map<std::string, int>::operator=(const&) (into populated Container)/8192 2412327 ns 379456 ns ``` Fixes #77658 Fixes #62571
2025-08-04 23:49:40 -08:00
}
// Identical to the left case above, just for the right nodes
if (__dest->__right_) {
__dest->__right_ = std::__static_fancy_pointer_cast<__node_base_pointer>(__assign_from_tree(
std::__static_fancy_pointer_cast<__node_pointer>(__dest->__right_),
std::__static_fancy_pointer_cast<__node_pointer>(__src->__right_),
[libc++] Optimize __tree copy/move constructor/assignment with allocator (#163558) This patch applies the same optimization as implemented in #151304 to the overloads taking an allocator as the second argument. Apple M4: ``` Benchmark old new Difference % Difference ----------------------------------------------------------- -------------- -------------- ------------ -------------- std::map<int,_int>::ctor(&&,_different_allocs)/0 14.59 12.78 -1.81 -12.41% std::map<int,_int>::ctor(&&,_different_allocs)/1024 16407.05 6265.11 -10141.94 -61.81% std::map<int,_int>::ctor(&&,_different_allocs)/32 395.99 199.76 -196.23 -49.56% std::map<int,_int>::ctor(&&,_different_allocs)/8192 141478.67 53767.84 -87710.83 -62.00% std::map<int,_int>::ctor(const&,_alloc)/0 12.83 12.71 -0.12 -0.94% std::map<int,_int>::ctor(const&,_alloc)/1024 9979.71 7849.11 -2130.59 -21.35% std::map<int,_int>::ctor(const&,_alloc)/32 283.82 266.05 -17.77 -6.26% std::map<int,_int>::ctor(const&,_alloc)/8192 81418.63 63190.41 -18228.21 -22.39% std::map<std::string,_int>::ctor(&&,_different_allocs)/0 14.58 12.68 -1.90 -13.00% std::map<std::string,_int>::ctor(&&,_different_allocs)/1024 19513.56 7806.04 -11707.52 -60.00% std::map<std::string,_int>::ctor(&&,_different_allocs)/32 477.80 247.28 -230.52 -48.25% std::map<std::string,_int>::ctor(&&,_different_allocs)/8192 504558.78 69592.21 -434966.56 -86.21% std::map<std::string,_int>::ctor(const&,_alloc)/0 12.64 12.60 -0.04 -0.33% std::map<std::string,_int>::ctor(const&,_alloc)/1024 43198.53 37220.54 -5977.99 -13.84% std::map<std::string,_int>::ctor(const&,_alloc)/32 928.39 867.03 -61.36 -6.61% std::map<std::string,_int>::ctor(const&,_alloc)/8192 461313.81 389200.82 -72112.99 -15.63% ```
2025-11-12 05:53:37 -09:00
__assign,
__construct_subtree));
[libc++] Optimize copy construction and assignment of __tree (#151304) ``` ---------------------------------------------------------------------------------------------------------- Benchmark old new ---------------------------------------------------------------------------------------------------------- std::map<int, int>::ctor(const&)/0 15.5 ns 14.9 ns std::map<int, int>::ctor(const&)/32 474 ns 321 ns std::map<int, int>::ctor(const&)/1024 24591 ns 11101 ns std::map<int, int>::ctor(const&)/8192 236153 ns 98868 ns std::map<std::string, int>::ctor(const&)/0 15.2 ns 14.9 ns std::map<std::string, int>::ctor(const&)/32 2673 ns 2340 ns std::map<std::string, int>::ctor(const&)/1024 115354 ns 86088 ns std::map<std::string, int>::ctor(const&)/8192 1298510 ns 626876 ns std::map<int, int>::operator=(const&) (into cleared Container)/0 16.5 ns 16.1 ns std::map<int, int>::operator=(const&) (into cleared Container)/32 548 ns 323 ns std::map<int, int>::operator=(const&) (into cleared Container)/1024 28418 ns 11026 ns std::map<int, int>::operator=(const&) (into cleared Container)/8192 281827 ns 97113 ns std::map<int, int>::operator=(const&) (into populated Container)/0 2.42 ns 1.85 ns std::map<int, int>::operator=(const&) (into populated Container)/32 369 ns 73.0 ns std::map<int, int>::operator=(const&) (into populated Container)/1024 24078 ns 2322 ns std::map<int, int>::operator=(const&) (into populated Container)/8192 266537 ns 22963 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/0 16.6 ns 16.2 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/32 2614 ns 1622 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/1024 116826 ns 63281 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/8192 1316655 ns 649177 ns std::map<std::string, int>::operator=(const&) (into populated Container)/0 2.42 ns 1.89 ns std::map<std::string, int>::operator=(const&) (into populated Container)/32 1264 ns 581 ns std::map<std::string, int>::operator=(const&) (into populated Container)/1024 238826 ns 39943 ns std::map<std::string, int>::operator=(const&) (into populated Container)/8192 2412327 ns 379456 ns ``` Fixes #77658 Fixes #62571
2025-08-04 23:49:40 -08:00
} else if (__src->__right_) {
auto __new_right = __construct_subtree(std::__static_fancy_pointer_cast<__node_pointer>(__src->__right_));
__dest->__right_ = std::__static_fancy_pointer_cast<__node_base_pointer>(__new_right);
__new_right->__parent_ = std::__static_fancy_pointer_cast<__end_node_pointer>(__dest);
[libc++] Optimize copy construction and assignment of __tree (#151304) ``` ---------------------------------------------------------------------------------------------------------- Benchmark old new ---------------------------------------------------------------------------------------------------------- std::map<int, int>::ctor(const&)/0 15.5 ns 14.9 ns std::map<int, int>::ctor(const&)/32 474 ns 321 ns std::map<int, int>::ctor(const&)/1024 24591 ns 11101 ns std::map<int, int>::ctor(const&)/8192 236153 ns 98868 ns std::map<std::string, int>::ctor(const&)/0 15.2 ns 14.9 ns std::map<std::string, int>::ctor(const&)/32 2673 ns 2340 ns std::map<std::string, int>::ctor(const&)/1024 115354 ns 86088 ns std::map<std::string, int>::ctor(const&)/8192 1298510 ns 626876 ns std::map<int, int>::operator=(const&) (into cleared Container)/0 16.5 ns 16.1 ns std::map<int, int>::operator=(const&) (into cleared Container)/32 548 ns 323 ns std::map<int, int>::operator=(const&) (into cleared Container)/1024 28418 ns 11026 ns std::map<int, int>::operator=(const&) (into cleared Container)/8192 281827 ns 97113 ns std::map<int, int>::operator=(const&) (into populated Container)/0 2.42 ns 1.85 ns std::map<int, int>::operator=(const&) (into populated Container)/32 369 ns 73.0 ns std::map<int, int>::operator=(const&) (into populated Container)/1024 24078 ns 2322 ns std::map<int, int>::operator=(const&) (into populated Container)/8192 266537 ns 22963 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/0 16.6 ns 16.2 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/32 2614 ns 1622 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/1024 116826 ns 63281 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/8192 1316655 ns 649177 ns std::map<std::string, int>::operator=(const&) (into populated Container)/0 2.42 ns 1.89 ns std::map<std::string, int>::operator=(const&) (into populated Container)/32 1264 ns 581 ns std::map<std::string, int>::operator=(const&) (into populated Container)/1024 238826 ns 39943 ns std::map<std::string, int>::operator=(const&) (into populated Container)/8192 2412327 ns 379456 ns ``` Fixes #77658 Fixes #62571
2025-08-04 23:49:40 -08:00
}
return __dest;
}
[libc++] Optimize __tree copy/move constructor/assignment with allocator (#163558) This patch applies the same optimization as implemented in #151304 to the overloads taking an allocator as the second argument. Apple M4: ``` Benchmark old new Difference % Difference ----------------------------------------------------------- -------------- -------------- ------------ -------------- std::map<int,_int>::ctor(&&,_different_allocs)/0 14.59 12.78 -1.81 -12.41% std::map<int,_int>::ctor(&&,_different_allocs)/1024 16407.05 6265.11 -10141.94 -61.81% std::map<int,_int>::ctor(&&,_different_allocs)/32 395.99 199.76 -196.23 -49.56% std::map<int,_int>::ctor(&&,_different_allocs)/8192 141478.67 53767.84 -87710.83 -62.00% std::map<int,_int>::ctor(const&,_alloc)/0 12.83 12.71 -0.12 -0.94% std::map<int,_int>::ctor(const&,_alloc)/1024 9979.71 7849.11 -2130.59 -21.35% std::map<int,_int>::ctor(const&,_alloc)/32 283.82 266.05 -17.77 -6.26% std::map<int,_int>::ctor(const&,_alloc)/8192 81418.63 63190.41 -18228.21 -22.39% std::map<std::string,_int>::ctor(&&,_different_allocs)/0 14.58 12.68 -1.90 -13.00% std::map<std::string,_int>::ctor(&&,_different_allocs)/1024 19513.56 7806.04 -11707.52 -60.00% std::map<std::string,_int>::ctor(&&,_different_allocs)/32 477.80 247.28 -230.52 -48.25% std::map<std::string,_int>::ctor(&&,_different_allocs)/8192 504558.78 69592.21 -434966.56 -86.21% std::map<std::string,_int>::ctor(const&,_alloc)/0 12.64 12.60 -0.04 -0.33% std::map<std::string,_int>::ctor(const&,_alloc)/1024 43198.53 37220.54 -5977.99 -13.84% std::map<std::string,_int>::ctor(const&,_alloc)/32 928.39 867.03 -61.36 -6.61% std::map<std::string,_int>::ctor(const&,_alloc)/8192 461313.81 389200.82 -72112.99 -15.63% ```
2025-11-12 05:53:37 -09:00
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __node_pointer
__copy_assign_tree(__node_pointer __dest, __node_pointer __src) {
[libc++] Optimize __tree copy/move constructor/assignment with allocator (#163558) This patch applies the same optimization as implemented in #151304 to the overloads taking an allocator as the second argument. Apple M4: ``` Benchmark old new Difference % Difference ----------------------------------------------------------- -------------- -------------- ------------ -------------- std::map<int,_int>::ctor(&&,_different_allocs)/0 14.59 12.78 -1.81 -12.41% std::map<int,_int>::ctor(&&,_different_allocs)/1024 16407.05 6265.11 -10141.94 -61.81% std::map<int,_int>::ctor(&&,_different_allocs)/32 395.99 199.76 -196.23 -49.56% std::map<int,_int>::ctor(&&,_different_allocs)/8192 141478.67 53767.84 -87710.83 -62.00% std::map<int,_int>::ctor(const&,_alloc)/0 12.83 12.71 -0.12 -0.94% std::map<int,_int>::ctor(const&,_alloc)/1024 9979.71 7849.11 -2130.59 -21.35% std::map<int,_int>::ctor(const&,_alloc)/32 283.82 266.05 -17.77 -6.26% std::map<int,_int>::ctor(const&,_alloc)/8192 81418.63 63190.41 -18228.21 -22.39% std::map<std::string,_int>::ctor(&&,_different_allocs)/0 14.58 12.68 -1.90 -13.00% std::map<std::string,_int>::ctor(&&,_different_allocs)/1024 19513.56 7806.04 -11707.52 -60.00% std::map<std::string,_int>::ctor(&&,_different_allocs)/32 477.80 247.28 -230.52 -48.25% std::map<std::string,_int>::ctor(&&,_different_allocs)/8192 504558.78 69592.21 -434966.56 -86.21% std::map<std::string,_int>::ctor(const&,_alloc)/0 12.64 12.60 -0.04 -0.33% std::map<std::string,_int>::ctor(const&,_alloc)/1024 43198.53 37220.54 -5977.99 -13.84% std::map<std::string,_int>::ctor(const&,_alloc)/32 928.39 867.03 -61.36 -6.61% std::map<std::string,_int>::ctor(const&,_alloc)/8192 461313.81 389200.82 -72112.99 -15.63% ```
2025-11-12 05:53:37 -09:00
return __assign_from_tree(
__dest,
__src,
[](value_type& __lhs, const value_type& __rhs) { __assign_value(__lhs, __rhs); },
[this](__node_pointer __nd) { return __copy_construct_tree(__nd); });
}
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __node_pointer
__move_assign_tree(__node_pointer __dest, __node_pointer __src) {
[libc++] Optimize __tree copy/move constructor/assignment with allocator (#163558) This patch applies the same optimization as implemented in #151304 to the overloads taking an allocator as the second argument. Apple M4: ``` Benchmark old new Difference % Difference ----------------------------------------------------------- -------------- -------------- ------------ -------------- std::map<int,_int>::ctor(&&,_different_allocs)/0 14.59 12.78 -1.81 -12.41% std::map<int,_int>::ctor(&&,_different_allocs)/1024 16407.05 6265.11 -10141.94 -61.81% std::map<int,_int>::ctor(&&,_different_allocs)/32 395.99 199.76 -196.23 -49.56% std::map<int,_int>::ctor(&&,_different_allocs)/8192 141478.67 53767.84 -87710.83 -62.00% std::map<int,_int>::ctor(const&,_alloc)/0 12.83 12.71 -0.12 -0.94% std::map<int,_int>::ctor(const&,_alloc)/1024 9979.71 7849.11 -2130.59 -21.35% std::map<int,_int>::ctor(const&,_alloc)/32 283.82 266.05 -17.77 -6.26% std::map<int,_int>::ctor(const&,_alloc)/8192 81418.63 63190.41 -18228.21 -22.39% std::map<std::string,_int>::ctor(&&,_different_allocs)/0 14.58 12.68 -1.90 -13.00% std::map<std::string,_int>::ctor(&&,_different_allocs)/1024 19513.56 7806.04 -11707.52 -60.00% std::map<std::string,_int>::ctor(&&,_different_allocs)/32 477.80 247.28 -230.52 -48.25% std::map<std::string,_int>::ctor(&&,_different_allocs)/8192 504558.78 69592.21 -434966.56 -86.21% std::map<std::string,_int>::ctor(const&,_alloc)/0 12.64 12.60 -0.04 -0.33% std::map<std::string,_int>::ctor(const&,_alloc)/1024 43198.53 37220.54 -5977.99 -13.84% std::map<std::string,_int>::ctor(const&,_alloc)/32 928.39 867.03 -61.36 -6.61% std::map<std::string,_int>::ctor(const&,_alloc)/8192 461313.81 389200.82 -72112.99 -15.63% ```
2025-11-12 05:53:37 -09:00
return __assign_from_tree(
__dest,
__src,
[](value_type& __lhs, value_type& __rhs) { __assign_value(__lhs, std::move(__rhs)); },
[this](__node_pointer __nd) { return __move_construct_tree(__nd); });
}
[libc++] Optimize {std,ranges}::for_each for iterating over __trees (#164405) This patch optimizes how `for_each` iterates over trees by using recursion and storing pointers to the next nodes on the stack. This avoids pointer chasing through the `__parent_` pointer, reducing cache misses. It also makes use of the compiler being able tail-call optimize the recursive function, removing back-tracking the iterators have to do. ``` Benchmark old new Difference % Difference -------------------------------------------------- -------------- -------------- ------------ -------------- rng::for_each(map<int>)/32 35.19 26.67 -8.52 -24.21% rng::for_each(map<int>)/50 64.13 40.68 -23.45 -36.57% rng::for_each(map<int>)/8 5.06 6.49 1.43 28.21% rng::for_each(map<int>)/8192 22893.89 9266.68 -13627.21 -59.52% rng::for_each(map<int>::iterator)/32 35.51 26.88 -8.63 -24.31% rng::for_each(map<int>::iterator)/50 64.39 41.24 -23.15 -35.95% rng::for_each(map<int>::iterator)/8 5.12 5.93 0.81 15.80% rng::for_each(map<int>::iterator)/8192 21283.14 9736.83 -11546.31 -54.25% rng::for_each(multimap<int>)/32 35.22 26.61 -8.61 -24.45% rng::for_each(multimap<int>)/50 64.10 40.07 -24.03 -37.49% rng::for_each(multimap<int>)/8 5.08 6.69 1.61 31.70% rng::for_each(multimap<int>)/8192 23130.44 9026.16 -14104.28 -60.98% rng::for_each(multimap<int>::iterator)/32 35.40 25.08 -10.32 -29.15% rng::for_each(multimap<int>::iterator)/50 64.19 38.15 -26.04 -40.56% rng::for_each(multimap<int>::iterator)/8 5.04 5.25 0.22 4.31% rng::for_each(multimap<int>::iterator)/8192 22875.97 9392.08 -13483.89 -58.94% rng::for_each(multiset<int>)/32 35.82 27.11 -8.72 -24.33% rng::for_each(multiset<int>)/50 62.92 41.59 -21.32 -33.89% rng::for_each(multiset<int>)/8 4.79 6.79 2.00 41.70% rng::for_each(multiset<int>)/8192 22642.68 9280.95 -13361.73 -59.01% rng::for_each(multiset<int>::iterator)/32 35.76 26.71 -9.04 -25.28% rng::for_each(multiset<int>::iterator)/50 63.44 39.00 -24.44 -38.53% rng::for_each(multiset<int>::iterator)/8 4.90 5.21 0.30 6.18% rng::for_each(multiset<int>::iterator)/8192 19930.45 9867.60 -10062.85 -50.49% rng::for_each(set<int>)/32 35.90 27.30 -8.60 -23.96% rng::for_each(set<int>)/50 63.15 40.75 -22.40 -35.47% rng::for_each(set<int>)/8 4.77 6.83 2.06 43.23% rng::for_each(set<int>)/8192 20262.77 9381.57 -10881.20 -53.70% rng::for_each(set<int>::iterator)/32 36.02 26.42 -9.60 -26.64% rng::for_each(set<int>::iterator)/50 63.29 37.97 -25.32 -40.01% rng::for_each(set<int>::iterator)/8 4.72 5.22 0.50 10.50% rng::for_each(set<int>::iterator)/8192 20041.91 9831.91 -10210.00 -50.94% ```
2025-12-11 23:05:51 -09:00
friend struct __specialized_algorithm<_Algorithm::__for_each, __single_range<__tree> >;
};
#if _LIBCPP_STD_VER >= 14
template <class _Tp, class _Compare, class _Allocator>
struct __specialized_algorithm<_Algorithm::__for_each, __single_range<__tree<_Tp, _Compare, _Allocator> > > {
static const bool __has_algorithm = true;
using __node_pointer _LIBCPP_NODEBUG = typename __tree<_Tp, _Compare, _Allocator>::__node_pointer;
template <class _Tree, class _Func, class _Proj>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 static auto
operator()(_Tree&& __range, _Func __func, _Proj __proj) {
if (__range.size() != 0)
std::__tree_iterate_from_root<__copy_cvref_t<_Tree, typename __remove_cvref_t<_Tree>::value_type>>(
[](__node_pointer) { return false; }, __range.__root(), __func, __proj);
[libc++] Optimize {std,ranges}::for_each for iterating over __trees (#164405) This patch optimizes how `for_each` iterates over trees by using recursion and storing pointers to the next nodes on the stack. This avoids pointer chasing through the `__parent_` pointer, reducing cache misses. It also makes use of the compiler being able tail-call optimize the recursive function, removing back-tracking the iterators have to do. ``` Benchmark old new Difference % Difference -------------------------------------------------- -------------- -------------- ------------ -------------- rng::for_each(map<int>)/32 35.19 26.67 -8.52 -24.21% rng::for_each(map<int>)/50 64.13 40.68 -23.45 -36.57% rng::for_each(map<int>)/8 5.06 6.49 1.43 28.21% rng::for_each(map<int>)/8192 22893.89 9266.68 -13627.21 -59.52% rng::for_each(map<int>::iterator)/32 35.51 26.88 -8.63 -24.31% rng::for_each(map<int>::iterator)/50 64.39 41.24 -23.15 -35.95% rng::for_each(map<int>::iterator)/8 5.12 5.93 0.81 15.80% rng::for_each(map<int>::iterator)/8192 21283.14 9736.83 -11546.31 -54.25% rng::for_each(multimap<int>)/32 35.22 26.61 -8.61 -24.45% rng::for_each(multimap<int>)/50 64.10 40.07 -24.03 -37.49% rng::for_each(multimap<int>)/8 5.08 6.69 1.61 31.70% rng::for_each(multimap<int>)/8192 23130.44 9026.16 -14104.28 -60.98% rng::for_each(multimap<int>::iterator)/32 35.40 25.08 -10.32 -29.15% rng::for_each(multimap<int>::iterator)/50 64.19 38.15 -26.04 -40.56% rng::for_each(multimap<int>::iterator)/8 5.04 5.25 0.22 4.31% rng::for_each(multimap<int>::iterator)/8192 22875.97 9392.08 -13483.89 -58.94% rng::for_each(multiset<int>)/32 35.82 27.11 -8.72 -24.33% rng::for_each(multiset<int>)/50 62.92 41.59 -21.32 -33.89% rng::for_each(multiset<int>)/8 4.79 6.79 2.00 41.70% rng::for_each(multiset<int>)/8192 22642.68 9280.95 -13361.73 -59.01% rng::for_each(multiset<int>::iterator)/32 35.76 26.71 -9.04 -25.28% rng::for_each(multiset<int>::iterator)/50 63.44 39.00 -24.44 -38.53% rng::for_each(multiset<int>::iterator)/8 4.90 5.21 0.30 6.18% rng::for_each(multiset<int>::iterator)/8192 19930.45 9867.60 -10062.85 -50.49% rng::for_each(set<int>)/32 35.90 27.30 -8.60 -23.96% rng::for_each(set<int>)/50 63.15 40.75 -22.40 -35.47% rng::for_each(set<int>)/8 4.77 6.83 2.06 43.23% rng::for_each(set<int>)/8192 20262.77 9381.57 -10881.20 -53.70% rng::for_each(set<int>::iterator)/32 36.02 26.42 -9.60 -26.64% rng::for_each(set<int>::iterator)/50 63.29 37.97 -25.32 -40.01% rng::for_each(set<int>::iterator)/8 4.72 5.22 0.50 10.50% rng::for_each(set<int>::iterator)/8192 20041.91 9831.91 -10210.00 -50.94% ```
2025-12-11 23:05:51 -09:00
return std::make_pair(__range.end(), std::move(__func));
}
2010-05-11 11:42:16 -08:00
};
[libc++] Optimize {std,ranges}::for_each for iterating over __trees (#164405) This patch optimizes how `for_each` iterates over trees by using recursion and storing pointers to the next nodes on the stack. This avoids pointer chasing through the `__parent_` pointer, reducing cache misses. It also makes use of the compiler being able tail-call optimize the recursive function, removing back-tracking the iterators have to do. ``` Benchmark old new Difference % Difference -------------------------------------------------- -------------- -------------- ------------ -------------- rng::for_each(map<int>)/32 35.19 26.67 -8.52 -24.21% rng::for_each(map<int>)/50 64.13 40.68 -23.45 -36.57% rng::for_each(map<int>)/8 5.06 6.49 1.43 28.21% rng::for_each(map<int>)/8192 22893.89 9266.68 -13627.21 -59.52% rng::for_each(map<int>::iterator)/32 35.51 26.88 -8.63 -24.31% rng::for_each(map<int>::iterator)/50 64.39 41.24 -23.15 -35.95% rng::for_each(map<int>::iterator)/8 5.12 5.93 0.81 15.80% rng::for_each(map<int>::iterator)/8192 21283.14 9736.83 -11546.31 -54.25% rng::for_each(multimap<int>)/32 35.22 26.61 -8.61 -24.45% rng::for_each(multimap<int>)/50 64.10 40.07 -24.03 -37.49% rng::for_each(multimap<int>)/8 5.08 6.69 1.61 31.70% rng::for_each(multimap<int>)/8192 23130.44 9026.16 -14104.28 -60.98% rng::for_each(multimap<int>::iterator)/32 35.40 25.08 -10.32 -29.15% rng::for_each(multimap<int>::iterator)/50 64.19 38.15 -26.04 -40.56% rng::for_each(multimap<int>::iterator)/8 5.04 5.25 0.22 4.31% rng::for_each(multimap<int>::iterator)/8192 22875.97 9392.08 -13483.89 -58.94% rng::for_each(multiset<int>)/32 35.82 27.11 -8.72 -24.33% rng::for_each(multiset<int>)/50 62.92 41.59 -21.32 -33.89% rng::for_each(multiset<int>)/8 4.79 6.79 2.00 41.70% rng::for_each(multiset<int>)/8192 22642.68 9280.95 -13361.73 -59.01% rng::for_each(multiset<int>::iterator)/32 35.76 26.71 -9.04 -25.28% rng::for_each(multiset<int>::iterator)/50 63.44 39.00 -24.44 -38.53% rng::for_each(multiset<int>::iterator)/8 4.90 5.21 0.30 6.18% rng::for_each(multiset<int>::iterator)/8192 19930.45 9867.60 -10062.85 -50.49% rng::for_each(set<int>)/32 35.90 27.30 -8.60 -23.96% rng::for_each(set<int>)/50 63.15 40.75 -22.40 -35.47% rng::for_each(set<int>)/8 4.77 6.83 2.06 43.23% rng::for_each(set<int>)/8192 20262.77 9381.57 -10881.20 -53.70% rng::for_each(set<int>::iterator)/32 36.02 26.42 -9.60 -26.64% rng::for_each(set<int>::iterator)/50 63.29 37.97 -25.32 -40.01% rng::for_each(set<int>::iterator)/8 4.72 5.22 0.50 10.50% rng::for_each(set<int>::iterator)/8192 20041.91 9831.91 -10210.00 -50.94% ```
2025-12-11 23:05:51 -09:00
#endif
2010-05-11 11:42:16 -08:00
template <class _Tp, class _Compare, class _Allocator>
_LIBCPP_CONSTEXPR_SINCE_CXX26 __tree<_Tp, _Compare, _Allocator>&
__tree<_Tp, _Compare, _Allocator>::operator=(const __tree& __t) {
[libc++] Optimize copy construction and assignment of __tree (#151304) ``` ---------------------------------------------------------------------------------------------------------- Benchmark old new ---------------------------------------------------------------------------------------------------------- std::map<int, int>::ctor(const&)/0 15.5 ns 14.9 ns std::map<int, int>::ctor(const&)/32 474 ns 321 ns std::map<int, int>::ctor(const&)/1024 24591 ns 11101 ns std::map<int, int>::ctor(const&)/8192 236153 ns 98868 ns std::map<std::string, int>::ctor(const&)/0 15.2 ns 14.9 ns std::map<std::string, int>::ctor(const&)/32 2673 ns 2340 ns std::map<std::string, int>::ctor(const&)/1024 115354 ns 86088 ns std::map<std::string, int>::ctor(const&)/8192 1298510 ns 626876 ns std::map<int, int>::operator=(const&) (into cleared Container)/0 16.5 ns 16.1 ns std::map<int, int>::operator=(const&) (into cleared Container)/32 548 ns 323 ns std::map<int, int>::operator=(const&) (into cleared Container)/1024 28418 ns 11026 ns std::map<int, int>::operator=(const&) (into cleared Container)/8192 281827 ns 97113 ns std::map<int, int>::operator=(const&) (into populated Container)/0 2.42 ns 1.85 ns std::map<int, int>::operator=(const&) (into populated Container)/32 369 ns 73.0 ns std::map<int, int>::operator=(const&) (into populated Container)/1024 24078 ns 2322 ns std::map<int, int>::operator=(const&) (into populated Container)/8192 266537 ns 22963 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/0 16.6 ns 16.2 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/32 2614 ns 1622 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/1024 116826 ns 63281 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/8192 1316655 ns 649177 ns std::map<std::string, int>::operator=(const&) (into populated Container)/0 2.42 ns 1.89 ns std::map<std::string, int>::operator=(const&) (into populated Container)/32 1264 ns 581 ns std::map<std::string, int>::operator=(const&) (into populated Container)/1024 238826 ns 39943 ns std::map<std::string, int>::operator=(const&) (into populated Container)/8192 2412327 ns 379456 ns ``` Fixes #77658 Fixes #62571
2025-08-04 23:49:40 -08:00
if (this == std::addressof(__t))
return *this;
value_comp() = __t.value_comp();
__copy_assign_alloc(__t);
if (__size_ != 0) {
*__root_ptr() = std::__static_fancy_pointer_cast<__node_base_pointer>(__copy_assign_tree(__root(), __t.__root()));
[libc++] Optimize copy construction and assignment of __tree (#151304) ``` ---------------------------------------------------------------------------------------------------------- Benchmark old new ---------------------------------------------------------------------------------------------------------- std::map<int, int>::ctor(const&)/0 15.5 ns 14.9 ns std::map<int, int>::ctor(const&)/32 474 ns 321 ns std::map<int, int>::ctor(const&)/1024 24591 ns 11101 ns std::map<int, int>::ctor(const&)/8192 236153 ns 98868 ns std::map<std::string, int>::ctor(const&)/0 15.2 ns 14.9 ns std::map<std::string, int>::ctor(const&)/32 2673 ns 2340 ns std::map<std::string, int>::ctor(const&)/1024 115354 ns 86088 ns std::map<std::string, int>::ctor(const&)/8192 1298510 ns 626876 ns std::map<int, int>::operator=(const&) (into cleared Container)/0 16.5 ns 16.1 ns std::map<int, int>::operator=(const&) (into cleared Container)/32 548 ns 323 ns std::map<int, int>::operator=(const&) (into cleared Container)/1024 28418 ns 11026 ns std::map<int, int>::operator=(const&) (into cleared Container)/8192 281827 ns 97113 ns std::map<int, int>::operator=(const&) (into populated Container)/0 2.42 ns 1.85 ns std::map<int, int>::operator=(const&) (into populated Container)/32 369 ns 73.0 ns std::map<int, int>::operator=(const&) (into populated Container)/1024 24078 ns 2322 ns std::map<int, int>::operator=(const&) (into populated Container)/8192 266537 ns 22963 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/0 16.6 ns 16.2 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/32 2614 ns 1622 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/1024 116826 ns 63281 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/8192 1316655 ns 649177 ns std::map<std::string, int>::operator=(const&) (into populated Container)/0 2.42 ns 1.89 ns std::map<std::string, int>::operator=(const&) (into populated Container)/32 1264 ns 581 ns std::map<std::string, int>::operator=(const&) (into populated Container)/1024 238826 ns 39943 ns std::map<std::string, int>::operator=(const&) (into populated Container)/8192 2412327 ns 379456 ns ``` Fixes #77658 Fixes #62571
2025-08-04 23:49:40 -08:00
} else {
*__root_ptr() = std::__static_fancy_pointer_cast<__node_base_pointer>(__copy_construct_tree(__t.__root()));
[libc++] Optimize copy construction and assignment of __tree (#151304) ``` ---------------------------------------------------------------------------------------------------------- Benchmark old new ---------------------------------------------------------------------------------------------------------- std::map<int, int>::ctor(const&)/0 15.5 ns 14.9 ns std::map<int, int>::ctor(const&)/32 474 ns 321 ns std::map<int, int>::ctor(const&)/1024 24591 ns 11101 ns std::map<int, int>::ctor(const&)/8192 236153 ns 98868 ns std::map<std::string, int>::ctor(const&)/0 15.2 ns 14.9 ns std::map<std::string, int>::ctor(const&)/32 2673 ns 2340 ns std::map<std::string, int>::ctor(const&)/1024 115354 ns 86088 ns std::map<std::string, int>::ctor(const&)/8192 1298510 ns 626876 ns std::map<int, int>::operator=(const&) (into cleared Container)/0 16.5 ns 16.1 ns std::map<int, int>::operator=(const&) (into cleared Container)/32 548 ns 323 ns std::map<int, int>::operator=(const&) (into cleared Container)/1024 28418 ns 11026 ns std::map<int, int>::operator=(const&) (into cleared Container)/8192 281827 ns 97113 ns std::map<int, int>::operator=(const&) (into populated Container)/0 2.42 ns 1.85 ns std::map<int, int>::operator=(const&) (into populated Container)/32 369 ns 73.0 ns std::map<int, int>::operator=(const&) (into populated Container)/1024 24078 ns 2322 ns std::map<int, int>::operator=(const&) (into populated Container)/8192 266537 ns 22963 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/0 16.6 ns 16.2 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/32 2614 ns 1622 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/1024 116826 ns 63281 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/8192 1316655 ns 649177 ns std::map<std::string, int>::operator=(const&) (into populated Container)/0 2.42 ns 1.89 ns std::map<std::string, int>::operator=(const&) (into populated Container)/32 1264 ns 581 ns std::map<std::string, int>::operator=(const&) (into populated Container)/1024 238826 ns 39943 ns std::map<std::string, int>::operator=(const&) (into populated Container)/8192 2412327 ns 379456 ns ``` Fixes #77658 Fixes #62571
2025-08-04 23:49:40 -08:00
if (__root())
__root()->__parent_ = __end_node();
2010-05-11 11:42:16 -08:00
}
__begin_node_ = __end_node()->__left_
? std::__static_fancy_pointer_cast<__end_node_pointer>(std::__tree_min(__end_node()->__left_))
: __end_node();
__size_ = __t.size();
[libc++] Optimize copy construction and assignment of __tree (#151304) ``` ---------------------------------------------------------------------------------------------------------- Benchmark old new ---------------------------------------------------------------------------------------------------------- std::map<int, int>::ctor(const&)/0 15.5 ns 14.9 ns std::map<int, int>::ctor(const&)/32 474 ns 321 ns std::map<int, int>::ctor(const&)/1024 24591 ns 11101 ns std::map<int, int>::ctor(const&)/8192 236153 ns 98868 ns std::map<std::string, int>::ctor(const&)/0 15.2 ns 14.9 ns std::map<std::string, int>::ctor(const&)/32 2673 ns 2340 ns std::map<std::string, int>::ctor(const&)/1024 115354 ns 86088 ns std::map<std::string, int>::ctor(const&)/8192 1298510 ns 626876 ns std::map<int, int>::operator=(const&) (into cleared Container)/0 16.5 ns 16.1 ns std::map<int, int>::operator=(const&) (into cleared Container)/32 548 ns 323 ns std::map<int, int>::operator=(const&) (into cleared Container)/1024 28418 ns 11026 ns std::map<int, int>::operator=(const&) (into cleared Container)/8192 281827 ns 97113 ns std::map<int, int>::operator=(const&) (into populated Container)/0 2.42 ns 1.85 ns std::map<int, int>::operator=(const&) (into populated Container)/32 369 ns 73.0 ns std::map<int, int>::operator=(const&) (into populated Container)/1024 24078 ns 2322 ns std::map<int, int>::operator=(const&) (into populated Container)/8192 266537 ns 22963 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/0 16.6 ns 16.2 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/32 2614 ns 1622 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/1024 116826 ns 63281 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/8192 1316655 ns 649177 ns std::map<std::string, int>::operator=(const&) (into populated Container)/0 2.42 ns 1.89 ns std::map<std::string, int>::operator=(const&) (into populated Container)/32 1264 ns 581 ns std::map<std::string, int>::operator=(const&) (into populated Container)/1024 238826 ns 39943 ns std::map<std::string, int>::operator=(const&) (into populated Container)/8192 2412327 ns 379456 ns ``` Fixes #77658 Fixes #62571
2025-08-04 23:49:40 -08:00
2010-05-11 11:42:16 -08:00
return *this;
}
template <class _Tp, class _Compare, class _Allocator>
_LIBCPP_CONSTEXPR_SINCE_CXX26 __tree<_Tp, _Compare, _Allocator>::__tree(const __tree& __t)
[libc++] Optimize copy construction and assignment of __tree (#151304) ``` ---------------------------------------------------------------------------------------------------------- Benchmark old new ---------------------------------------------------------------------------------------------------------- std::map<int, int>::ctor(const&)/0 15.5 ns 14.9 ns std::map<int, int>::ctor(const&)/32 474 ns 321 ns std::map<int, int>::ctor(const&)/1024 24591 ns 11101 ns std::map<int, int>::ctor(const&)/8192 236153 ns 98868 ns std::map<std::string, int>::ctor(const&)/0 15.2 ns 14.9 ns std::map<std::string, int>::ctor(const&)/32 2673 ns 2340 ns std::map<std::string, int>::ctor(const&)/1024 115354 ns 86088 ns std::map<std::string, int>::ctor(const&)/8192 1298510 ns 626876 ns std::map<int, int>::operator=(const&) (into cleared Container)/0 16.5 ns 16.1 ns std::map<int, int>::operator=(const&) (into cleared Container)/32 548 ns 323 ns std::map<int, int>::operator=(const&) (into cleared Container)/1024 28418 ns 11026 ns std::map<int, int>::operator=(const&) (into cleared Container)/8192 281827 ns 97113 ns std::map<int, int>::operator=(const&) (into populated Container)/0 2.42 ns 1.85 ns std::map<int, int>::operator=(const&) (into populated Container)/32 369 ns 73.0 ns std::map<int, int>::operator=(const&) (into populated Container)/1024 24078 ns 2322 ns std::map<int, int>::operator=(const&) (into populated Container)/8192 266537 ns 22963 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/0 16.6 ns 16.2 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/32 2614 ns 1622 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/1024 116826 ns 63281 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/8192 1316655 ns 649177 ns std::map<std::string, int>::operator=(const&) (into populated Container)/0 2.42 ns 1.89 ns std::map<std::string, int>::operator=(const&) (into populated Container)/32 1264 ns 581 ns std::map<std::string, int>::operator=(const&) (into populated Container)/1024 238826 ns 39943 ns std::map<std::string, int>::operator=(const&) (into populated Container)/8192 2412327 ns 379456 ns ``` Fixes #77658 Fixes #62571
2025-08-04 23:49:40 -08:00
: __begin_node_(__end_node()),
__node_alloc_(__node_traits::select_on_container_copy_construction(__t.__node_alloc())),
__size_(0),
__value_comp_(__t.value_comp()) {
[libc++] Optimize copy construction and assignment of __tree (#151304) ``` ---------------------------------------------------------------------------------------------------------- Benchmark old new ---------------------------------------------------------------------------------------------------------- std::map<int, int>::ctor(const&)/0 15.5 ns 14.9 ns std::map<int, int>::ctor(const&)/32 474 ns 321 ns std::map<int, int>::ctor(const&)/1024 24591 ns 11101 ns std::map<int, int>::ctor(const&)/8192 236153 ns 98868 ns std::map<std::string, int>::ctor(const&)/0 15.2 ns 14.9 ns std::map<std::string, int>::ctor(const&)/32 2673 ns 2340 ns std::map<std::string, int>::ctor(const&)/1024 115354 ns 86088 ns std::map<std::string, int>::ctor(const&)/8192 1298510 ns 626876 ns std::map<int, int>::operator=(const&) (into cleared Container)/0 16.5 ns 16.1 ns std::map<int, int>::operator=(const&) (into cleared Container)/32 548 ns 323 ns std::map<int, int>::operator=(const&) (into cleared Container)/1024 28418 ns 11026 ns std::map<int, int>::operator=(const&) (into cleared Container)/8192 281827 ns 97113 ns std::map<int, int>::operator=(const&) (into populated Container)/0 2.42 ns 1.85 ns std::map<int, int>::operator=(const&) (into populated Container)/32 369 ns 73.0 ns std::map<int, int>::operator=(const&) (into populated Container)/1024 24078 ns 2322 ns std::map<int, int>::operator=(const&) (into populated Container)/8192 266537 ns 22963 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/0 16.6 ns 16.2 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/32 2614 ns 1622 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/1024 116826 ns 63281 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/8192 1316655 ns 649177 ns std::map<std::string, int>::operator=(const&) (into populated Container)/0 2.42 ns 1.89 ns std::map<std::string, int>::operator=(const&) (into populated Container)/32 1264 ns 581 ns std::map<std::string, int>::operator=(const&) (into populated Container)/1024 238826 ns 39943 ns std::map<std::string, int>::operator=(const&) (into populated Container)/8192 2412327 ns 379456 ns ``` Fixes #77658 Fixes #62571
2025-08-04 23:49:40 -08:00
if (__t.size() == 0)
return;
*__root_ptr() = std::__static_fancy_pointer_cast<__node_base_pointer>(__copy_construct_tree(__t.__root()));
[libc++] Optimize copy construction and assignment of __tree (#151304) ``` ---------------------------------------------------------------------------------------------------------- Benchmark old new ---------------------------------------------------------------------------------------------------------- std::map<int, int>::ctor(const&)/0 15.5 ns 14.9 ns std::map<int, int>::ctor(const&)/32 474 ns 321 ns std::map<int, int>::ctor(const&)/1024 24591 ns 11101 ns std::map<int, int>::ctor(const&)/8192 236153 ns 98868 ns std::map<std::string, int>::ctor(const&)/0 15.2 ns 14.9 ns std::map<std::string, int>::ctor(const&)/32 2673 ns 2340 ns std::map<std::string, int>::ctor(const&)/1024 115354 ns 86088 ns std::map<std::string, int>::ctor(const&)/8192 1298510 ns 626876 ns std::map<int, int>::operator=(const&) (into cleared Container)/0 16.5 ns 16.1 ns std::map<int, int>::operator=(const&) (into cleared Container)/32 548 ns 323 ns std::map<int, int>::operator=(const&) (into cleared Container)/1024 28418 ns 11026 ns std::map<int, int>::operator=(const&) (into cleared Container)/8192 281827 ns 97113 ns std::map<int, int>::operator=(const&) (into populated Container)/0 2.42 ns 1.85 ns std::map<int, int>::operator=(const&) (into populated Container)/32 369 ns 73.0 ns std::map<int, int>::operator=(const&) (into populated Container)/1024 24078 ns 2322 ns std::map<int, int>::operator=(const&) (into populated Container)/8192 266537 ns 22963 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/0 16.6 ns 16.2 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/32 2614 ns 1622 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/1024 116826 ns 63281 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/8192 1316655 ns 649177 ns std::map<std::string, int>::operator=(const&) (into populated Container)/0 2.42 ns 1.89 ns std::map<std::string, int>::operator=(const&) (into populated Container)/32 1264 ns 581 ns std::map<std::string, int>::operator=(const&) (into populated Container)/1024 238826 ns 39943 ns std::map<std::string, int>::operator=(const&) (into populated Container)/8192 2412327 ns 379456 ns ``` Fixes #77658 Fixes #62571
2025-08-04 23:49:40 -08:00
__root()->__parent_ = __end_node();
__begin_node_ = std::__static_fancy_pointer_cast<__end_node_pointer>(std::__tree_min(__end_node()->__left_));
__size_ = __t.size();
2010-05-11 11:42:16 -08:00
}
template <class _Tp, class _Compare, class _Allocator>
_LIBCPP_CONSTEXPR_SINCE_CXX26 __tree<_Tp, _Compare, _Allocator>::__tree(__tree&& __t) _NOEXCEPT_(
2011-06-04 06:31:57 -08:00
is_nothrow_move_constructible<__node_allocator>::value&& is_nothrow_move_constructible<value_compare>::value)
: __begin_node_(std::move(__t.__begin_node_)),
__end_node_(std::move(__t.__end_node_)),
__node_alloc_(std::move(__t.__node_alloc_)),
__size_(__t.__size_),
__value_comp_(std::move(__t.__value_comp_)) {
if (__size_ == 0)
__begin_node_ = __end_node();
2010-05-11 11:42:16 -08:00
else {
__end_node()->__left_->__parent_ = std::__static_fancy_pointer_cast<__end_node_pointer>(__end_node());
__t.__begin_node_ = __t.__end_node();
2010-05-11 11:42:16 -08:00
__t.__end_node()->__left_ = nullptr;
__t.__size_ = 0;
2010-05-11 11:42:16 -08:00
}
}
template <class _Tp, class _Compare, class _Allocator>
_LIBCPP_CONSTEXPR_SINCE_CXX26 __tree<_Tp, _Compare, _Allocator>::__tree(__tree&& __t, const allocator_type& __a)
[libc++] Optimize __tree copy/move constructor/assignment with allocator (#163558) This patch applies the same optimization as implemented in #151304 to the overloads taking an allocator as the second argument. Apple M4: ``` Benchmark old new Difference % Difference ----------------------------------------------------------- -------------- -------------- ------------ -------------- std::map<int,_int>::ctor(&&,_different_allocs)/0 14.59 12.78 -1.81 -12.41% std::map<int,_int>::ctor(&&,_different_allocs)/1024 16407.05 6265.11 -10141.94 -61.81% std::map<int,_int>::ctor(&&,_different_allocs)/32 395.99 199.76 -196.23 -49.56% std::map<int,_int>::ctor(&&,_different_allocs)/8192 141478.67 53767.84 -87710.83 -62.00% std::map<int,_int>::ctor(const&,_alloc)/0 12.83 12.71 -0.12 -0.94% std::map<int,_int>::ctor(const&,_alloc)/1024 9979.71 7849.11 -2130.59 -21.35% std::map<int,_int>::ctor(const&,_alloc)/32 283.82 266.05 -17.77 -6.26% std::map<int,_int>::ctor(const&,_alloc)/8192 81418.63 63190.41 -18228.21 -22.39% std::map<std::string,_int>::ctor(&&,_different_allocs)/0 14.58 12.68 -1.90 -13.00% std::map<std::string,_int>::ctor(&&,_different_allocs)/1024 19513.56 7806.04 -11707.52 -60.00% std::map<std::string,_int>::ctor(&&,_different_allocs)/32 477.80 247.28 -230.52 -48.25% std::map<std::string,_int>::ctor(&&,_different_allocs)/8192 504558.78 69592.21 -434966.56 -86.21% std::map<std::string,_int>::ctor(const&,_alloc)/0 12.64 12.60 -0.04 -0.33% std::map<std::string,_int>::ctor(const&,_alloc)/1024 43198.53 37220.54 -5977.99 -13.84% std::map<std::string,_int>::ctor(const&,_alloc)/32 928.39 867.03 -61.36 -6.61% std::map<std::string,_int>::ctor(const&,_alloc)/8192 461313.81 389200.82 -72112.99 -15.63% ```
2025-11-12 05:53:37 -09:00
: __begin_node_(__end_node()),
__node_alloc_(__node_allocator(__a)),
__size_(0),
__value_comp_(std::move(__t.value_comp())) {
if (__t.size() == 0)
return;
2010-05-11 11:42:16 -08:00
if (__a == __t.__alloc()) {
[libc++] Optimize __tree copy/move constructor/assignment with allocator (#163558) This patch applies the same optimization as implemented in #151304 to the overloads taking an allocator as the second argument. Apple M4: ``` Benchmark old new Difference % Difference ----------------------------------------------------------- -------------- -------------- ------------ -------------- std::map<int,_int>::ctor(&&,_different_allocs)/0 14.59 12.78 -1.81 -12.41% std::map<int,_int>::ctor(&&,_different_allocs)/1024 16407.05 6265.11 -10141.94 -61.81% std::map<int,_int>::ctor(&&,_different_allocs)/32 395.99 199.76 -196.23 -49.56% std::map<int,_int>::ctor(&&,_different_allocs)/8192 141478.67 53767.84 -87710.83 -62.00% std::map<int,_int>::ctor(const&,_alloc)/0 12.83 12.71 -0.12 -0.94% std::map<int,_int>::ctor(const&,_alloc)/1024 9979.71 7849.11 -2130.59 -21.35% std::map<int,_int>::ctor(const&,_alloc)/32 283.82 266.05 -17.77 -6.26% std::map<int,_int>::ctor(const&,_alloc)/8192 81418.63 63190.41 -18228.21 -22.39% std::map<std::string,_int>::ctor(&&,_different_allocs)/0 14.58 12.68 -1.90 -13.00% std::map<std::string,_int>::ctor(&&,_different_allocs)/1024 19513.56 7806.04 -11707.52 -60.00% std::map<std::string,_int>::ctor(&&,_different_allocs)/32 477.80 247.28 -230.52 -48.25% std::map<std::string,_int>::ctor(&&,_different_allocs)/8192 504558.78 69592.21 -434966.56 -86.21% std::map<std::string,_int>::ctor(const&,_alloc)/0 12.64 12.60 -0.04 -0.33% std::map<std::string,_int>::ctor(const&,_alloc)/1024 43198.53 37220.54 -5977.99 -13.84% std::map<std::string,_int>::ctor(const&,_alloc)/32 928.39 867.03 -61.36 -6.61% std::map<std::string,_int>::ctor(const&,_alloc)/8192 461313.81 389200.82 -72112.99 -15.63% ```
2025-11-12 05:53:37 -09:00
__begin_node_ = __t.__begin_node_;
__end_node()->__left_ = __t.__end_node()->__left_;
__end_node()->__left_->__parent_ = std::__static_fancy_pointer_cast<__end_node_pointer>(__end_node());
[libc++] Optimize __tree copy/move constructor/assignment with allocator (#163558) This patch applies the same optimization as implemented in #151304 to the overloads taking an allocator as the second argument. Apple M4: ``` Benchmark old new Difference % Difference ----------------------------------------------------------- -------------- -------------- ------------ -------------- std::map<int,_int>::ctor(&&,_different_allocs)/0 14.59 12.78 -1.81 -12.41% std::map<int,_int>::ctor(&&,_different_allocs)/1024 16407.05 6265.11 -10141.94 -61.81% std::map<int,_int>::ctor(&&,_different_allocs)/32 395.99 199.76 -196.23 -49.56% std::map<int,_int>::ctor(&&,_different_allocs)/8192 141478.67 53767.84 -87710.83 -62.00% std::map<int,_int>::ctor(const&,_alloc)/0 12.83 12.71 -0.12 -0.94% std::map<int,_int>::ctor(const&,_alloc)/1024 9979.71 7849.11 -2130.59 -21.35% std::map<int,_int>::ctor(const&,_alloc)/32 283.82 266.05 -17.77 -6.26% std::map<int,_int>::ctor(const&,_alloc)/8192 81418.63 63190.41 -18228.21 -22.39% std::map<std::string,_int>::ctor(&&,_different_allocs)/0 14.58 12.68 -1.90 -13.00% std::map<std::string,_int>::ctor(&&,_different_allocs)/1024 19513.56 7806.04 -11707.52 -60.00% std::map<std::string,_int>::ctor(&&,_different_allocs)/32 477.80 247.28 -230.52 -48.25% std::map<std::string,_int>::ctor(&&,_different_allocs)/8192 504558.78 69592.21 -434966.56 -86.21% std::map<std::string,_int>::ctor(const&,_alloc)/0 12.64 12.60 -0.04 -0.33% std::map<std::string,_int>::ctor(const&,_alloc)/1024 43198.53 37220.54 -5977.99 -13.84% std::map<std::string,_int>::ctor(const&,_alloc)/32 928.39 867.03 -61.36 -6.61% std::map<std::string,_int>::ctor(const&,_alloc)/8192 461313.81 389200.82 -72112.99 -15.63% ```
2025-11-12 05:53:37 -09:00
__size_ = __t.__size_;
__t.__begin_node_ = __t.__end_node();
__t.__end_node()->__left_ = nullptr;
__t.__size_ = 0;
2010-05-11 11:42:16 -08:00
} else {
*__root_ptr() = std::__static_fancy_pointer_cast<__node_base_pointer>(__move_construct_tree(__t.__root()));
[libc++] Optimize __tree copy/move constructor/assignment with allocator (#163558) This patch applies the same optimization as implemented in #151304 to the overloads taking an allocator as the second argument. Apple M4: ``` Benchmark old new Difference % Difference ----------------------------------------------------------- -------------- -------------- ------------ -------------- std::map<int,_int>::ctor(&&,_different_allocs)/0 14.59 12.78 -1.81 -12.41% std::map<int,_int>::ctor(&&,_different_allocs)/1024 16407.05 6265.11 -10141.94 -61.81% std::map<int,_int>::ctor(&&,_different_allocs)/32 395.99 199.76 -196.23 -49.56% std::map<int,_int>::ctor(&&,_different_allocs)/8192 141478.67 53767.84 -87710.83 -62.00% std::map<int,_int>::ctor(const&,_alloc)/0 12.83 12.71 -0.12 -0.94% std::map<int,_int>::ctor(const&,_alloc)/1024 9979.71 7849.11 -2130.59 -21.35% std::map<int,_int>::ctor(const&,_alloc)/32 283.82 266.05 -17.77 -6.26% std::map<int,_int>::ctor(const&,_alloc)/8192 81418.63 63190.41 -18228.21 -22.39% std::map<std::string,_int>::ctor(&&,_different_allocs)/0 14.58 12.68 -1.90 -13.00% std::map<std::string,_int>::ctor(&&,_different_allocs)/1024 19513.56 7806.04 -11707.52 -60.00% std::map<std::string,_int>::ctor(&&,_different_allocs)/32 477.80 247.28 -230.52 -48.25% std::map<std::string,_int>::ctor(&&,_different_allocs)/8192 504558.78 69592.21 -434966.56 -86.21% std::map<std::string,_int>::ctor(const&,_alloc)/0 12.64 12.60 -0.04 -0.33% std::map<std::string,_int>::ctor(const&,_alloc)/1024 43198.53 37220.54 -5977.99 -13.84% std::map<std::string,_int>::ctor(const&,_alloc)/32 928.39 867.03 -61.36 -6.61% std::map<std::string,_int>::ctor(const&,_alloc)/8192 461313.81 389200.82 -72112.99 -15.63% ```
2025-11-12 05:53:37 -09:00
__root()->__parent_ = __end_node();
__begin_node_ = std::__static_fancy_pointer_cast<__end_node_pointer>(std::__tree_min(__end_node()->__left_));
[libc++] Optimize __tree copy/move constructor/assignment with allocator (#163558) This patch applies the same optimization as implemented in #151304 to the overloads taking an allocator as the second argument. Apple M4: ``` Benchmark old new Difference % Difference ----------------------------------------------------------- -------------- -------------- ------------ -------------- std::map<int,_int>::ctor(&&,_different_allocs)/0 14.59 12.78 -1.81 -12.41% std::map<int,_int>::ctor(&&,_different_allocs)/1024 16407.05 6265.11 -10141.94 -61.81% std::map<int,_int>::ctor(&&,_different_allocs)/32 395.99 199.76 -196.23 -49.56% std::map<int,_int>::ctor(&&,_different_allocs)/8192 141478.67 53767.84 -87710.83 -62.00% std::map<int,_int>::ctor(const&,_alloc)/0 12.83 12.71 -0.12 -0.94% std::map<int,_int>::ctor(const&,_alloc)/1024 9979.71 7849.11 -2130.59 -21.35% std::map<int,_int>::ctor(const&,_alloc)/32 283.82 266.05 -17.77 -6.26% std::map<int,_int>::ctor(const&,_alloc)/8192 81418.63 63190.41 -18228.21 -22.39% std::map<std::string,_int>::ctor(&&,_different_allocs)/0 14.58 12.68 -1.90 -13.00% std::map<std::string,_int>::ctor(&&,_different_allocs)/1024 19513.56 7806.04 -11707.52 -60.00% std::map<std::string,_int>::ctor(&&,_different_allocs)/32 477.80 247.28 -230.52 -48.25% std::map<std::string,_int>::ctor(&&,_different_allocs)/8192 504558.78 69592.21 -434966.56 -86.21% std::map<std::string,_int>::ctor(const&,_alloc)/0 12.64 12.60 -0.04 -0.33% std::map<std::string,_int>::ctor(const&,_alloc)/1024 43198.53 37220.54 -5977.99 -13.84% std::map<std::string,_int>::ctor(const&,_alloc)/32 928.39 867.03 -61.36 -6.61% std::map<std::string,_int>::ctor(const&,_alloc)/8192 461313.81 389200.82 -72112.99 -15.63% ```
2025-11-12 05:53:37 -09:00
__size_ = __t.size();
__t.clear(); // Ensure that __t is in a valid state after moving out the keys
2010-05-11 11:42:16 -08:00
}
}
template <class _Tp, class _Compare, class _Allocator>
_LIBCPP_CONSTEXPR_SINCE_CXX26 void __tree<_Tp, _Compare, _Allocator>::__move_assign(__tree& __t, true_type)
2011-06-04 06:31:57 -08:00
_NOEXCEPT_(is_nothrow_move_assignable<value_compare>::value&& is_nothrow_move_assignable<__node_allocator>::value) {
destroy(std::__static_fancy_pointer_cast<__node_pointer>(__end_node()->__left_));
__begin_node_ = __t.__begin_node_;
__end_node_ = __t.__end_node_;
2010-05-11 11:42:16 -08:00
__move_assign_alloc(__t);
__size_ = __t.__size_;
__value_comp_ = std::move(__t.__value_comp_);
if (__size_ == 0)
__begin_node_ = __end_node();
2010-05-11 11:42:16 -08:00
else {
__end_node()->__left_->__parent_ = std::__static_fancy_pointer_cast<__end_node_pointer>(__end_node());
__t.__begin_node_ = __t.__end_node();
2010-05-11 11:42:16 -08:00
__t.__end_node()->__left_ = nullptr;
__t.__size_ = 0;
2010-05-11 11:42:16 -08:00
}
}
template <class _Tp, class _Compare, class _Allocator>
_LIBCPP_CONSTEXPR_SINCE_CXX26 void __tree<_Tp, _Compare, _Allocator>::__move_assign(__tree& __t, false_type) {
[libc++] Optimize __tree copy/move constructor/assignment with allocator (#163558) This patch applies the same optimization as implemented in #151304 to the overloads taking an allocator as the second argument. Apple M4: ``` Benchmark old new Difference % Difference ----------------------------------------------------------- -------------- -------------- ------------ -------------- std::map<int,_int>::ctor(&&,_different_allocs)/0 14.59 12.78 -1.81 -12.41% std::map<int,_int>::ctor(&&,_different_allocs)/1024 16407.05 6265.11 -10141.94 -61.81% std::map<int,_int>::ctor(&&,_different_allocs)/32 395.99 199.76 -196.23 -49.56% std::map<int,_int>::ctor(&&,_different_allocs)/8192 141478.67 53767.84 -87710.83 -62.00% std::map<int,_int>::ctor(const&,_alloc)/0 12.83 12.71 -0.12 -0.94% std::map<int,_int>::ctor(const&,_alloc)/1024 9979.71 7849.11 -2130.59 -21.35% std::map<int,_int>::ctor(const&,_alloc)/32 283.82 266.05 -17.77 -6.26% std::map<int,_int>::ctor(const&,_alloc)/8192 81418.63 63190.41 -18228.21 -22.39% std::map<std::string,_int>::ctor(&&,_different_allocs)/0 14.58 12.68 -1.90 -13.00% std::map<std::string,_int>::ctor(&&,_different_allocs)/1024 19513.56 7806.04 -11707.52 -60.00% std::map<std::string,_int>::ctor(&&,_different_allocs)/32 477.80 247.28 -230.52 -48.25% std::map<std::string,_int>::ctor(&&,_different_allocs)/8192 504558.78 69592.21 -434966.56 -86.21% std::map<std::string,_int>::ctor(const&,_alloc)/0 12.64 12.60 -0.04 -0.33% std::map<std::string,_int>::ctor(const&,_alloc)/1024 43198.53 37220.54 -5977.99 -13.84% std::map<std::string,_int>::ctor(const&,_alloc)/32 928.39 867.03 -61.36 -6.61% std::map<std::string,_int>::ctor(const&,_alloc)/8192 461313.81 389200.82 -72112.99 -15.63% ```
2025-11-12 05:53:37 -09:00
if (__node_alloc() == __t.__node_alloc()) {
2010-05-11 11:42:16 -08:00
__move_assign(__t, true_type());
[libc++] Optimize __tree copy/move constructor/assignment with allocator (#163558) This patch applies the same optimization as implemented in #151304 to the overloads taking an allocator as the second argument. Apple M4: ``` Benchmark old new Difference % Difference ----------------------------------------------------------- -------------- -------------- ------------ -------------- std::map<int,_int>::ctor(&&,_different_allocs)/0 14.59 12.78 -1.81 -12.41% std::map<int,_int>::ctor(&&,_different_allocs)/1024 16407.05 6265.11 -10141.94 -61.81% std::map<int,_int>::ctor(&&,_different_allocs)/32 395.99 199.76 -196.23 -49.56% std::map<int,_int>::ctor(&&,_different_allocs)/8192 141478.67 53767.84 -87710.83 -62.00% std::map<int,_int>::ctor(const&,_alloc)/0 12.83 12.71 -0.12 -0.94% std::map<int,_int>::ctor(const&,_alloc)/1024 9979.71 7849.11 -2130.59 -21.35% std::map<int,_int>::ctor(const&,_alloc)/32 283.82 266.05 -17.77 -6.26% std::map<int,_int>::ctor(const&,_alloc)/8192 81418.63 63190.41 -18228.21 -22.39% std::map<std::string,_int>::ctor(&&,_different_allocs)/0 14.58 12.68 -1.90 -13.00% std::map<std::string,_int>::ctor(&&,_different_allocs)/1024 19513.56 7806.04 -11707.52 -60.00% std::map<std::string,_int>::ctor(&&,_different_allocs)/32 477.80 247.28 -230.52 -48.25% std::map<std::string,_int>::ctor(&&,_different_allocs)/8192 504558.78 69592.21 -434966.56 -86.21% std::map<std::string,_int>::ctor(const&,_alloc)/0 12.64 12.60 -0.04 -0.33% std::map<std::string,_int>::ctor(const&,_alloc)/1024 43198.53 37220.54 -5977.99 -13.84% std::map<std::string,_int>::ctor(const&,_alloc)/32 928.39 867.03 -61.36 -6.61% std::map<std::string,_int>::ctor(const&,_alloc)/8192 461313.81 389200.82 -72112.99 -15.63% ```
2025-11-12 05:53:37 -09:00
} else {
value_comp() = std::move(__t.value_comp());
if (__size_ != 0) {
*__root_ptr() = std::__static_fancy_pointer_cast<__node_base_pointer>(__move_assign_tree(__root(), __t.__root()));
[libc++] Optimize __tree copy/move constructor/assignment with allocator (#163558) This patch applies the same optimization as implemented in #151304 to the overloads taking an allocator as the second argument. Apple M4: ``` Benchmark old new Difference % Difference ----------------------------------------------------------- -------------- -------------- ------------ -------------- std::map<int,_int>::ctor(&&,_different_allocs)/0 14.59 12.78 -1.81 -12.41% std::map<int,_int>::ctor(&&,_different_allocs)/1024 16407.05 6265.11 -10141.94 -61.81% std::map<int,_int>::ctor(&&,_different_allocs)/32 395.99 199.76 -196.23 -49.56% std::map<int,_int>::ctor(&&,_different_allocs)/8192 141478.67 53767.84 -87710.83 -62.00% std::map<int,_int>::ctor(const&,_alloc)/0 12.83 12.71 -0.12 -0.94% std::map<int,_int>::ctor(const&,_alloc)/1024 9979.71 7849.11 -2130.59 -21.35% std::map<int,_int>::ctor(const&,_alloc)/32 283.82 266.05 -17.77 -6.26% std::map<int,_int>::ctor(const&,_alloc)/8192 81418.63 63190.41 -18228.21 -22.39% std::map<std::string,_int>::ctor(&&,_different_allocs)/0 14.58 12.68 -1.90 -13.00% std::map<std::string,_int>::ctor(&&,_different_allocs)/1024 19513.56 7806.04 -11707.52 -60.00% std::map<std::string,_int>::ctor(&&,_different_allocs)/32 477.80 247.28 -230.52 -48.25% std::map<std::string,_int>::ctor(&&,_different_allocs)/8192 504558.78 69592.21 -434966.56 -86.21% std::map<std::string,_int>::ctor(const&,_alloc)/0 12.64 12.60 -0.04 -0.33% std::map<std::string,_int>::ctor(const&,_alloc)/1024 43198.53 37220.54 -5977.99 -13.84% std::map<std::string,_int>::ctor(const&,_alloc)/32 928.39 867.03 -61.36 -6.61% std::map<std::string,_int>::ctor(const&,_alloc)/8192 461313.81 389200.82 -72112.99 -15.63% ```
2025-11-12 05:53:37 -09:00
} else {
*__root_ptr() = std::__static_fancy_pointer_cast<__node_base_pointer>(__move_construct_tree(__t.__root()));
[libc++] Optimize __tree copy/move constructor/assignment with allocator (#163558) This patch applies the same optimization as implemented in #151304 to the overloads taking an allocator as the second argument. Apple M4: ``` Benchmark old new Difference % Difference ----------------------------------------------------------- -------------- -------------- ------------ -------------- std::map<int,_int>::ctor(&&,_different_allocs)/0 14.59 12.78 -1.81 -12.41% std::map<int,_int>::ctor(&&,_different_allocs)/1024 16407.05 6265.11 -10141.94 -61.81% std::map<int,_int>::ctor(&&,_different_allocs)/32 395.99 199.76 -196.23 -49.56% std::map<int,_int>::ctor(&&,_different_allocs)/8192 141478.67 53767.84 -87710.83 -62.00% std::map<int,_int>::ctor(const&,_alloc)/0 12.83 12.71 -0.12 -0.94% std::map<int,_int>::ctor(const&,_alloc)/1024 9979.71 7849.11 -2130.59 -21.35% std::map<int,_int>::ctor(const&,_alloc)/32 283.82 266.05 -17.77 -6.26% std::map<int,_int>::ctor(const&,_alloc)/8192 81418.63 63190.41 -18228.21 -22.39% std::map<std::string,_int>::ctor(&&,_different_allocs)/0 14.58 12.68 -1.90 -13.00% std::map<std::string,_int>::ctor(&&,_different_allocs)/1024 19513.56 7806.04 -11707.52 -60.00% std::map<std::string,_int>::ctor(&&,_different_allocs)/32 477.80 247.28 -230.52 -48.25% std::map<std::string,_int>::ctor(&&,_different_allocs)/8192 504558.78 69592.21 -434966.56 -86.21% std::map<std::string,_int>::ctor(const&,_alloc)/0 12.64 12.60 -0.04 -0.33% std::map<std::string,_int>::ctor(const&,_alloc)/1024 43198.53 37220.54 -5977.99 -13.84% std::map<std::string,_int>::ctor(const&,_alloc)/32 928.39 867.03 -61.36 -6.61% std::map<std::string,_int>::ctor(const&,_alloc)/8192 461313.81 389200.82 -72112.99 -15.63% ```
2025-11-12 05:53:37 -09:00
if (__root())
__root()->__parent_ = __end_node();
}
__begin_node_ = __end_node()->__left_
? std::__static_fancy_pointer_cast<__end_node_pointer>(std::__tree_min(__end_node()->__left_))
: __end_node();
[libc++] Optimize __tree copy/move constructor/assignment with allocator (#163558) This patch applies the same optimization as implemented in #151304 to the overloads taking an allocator as the second argument. Apple M4: ``` Benchmark old new Difference % Difference ----------------------------------------------------------- -------------- -------------- ------------ -------------- std::map<int,_int>::ctor(&&,_different_allocs)/0 14.59 12.78 -1.81 -12.41% std::map<int,_int>::ctor(&&,_different_allocs)/1024 16407.05 6265.11 -10141.94 -61.81% std::map<int,_int>::ctor(&&,_different_allocs)/32 395.99 199.76 -196.23 -49.56% std::map<int,_int>::ctor(&&,_different_allocs)/8192 141478.67 53767.84 -87710.83 -62.00% std::map<int,_int>::ctor(const&,_alloc)/0 12.83 12.71 -0.12 -0.94% std::map<int,_int>::ctor(const&,_alloc)/1024 9979.71 7849.11 -2130.59 -21.35% std::map<int,_int>::ctor(const&,_alloc)/32 283.82 266.05 -17.77 -6.26% std::map<int,_int>::ctor(const&,_alloc)/8192 81418.63 63190.41 -18228.21 -22.39% std::map<std::string,_int>::ctor(&&,_different_allocs)/0 14.58 12.68 -1.90 -13.00% std::map<std::string,_int>::ctor(&&,_different_allocs)/1024 19513.56 7806.04 -11707.52 -60.00% std::map<std::string,_int>::ctor(&&,_different_allocs)/32 477.80 247.28 -230.52 -48.25% std::map<std::string,_int>::ctor(&&,_different_allocs)/8192 504558.78 69592.21 -434966.56 -86.21% std::map<std::string,_int>::ctor(const&,_alloc)/0 12.64 12.60 -0.04 -0.33% std::map<std::string,_int>::ctor(const&,_alloc)/1024 43198.53 37220.54 -5977.99 -13.84% std::map<std::string,_int>::ctor(const&,_alloc)/32 928.39 867.03 -61.36 -6.61% std::map<std::string,_int>::ctor(const&,_alloc)/8192 461313.81 389200.82 -72112.99 -15.63% ```
2025-11-12 05:53:37 -09:00
__size_ = __t.size();
__t.clear(); // Ensure that __t is in a valid state after moving out the keys
}
2010-05-11 11:42:16 -08:00
}
template <class _Tp, class _Compare, class _Allocator>
_LIBCPP_CONSTEXPR_SINCE_CXX26 void __tree<_Tp, _Compare, _Allocator>::swap(__tree& __t)
#if _LIBCPP_STD_VER <= 11
_NOEXCEPT_(__is_nothrow_swappable_v<value_compare> &&
(!__node_traits::propagate_on_container_swap::value || __is_nothrow_swappable_v<__node_allocator>))
#else
_NOEXCEPT_(__is_nothrow_swappable_v<value_compare>)
#endif
2010-05-11 11:42:16 -08:00
{
using std::swap;
2010-05-11 11:42:16 -08:00
swap(__begin_node_, __t.__begin_node_);
swap(__end_node_, __t.__end_node_);
std::__swap_allocator(__node_alloc(), __t.__node_alloc());
swap(__size_, __t.__size_);
swap(__value_comp_, __t.__value_comp_);
if (__size_ == 0)
__begin_node_ = __end_node();
2010-05-11 11:42:16 -08:00
else
__end_node()->__left_->__parent_ = __end_node();
if (__t.__size_ == 0)
__t.__begin_node_ = __t.__end_node();
2010-05-11 11:42:16 -08:00
else
__t.__end_node()->__left_->__parent_ = __t.__end_node();
2010-05-11 11:42:16 -08:00
}
template <class _Tp, class _Compare, class _Allocator>
_LIBCPP_CONSTEXPR_SINCE_CXX26 void __tree<_Tp, _Compare, _Allocator>::clear() _NOEXCEPT {
2010-05-11 11:42:16 -08:00
destroy(__root());
__size_ = 0;
__begin_node_ = __end_node();
2010-05-11 11:42:16 -08:00
__end_node()->__left_ = nullptr;
}
// Find lower_bound place to insert
// Set __parent to parent of null leaf
// Return reference to null leaf
template <class _Tp, class _Compare, class _Allocator>
_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::__node_base_pointer&
__tree<_Tp, _Compare, _Allocator>::__find_leaf_low(__end_node_pointer& __parent, const value_type& __v) {
2010-05-11 11:42:16 -08:00
__node_pointer __nd = __root();
if (__nd != nullptr) {
while (true) {
if (value_comp()(__nd->__get_value(), __v)) {
2010-05-11 11:42:16 -08:00
if (__nd->__right_ != nullptr)
__nd = std::__static_fancy_pointer_cast<__node_pointer>(__nd->__right_);
2010-05-11 11:42:16 -08:00
else {
__parent = std::__static_fancy_pointer_cast<__end_node_pointer>(__nd);
2010-05-11 11:42:16 -08:00
return __nd->__right_;
}
} else {
2010-05-11 11:42:16 -08:00
if (__nd->__left_ != nullptr)
__nd = std::__static_fancy_pointer_cast<__node_pointer>(__nd->__left_);
else {
__parent = std::__static_fancy_pointer_cast<__end_node_pointer>(__nd);
2010-05-11 11:42:16 -08:00
return __parent->__left_;
}
}
2010-05-11 11:42:16 -08:00
}
}
__parent = __end_node();
2010-05-11 11:42:16 -08:00
return __parent->__left_;
}
// Find upper_bound place to insert
// Set __parent to parent of null leaf
// Return reference to null leaf
template <class _Tp, class _Compare, class _Allocator>
_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::__node_base_pointer&
__tree<_Tp, _Compare, _Allocator>::__find_leaf_high(__end_node_pointer& __parent, const value_type& __v) {
2010-05-11 11:42:16 -08:00
__node_pointer __nd = __root();
if (__nd != nullptr) {
while (true) {
if (value_comp()(__v, __nd->__get_value())) {
2010-05-11 11:42:16 -08:00
if (__nd->__left_ != nullptr)
__nd = std::__static_fancy_pointer_cast<__node_pointer>(__nd->__left_);
2010-05-11 11:42:16 -08:00
else {
__parent = std::__static_fancy_pointer_cast<__end_node_pointer>(__nd);
2010-05-11 11:42:16 -08:00
return __parent->__left_;
}
} else {
if (__nd->__right_ != nullptr)
__nd = std::__static_fancy_pointer_cast<__node_pointer>(__nd->__right_);
2010-05-11 11:42:16 -08:00
else {
__parent = std::__static_fancy_pointer_cast<__end_node_pointer>(__nd);
Fix undefined behavior in __tree Summary: This patch attempts to fix the undefined behavior in __tree by changing the node pointer types used throughout. The pointer types are changed for raw pointers in the current ABI and for fancy pointers in ABI V2 (since the fancy pointer types may not be ABI compatible). The UB in `__tree` arises because tree downcasts the embedded end node and then deferences that pointer. Currently there are 3 node types in __tree. * `__tree_end_node` which contains the `__left_` pointer. This node is embedded within the container. * `__tree_node_base` which contains `__right_`, `__parent_` and `__is_black`. This node is used throughout the tree rebalancing algorithms. * `__tree_node` which contains `__value_`. Currently `__tree` stores the start of the tree, `__begin_node_`, as a pointer to a `__tree_node`. Additionally the iterators store their position as a pointer to a `__tree_node`. In both of these cases the pointee can be the end node. This is fixed by changing them to store `__tree_end_node` pointers instead. To make this change I introduced an `__iter_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node` in the current one. Both `__tree::__begin_node_` and iterator pointers are now stored as `__iter_pointers`. The other situation where `__tree_end_node` is stored as the wrong type is in `__tree_node_base::__parent_`. Currently `__left_`, `__right_`, and `__parent_` are all `__tree_node_base` pointers. Since the end node will only be stored in `__parent_` the fix is to change `__parent_` to be a pointer to `__tree_end_node`. To make this change I introduced a `__parent_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node_base` in the current one. Note that in the new ABI `__iter_pointer` and `__parent_pointer` are the same type (but not in the old one). The confusion between these two types is unfortunate but it was the best solution I could come up with that maintains the ABI. The typedef changes force a ton of explicit type casts to correct pointer types and to make current code compatible with both the old and new pointer typedefs. This is the bulk of the change and it's really messy. Unfortunately I don't know how to avoid it. Please let me know what you think. Reviewers: howard.hinnant, mclow.lists Subscribers: howard.hinnant, bbannier, cfe-commits Differential Revision: https://reviews.llvm.org/D20786 llvm-svn: 276003
2016-07-19 09:56:20 -08:00
return __nd->__right_;
2010-05-11 11:42:16 -08:00
}
}
2010-05-11 11:42:16 -08:00
}
}
__parent = __end_node();
2010-05-11 11:42:16 -08:00
return __parent->__left_;
}
// Find leaf place to insert closest to __hint
// First check prior to __hint.
// Next check after __hint.
// Next do O(log N) search.
// Set __parent to parent of null leaf
// Return reference to null leaf
template <class _Tp, class _Compare, class _Allocator>
_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::__node_base_pointer&
__tree<_Tp, _Compare, _Allocator>::__find_leaf(
const_iterator __hint, __end_node_pointer& __parent, const value_type& __v) {
2010-05-11 11:42:16 -08:00
if (__hint == end() || !value_comp()(*__hint, __v)) // check before
{
// __v <= *__hint
const_iterator __prior = __hint;
if (__prior == begin() || !value_comp()(__v, *--__prior)) {
// *prev(__hint) <= __v <= *__hint
if (__hint.__ptr_->__left_ == nullptr) {
__parent = std::__static_fancy_pointer_cast<__end_node_pointer>(__hint.__ptr_);
2010-05-11 11:42:16 -08:00
return __parent->__left_;
} else {
__parent = std::__static_fancy_pointer_cast<__end_node_pointer>(__prior.__ptr_);
return std::__static_fancy_pointer_cast<__node_base_pointer>(__prior.__ptr_)->__right_;
2010-05-11 11:42:16 -08:00
}
}
// __v < *prev(__hint)
return __find_leaf_high(__parent, __v);
}
2010-05-11 11:42:16 -08:00
// else __v > *__hint
return __find_leaf_low(__parent, __v);
}
// Find __v
// If __v exists, return the parent of the node of __v and a reference to the pointer to the node of __v.
// If __v doesn't exist, return the parent of the null leaf and a reference to the pointer to the null leaf.
2010-05-11 11:42:16 -08:00
template <class _Tp, class _Compare, class _Allocator>
template <class _Key>
_LIBCPP_HIDE_FROM_ABI
_LIBCPP_CONSTEXPR_SINCE_CXX26 pair<typename __tree<_Tp, _Compare, _Allocator>::__end_node_pointer,
typename __tree<_Tp, _Compare, _Allocator>::__node_base_pointer&>
__tree<_Tp, _Compare, _Allocator>::__find_equal(const _Key& __v) {
using _Pair = pair<__end_node_pointer, __node_base_pointer&>;
__node_pointer __nd = __root();
if (__nd == nullptr) {
auto __end = __end_node();
return _Pair(__end, __end->__left_);
}
__node_base_pointer* __node_ptr = __root_ptr();
auto&& __transparent = std::__as_transparent<key_type>(value_comp());
auto __comp =
__lazy_synth_three_way_comparator<__make_transparent_t<key_type, _Compare>, _Key, value_type>(__transparent);
[libc++] Optimize most of the __tree search algorithms (#155245) This patch introduces a new comparator, namely `__lazy_synth_three_way`, which tries to provide an efficient three way comparator for known types and falls back to using the provided comparator if it doesn't know how to do that. Currently, an efficient three way comparison is only provided when using one of the `less` comparions object from the standard library and `std::string`. This will be extended in future patches. ``` ------------------------------------------------------------------------------------------------------------------------------ Benchmark old new ------------------------------------------------------------------------------------------------------------------------------ std::map<std::string, int>::ctor(const&)/0 12.6 ns 12.6 ns std::map<std::string, int>::ctor(const&)/32 858 ns 837 ns std::map<std::string, int>::ctor(const&)/1024 46700 ns 46739 ns std::map<std::string, int>::ctor(const&)/8192 458100 ns 449806 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/0 12.8 ns 12.7 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/32 1286 ns 1266 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/1024 93812 ns 84686 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/8192 1480346 ns 1385924 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/0 12.9 ns 12.8 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/32 1044 ns 1055 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/1024 63071 ns 62861 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/8192 595046 ns 590223 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/0 13.6 ns 13.6 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/32 880 ns 911 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/1024 48627 ns 47808 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/8192 458552 ns 454497 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/0 13.8 ns 13.6 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/32 864 ns 851 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/1024 49483 ns 49555 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/8192 456977 ns 457894 ns std::map<std::string, int>::operator=(const&) (into populated Container)/0 1.31 ns 1.31 ns std::map<std::string, int>::operator=(const&) (into populated Container)/32 425 ns 415 ns std::map<std::string, int>::operator=(const&) (into populated Container)/1024 14248 ns 14225 ns std::map<std::string, int>::operator=(const&) (into populated Container)/8192 136684 ns 133696 ns std::map<std::string, int>::insert(value) (already present)/0 21.5 ns 16.2 ns std::map<std::string, int>::insert(value) (already present)/32 22.7 ns 25.1 ns std::map<std::string, int>::insert(value) (already present)/1024 54.5 ns 29.1 ns std::map<std::string, int>::insert(value) (already present)/8192 78.4 ns 30.4 ns std::map<std::string, int>::insert(value) (new value)/0 40.9 ns 39.0 ns std::map<std::string, int>::insert(value) (new value)/32 58.3 ns 47.2 ns std::map<std::string, int>::insert(value) (new value)/1024 120 ns 71.3 ns std::map<std::string, int>::insert(value) (new value)/8192 157 ns 129 ns std::map<std::string, int>::insert(hint, value) (good hint)/0 40.3 ns 40.7 ns std::map<std::string, int>::insert(hint, value) (good hint)/32 48.0 ns 30.0 ns std::map<std::string, int>::insert(hint, value) (good hint)/1024 107 ns 63.2 ns std::map<std::string, int>::insert(hint, value) (good hint)/8192 132 ns 107 ns std::map<std::string, int>::insert(hint, value) (bad hint)/0 27.0 ns 40.9 ns std::map<std::string, int>::insert(hint, value) (bad hint)/32 68.3 ns 58.4 ns std::map<std::string, int>::insert(hint, value) (bad hint)/1024 125 ns 82.0 ns std::map<std::string, int>::insert(hint, value) (bad hint)/8192 155 ns 150 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/0 404 ns 405 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/32 2004 ns 1805 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/1024 102820 ns 76102 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/8192 1144590 ns 949266 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/0 408 ns 404 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/32 1592 ns 1377 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/1024 74847 ns 53921 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/8192 828505 ns 698716 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/0 407 ns 407 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/32 1584 ns 1557 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/1024 47157 ns 47443 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/8192 623887 ns 628385 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/0 405 ns 403 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/32 1478 ns 1510 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/1024 47852 ns 47835 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/8192 605311 ns 606951 ns std::map<std::string, int>::erase(key) (existent)/0 129 ns 94.0 ns std::map<std::string, int>::erase(key) (existent)/32 110 ns 106 ns std::map<std::string, int>::erase(key) (existent)/1024 121 ns 128 ns std::map<std::string, int>::erase(key) (existent)/8192 165 ns 66.9 ns std::map<std::string, int>::erase(key) (non-existent)/0 0.269 ns 0.257 ns std::map<std::string, int>::erase(key) (non-existent)/32 21.9 ns 11.3 ns std::map<std::string, int>::erase(key) (non-existent)/1024 53.5 ns 25.4 ns std::map<std::string, int>::erase(key) (non-existent)/8192 67.3 ns 31.9 ns std::map<std::string, int>::erase(iterator)/0 46.3 ns 46.7 ns std::map<std::string, int>::erase(iterator)/32 44.4 ns 41.8 ns std::map<std::string, int>::erase(iterator)/1024 43.7 ns 46.4 ns std::map<std::string, int>::erase(iterator)/8192 45.2 ns 44.1 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/0 407 ns 407 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/32 876 ns 906 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/1024 20880 ns 20444 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/8192 252881 ns 241583 ns std::map<std::string, int>::clear()/0 407 ns 408 ns std::map<std::string, int>::clear()/32 1252 ns 1323 ns std::map<std::string, int>::clear()/1024 38488 ns 38017 ns std::map<std::string, int>::clear()/8192 416492 ns 428534 ns std::map<std::string, int>::find(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::find(key) (existent)/32 33.9 ns 15.3 ns std::map<std::string, int>::find(key) (existent)/1024 43.0 ns 25.5 ns std::map<std::string, int>::find(key) (existent)/8192 44.6 ns 29.3 ns std::map<std::string, int>::find(key) (non-existent)/0 0.259 ns 0.257 ns std::map<std::string, int>::find(key) (non-existent)/32 22.6 ns 11.4 ns std::map<std::string, int>::find(key) (non-existent)/1024 48.6 ns 25.1 ns std::map<std::string, int>::find(key) (non-existent)/8192 64.1 ns 31.1 ns std::map<std::string, int>::count(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::count(key) (existent)/32 32.2 ns 17.3 ns std::map<std::string, int>::count(key) (existent)/1024 42.4 ns 25.3 ns std::map<std::string, int>::count(key) (existent)/8192 44.4 ns 31.6 ns std::map<std::string, int>::count(key) (non-existent)/0 0.260 ns 0.259 ns std::map<std::string, int>::count(key) (non-existent)/32 22.9 ns 11.3 ns std::map<std::string, int>::count(key) (non-existent)/1024 49.8 ns 25.5 ns std::map<std::string, int>::count(key) (non-existent)/8192 66.3 ns 31.9 ns std::map<std::string, int>::contains(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::contains(key) (existent)/32 31.4 ns 18.0 ns std::map<std::string, int>::contains(key) (existent)/1024 44.3 ns 26.5 ns std::map<std::string, int>::contains(key) (existent)/8192 47.4 ns 30.2 ns std::map<std::string, int>::contains(key) (non-existent)/0 0.452 ns 0.441 ns std::map<std::string, int>::contains(key) (non-existent)/32 23.1 ns 11.5 ns std::map<std::string, int>::contains(key) (non-existent)/1024 46.2 ns 26.3 ns std::map<std::string, int>::contains(key) (non-existent)/8192 63.4 ns 31.4 ns std::map<std::string, int>::lower_bound(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::lower_bound(key) (existent)/32 17.2 ns 19.0 ns std::map<std::string, int>::lower_bound(key) (existent)/1024 27.1 ns 26.2 ns std::map<std::string, int>::lower_bound(key) (existent)/8192 34.0 ns 36.0 ns std::map<std::string, int>::lower_bound(key) (non-existent)/0 0.259 ns 0.257 ns std::map<std::string, int>::lower_bound(key) (non-existent)/32 11.6 ns 11.5 ns std::map<std::string, int>::lower_bound(key) (non-existent)/1024 24.8 ns 25.6 ns std::map<std::string, int>::lower_bound(key) (non-existent)/8192 31.7 ns 31.6 ns std::map<std::string, int>::upper_bound(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::upper_bound(key) (existent)/32 18.8 ns 19.7 ns std::map<std::string, int>::upper_bound(key) (existent)/1024 25.3 ns 27.7 ns std::map<std::string, int>::upper_bound(key) (existent)/8192 30.2 ns 29.9 ns std::map<std::string, int>::upper_bound(key) (non-existent)/0 0.260 ns 0.259 ns std::map<std::string, int>::upper_bound(key) (non-existent)/32 11.3 ns 12.0 ns std::map<std::string, int>::upper_bound(key) (non-existent)/1024 25.6 ns 25.9 ns std::map<std::string, int>::upper_bound(key) (non-existent)/8192 33.1 ns 34.2 ns std::map<std::string, int>::equal_range(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::equal_range(key) (existent)/32 33.5 ns 15.8 ns std::map<std::string, int>::equal_range(key) (existent)/1024 43.0 ns 25.1 ns std::map<std::string, int>::equal_range(key) (existent)/8192 54.1 ns 30.7 ns std::map<std::string, int>::equal_range(key) (non-existent)/0 0.265 ns 0.259 ns std::map<std::string, int>::equal_range(key) (non-existent)/32 22.1 ns 12.1 ns std::map<std::string, int>::equal_range(key) (non-existent)/1024 44.8 ns 24.4 ns std::map<std::string, int>::equal_range(key) (non-existent)/8192 62.2 ns 40.1 ns ``` Fixes #66577
2025-09-07 23:56:14 -08:00
while (true) {
[libc++] Optimize most of the __tree search algorithms (#155245) This patch introduces a new comparator, namely `__lazy_synth_three_way`, which tries to provide an efficient three way comparator for known types and falls back to using the provided comparator if it doesn't know how to do that. Currently, an efficient three way comparison is only provided when using one of the `less` comparions object from the standard library and `std::string`. This will be extended in future patches. ``` ------------------------------------------------------------------------------------------------------------------------------ Benchmark old new ------------------------------------------------------------------------------------------------------------------------------ std::map<std::string, int>::ctor(const&)/0 12.6 ns 12.6 ns std::map<std::string, int>::ctor(const&)/32 858 ns 837 ns std::map<std::string, int>::ctor(const&)/1024 46700 ns 46739 ns std::map<std::string, int>::ctor(const&)/8192 458100 ns 449806 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/0 12.8 ns 12.7 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/32 1286 ns 1266 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/1024 93812 ns 84686 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/8192 1480346 ns 1385924 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/0 12.9 ns 12.8 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/32 1044 ns 1055 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/1024 63071 ns 62861 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/8192 595046 ns 590223 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/0 13.6 ns 13.6 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/32 880 ns 911 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/1024 48627 ns 47808 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/8192 458552 ns 454497 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/0 13.8 ns 13.6 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/32 864 ns 851 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/1024 49483 ns 49555 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/8192 456977 ns 457894 ns std::map<std::string, int>::operator=(const&) (into populated Container)/0 1.31 ns 1.31 ns std::map<std::string, int>::operator=(const&) (into populated Container)/32 425 ns 415 ns std::map<std::string, int>::operator=(const&) (into populated Container)/1024 14248 ns 14225 ns std::map<std::string, int>::operator=(const&) (into populated Container)/8192 136684 ns 133696 ns std::map<std::string, int>::insert(value) (already present)/0 21.5 ns 16.2 ns std::map<std::string, int>::insert(value) (already present)/32 22.7 ns 25.1 ns std::map<std::string, int>::insert(value) (already present)/1024 54.5 ns 29.1 ns std::map<std::string, int>::insert(value) (already present)/8192 78.4 ns 30.4 ns std::map<std::string, int>::insert(value) (new value)/0 40.9 ns 39.0 ns std::map<std::string, int>::insert(value) (new value)/32 58.3 ns 47.2 ns std::map<std::string, int>::insert(value) (new value)/1024 120 ns 71.3 ns std::map<std::string, int>::insert(value) (new value)/8192 157 ns 129 ns std::map<std::string, int>::insert(hint, value) (good hint)/0 40.3 ns 40.7 ns std::map<std::string, int>::insert(hint, value) (good hint)/32 48.0 ns 30.0 ns std::map<std::string, int>::insert(hint, value) (good hint)/1024 107 ns 63.2 ns std::map<std::string, int>::insert(hint, value) (good hint)/8192 132 ns 107 ns std::map<std::string, int>::insert(hint, value) (bad hint)/0 27.0 ns 40.9 ns std::map<std::string, int>::insert(hint, value) (bad hint)/32 68.3 ns 58.4 ns std::map<std::string, int>::insert(hint, value) (bad hint)/1024 125 ns 82.0 ns std::map<std::string, int>::insert(hint, value) (bad hint)/8192 155 ns 150 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/0 404 ns 405 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/32 2004 ns 1805 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/1024 102820 ns 76102 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/8192 1144590 ns 949266 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/0 408 ns 404 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/32 1592 ns 1377 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/1024 74847 ns 53921 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/8192 828505 ns 698716 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/0 407 ns 407 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/32 1584 ns 1557 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/1024 47157 ns 47443 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/8192 623887 ns 628385 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/0 405 ns 403 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/32 1478 ns 1510 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/1024 47852 ns 47835 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/8192 605311 ns 606951 ns std::map<std::string, int>::erase(key) (existent)/0 129 ns 94.0 ns std::map<std::string, int>::erase(key) (existent)/32 110 ns 106 ns std::map<std::string, int>::erase(key) (existent)/1024 121 ns 128 ns std::map<std::string, int>::erase(key) (existent)/8192 165 ns 66.9 ns std::map<std::string, int>::erase(key) (non-existent)/0 0.269 ns 0.257 ns std::map<std::string, int>::erase(key) (non-existent)/32 21.9 ns 11.3 ns std::map<std::string, int>::erase(key) (non-existent)/1024 53.5 ns 25.4 ns std::map<std::string, int>::erase(key) (non-existent)/8192 67.3 ns 31.9 ns std::map<std::string, int>::erase(iterator)/0 46.3 ns 46.7 ns std::map<std::string, int>::erase(iterator)/32 44.4 ns 41.8 ns std::map<std::string, int>::erase(iterator)/1024 43.7 ns 46.4 ns std::map<std::string, int>::erase(iterator)/8192 45.2 ns 44.1 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/0 407 ns 407 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/32 876 ns 906 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/1024 20880 ns 20444 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/8192 252881 ns 241583 ns std::map<std::string, int>::clear()/0 407 ns 408 ns std::map<std::string, int>::clear()/32 1252 ns 1323 ns std::map<std::string, int>::clear()/1024 38488 ns 38017 ns std::map<std::string, int>::clear()/8192 416492 ns 428534 ns std::map<std::string, int>::find(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::find(key) (existent)/32 33.9 ns 15.3 ns std::map<std::string, int>::find(key) (existent)/1024 43.0 ns 25.5 ns std::map<std::string, int>::find(key) (existent)/8192 44.6 ns 29.3 ns std::map<std::string, int>::find(key) (non-existent)/0 0.259 ns 0.257 ns std::map<std::string, int>::find(key) (non-existent)/32 22.6 ns 11.4 ns std::map<std::string, int>::find(key) (non-existent)/1024 48.6 ns 25.1 ns std::map<std::string, int>::find(key) (non-existent)/8192 64.1 ns 31.1 ns std::map<std::string, int>::count(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::count(key) (existent)/32 32.2 ns 17.3 ns std::map<std::string, int>::count(key) (existent)/1024 42.4 ns 25.3 ns std::map<std::string, int>::count(key) (existent)/8192 44.4 ns 31.6 ns std::map<std::string, int>::count(key) (non-existent)/0 0.260 ns 0.259 ns std::map<std::string, int>::count(key) (non-existent)/32 22.9 ns 11.3 ns std::map<std::string, int>::count(key) (non-existent)/1024 49.8 ns 25.5 ns std::map<std::string, int>::count(key) (non-existent)/8192 66.3 ns 31.9 ns std::map<std::string, int>::contains(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::contains(key) (existent)/32 31.4 ns 18.0 ns std::map<std::string, int>::contains(key) (existent)/1024 44.3 ns 26.5 ns std::map<std::string, int>::contains(key) (existent)/8192 47.4 ns 30.2 ns std::map<std::string, int>::contains(key) (non-existent)/0 0.452 ns 0.441 ns std::map<std::string, int>::contains(key) (non-existent)/32 23.1 ns 11.5 ns std::map<std::string, int>::contains(key) (non-existent)/1024 46.2 ns 26.3 ns std::map<std::string, int>::contains(key) (non-existent)/8192 63.4 ns 31.4 ns std::map<std::string, int>::lower_bound(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::lower_bound(key) (existent)/32 17.2 ns 19.0 ns std::map<std::string, int>::lower_bound(key) (existent)/1024 27.1 ns 26.2 ns std::map<std::string, int>::lower_bound(key) (existent)/8192 34.0 ns 36.0 ns std::map<std::string, int>::lower_bound(key) (non-existent)/0 0.259 ns 0.257 ns std::map<std::string, int>::lower_bound(key) (non-existent)/32 11.6 ns 11.5 ns std::map<std::string, int>::lower_bound(key) (non-existent)/1024 24.8 ns 25.6 ns std::map<std::string, int>::lower_bound(key) (non-existent)/8192 31.7 ns 31.6 ns std::map<std::string, int>::upper_bound(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::upper_bound(key) (existent)/32 18.8 ns 19.7 ns std::map<std::string, int>::upper_bound(key) (existent)/1024 25.3 ns 27.7 ns std::map<std::string, int>::upper_bound(key) (existent)/8192 30.2 ns 29.9 ns std::map<std::string, int>::upper_bound(key) (non-existent)/0 0.260 ns 0.259 ns std::map<std::string, int>::upper_bound(key) (non-existent)/32 11.3 ns 12.0 ns std::map<std::string, int>::upper_bound(key) (non-existent)/1024 25.6 ns 25.9 ns std::map<std::string, int>::upper_bound(key) (non-existent)/8192 33.1 ns 34.2 ns std::map<std::string, int>::equal_range(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::equal_range(key) (existent)/32 33.5 ns 15.8 ns std::map<std::string, int>::equal_range(key) (existent)/1024 43.0 ns 25.1 ns std::map<std::string, int>::equal_range(key) (existent)/8192 54.1 ns 30.7 ns std::map<std::string, int>::equal_range(key) (non-existent)/0 0.265 ns 0.259 ns std::map<std::string, int>::equal_range(key) (non-existent)/32 22.1 ns 12.1 ns std::map<std::string, int>::equal_range(key) (non-existent)/1024 44.8 ns 24.4 ns std::map<std::string, int>::equal_range(key) (non-existent)/8192 62.2 ns 40.1 ns ``` Fixes #66577
2025-09-07 23:56:14 -08:00
auto __comp_res = __comp(__v, __nd->__get_value());
if (__comp_res.__less()) {
if (__nd->__left_ == nullptr)
return _Pair(std::__static_fancy_pointer_cast<__end_node_pointer>(__nd), __nd->__left_);
__node_ptr = std::addressof(__nd->__left_);
__nd = std::__static_fancy_pointer_cast<__node_pointer>(__nd->__left_);
[libc++] Optimize most of the __tree search algorithms (#155245) This patch introduces a new comparator, namely `__lazy_synth_three_way`, which tries to provide an efficient three way comparator for known types and falls back to using the provided comparator if it doesn't know how to do that. Currently, an efficient three way comparison is only provided when using one of the `less` comparions object from the standard library and `std::string`. This will be extended in future patches. ``` ------------------------------------------------------------------------------------------------------------------------------ Benchmark old new ------------------------------------------------------------------------------------------------------------------------------ std::map<std::string, int>::ctor(const&)/0 12.6 ns 12.6 ns std::map<std::string, int>::ctor(const&)/32 858 ns 837 ns std::map<std::string, int>::ctor(const&)/1024 46700 ns 46739 ns std::map<std::string, int>::ctor(const&)/8192 458100 ns 449806 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/0 12.8 ns 12.7 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/32 1286 ns 1266 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/1024 93812 ns 84686 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/8192 1480346 ns 1385924 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/0 12.9 ns 12.8 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/32 1044 ns 1055 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/1024 63071 ns 62861 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/8192 595046 ns 590223 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/0 13.6 ns 13.6 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/32 880 ns 911 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/1024 48627 ns 47808 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/8192 458552 ns 454497 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/0 13.8 ns 13.6 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/32 864 ns 851 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/1024 49483 ns 49555 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/8192 456977 ns 457894 ns std::map<std::string, int>::operator=(const&) (into populated Container)/0 1.31 ns 1.31 ns std::map<std::string, int>::operator=(const&) (into populated Container)/32 425 ns 415 ns std::map<std::string, int>::operator=(const&) (into populated Container)/1024 14248 ns 14225 ns std::map<std::string, int>::operator=(const&) (into populated Container)/8192 136684 ns 133696 ns std::map<std::string, int>::insert(value) (already present)/0 21.5 ns 16.2 ns std::map<std::string, int>::insert(value) (already present)/32 22.7 ns 25.1 ns std::map<std::string, int>::insert(value) (already present)/1024 54.5 ns 29.1 ns std::map<std::string, int>::insert(value) (already present)/8192 78.4 ns 30.4 ns std::map<std::string, int>::insert(value) (new value)/0 40.9 ns 39.0 ns std::map<std::string, int>::insert(value) (new value)/32 58.3 ns 47.2 ns std::map<std::string, int>::insert(value) (new value)/1024 120 ns 71.3 ns std::map<std::string, int>::insert(value) (new value)/8192 157 ns 129 ns std::map<std::string, int>::insert(hint, value) (good hint)/0 40.3 ns 40.7 ns std::map<std::string, int>::insert(hint, value) (good hint)/32 48.0 ns 30.0 ns std::map<std::string, int>::insert(hint, value) (good hint)/1024 107 ns 63.2 ns std::map<std::string, int>::insert(hint, value) (good hint)/8192 132 ns 107 ns std::map<std::string, int>::insert(hint, value) (bad hint)/0 27.0 ns 40.9 ns std::map<std::string, int>::insert(hint, value) (bad hint)/32 68.3 ns 58.4 ns std::map<std::string, int>::insert(hint, value) (bad hint)/1024 125 ns 82.0 ns std::map<std::string, int>::insert(hint, value) (bad hint)/8192 155 ns 150 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/0 404 ns 405 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/32 2004 ns 1805 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/1024 102820 ns 76102 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/8192 1144590 ns 949266 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/0 408 ns 404 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/32 1592 ns 1377 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/1024 74847 ns 53921 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/8192 828505 ns 698716 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/0 407 ns 407 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/32 1584 ns 1557 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/1024 47157 ns 47443 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/8192 623887 ns 628385 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/0 405 ns 403 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/32 1478 ns 1510 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/1024 47852 ns 47835 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/8192 605311 ns 606951 ns std::map<std::string, int>::erase(key) (existent)/0 129 ns 94.0 ns std::map<std::string, int>::erase(key) (existent)/32 110 ns 106 ns std::map<std::string, int>::erase(key) (existent)/1024 121 ns 128 ns std::map<std::string, int>::erase(key) (existent)/8192 165 ns 66.9 ns std::map<std::string, int>::erase(key) (non-existent)/0 0.269 ns 0.257 ns std::map<std::string, int>::erase(key) (non-existent)/32 21.9 ns 11.3 ns std::map<std::string, int>::erase(key) (non-existent)/1024 53.5 ns 25.4 ns std::map<std::string, int>::erase(key) (non-existent)/8192 67.3 ns 31.9 ns std::map<std::string, int>::erase(iterator)/0 46.3 ns 46.7 ns std::map<std::string, int>::erase(iterator)/32 44.4 ns 41.8 ns std::map<std::string, int>::erase(iterator)/1024 43.7 ns 46.4 ns std::map<std::string, int>::erase(iterator)/8192 45.2 ns 44.1 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/0 407 ns 407 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/32 876 ns 906 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/1024 20880 ns 20444 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/8192 252881 ns 241583 ns std::map<std::string, int>::clear()/0 407 ns 408 ns std::map<std::string, int>::clear()/32 1252 ns 1323 ns std::map<std::string, int>::clear()/1024 38488 ns 38017 ns std::map<std::string, int>::clear()/8192 416492 ns 428534 ns std::map<std::string, int>::find(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::find(key) (existent)/32 33.9 ns 15.3 ns std::map<std::string, int>::find(key) (existent)/1024 43.0 ns 25.5 ns std::map<std::string, int>::find(key) (existent)/8192 44.6 ns 29.3 ns std::map<std::string, int>::find(key) (non-existent)/0 0.259 ns 0.257 ns std::map<std::string, int>::find(key) (non-existent)/32 22.6 ns 11.4 ns std::map<std::string, int>::find(key) (non-existent)/1024 48.6 ns 25.1 ns std::map<std::string, int>::find(key) (non-existent)/8192 64.1 ns 31.1 ns std::map<std::string, int>::count(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::count(key) (existent)/32 32.2 ns 17.3 ns std::map<std::string, int>::count(key) (existent)/1024 42.4 ns 25.3 ns std::map<std::string, int>::count(key) (existent)/8192 44.4 ns 31.6 ns std::map<std::string, int>::count(key) (non-existent)/0 0.260 ns 0.259 ns std::map<std::string, int>::count(key) (non-existent)/32 22.9 ns 11.3 ns std::map<std::string, int>::count(key) (non-existent)/1024 49.8 ns 25.5 ns std::map<std::string, int>::count(key) (non-existent)/8192 66.3 ns 31.9 ns std::map<std::string, int>::contains(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::contains(key) (existent)/32 31.4 ns 18.0 ns std::map<std::string, int>::contains(key) (existent)/1024 44.3 ns 26.5 ns std::map<std::string, int>::contains(key) (existent)/8192 47.4 ns 30.2 ns std::map<std::string, int>::contains(key) (non-existent)/0 0.452 ns 0.441 ns std::map<std::string, int>::contains(key) (non-existent)/32 23.1 ns 11.5 ns std::map<std::string, int>::contains(key) (non-existent)/1024 46.2 ns 26.3 ns std::map<std::string, int>::contains(key) (non-existent)/8192 63.4 ns 31.4 ns std::map<std::string, int>::lower_bound(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::lower_bound(key) (existent)/32 17.2 ns 19.0 ns std::map<std::string, int>::lower_bound(key) (existent)/1024 27.1 ns 26.2 ns std::map<std::string, int>::lower_bound(key) (existent)/8192 34.0 ns 36.0 ns std::map<std::string, int>::lower_bound(key) (non-existent)/0 0.259 ns 0.257 ns std::map<std::string, int>::lower_bound(key) (non-existent)/32 11.6 ns 11.5 ns std::map<std::string, int>::lower_bound(key) (non-existent)/1024 24.8 ns 25.6 ns std::map<std::string, int>::lower_bound(key) (non-existent)/8192 31.7 ns 31.6 ns std::map<std::string, int>::upper_bound(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::upper_bound(key) (existent)/32 18.8 ns 19.7 ns std::map<std::string, int>::upper_bound(key) (existent)/1024 25.3 ns 27.7 ns std::map<std::string, int>::upper_bound(key) (existent)/8192 30.2 ns 29.9 ns std::map<std::string, int>::upper_bound(key) (non-existent)/0 0.260 ns 0.259 ns std::map<std::string, int>::upper_bound(key) (non-existent)/32 11.3 ns 12.0 ns std::map<std::string, int>::upper_bound(key) (non-existent)/1024 25.6 ns 25.9 ns std::map<std::string, int>::upper_bound(key) (non-existent)/8192 33.1 ns 34.2 ns std::map<std::string, int>::equal_range(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::equal_range(key) (existent)/32 33.5 ns 15.8 ns std::map<std::string, int>::equal_range(key) (existent)/1024 43.0 ns 25.1 ns std::map<std::string, int>::equal_range(key) (existent)/8192 54.1 ns 30.7 ns std::map<std::string, int>::equal_range(key) (non-existent)/0 0.265 ns 0.259 ns std::map<std::string, int>::equal_range(key) (non-existent)/32 22.1 ns 12.1 ns std::map<std::string, int>::equal_range(key) (non-existent)/1024 44.8 ns 24.4 ns std::map<std::string, int>::equal_range(key) (non-existent)/8192 62.2 ns 40.1 ns ``` Fixes #66577
2025-09-07 23:56:14 -08:00
} else if (__comp_res.__greater()) {
if (__nd->__right_ == nullptr)
return _Pair(std::__static_fancy_pointer_cast<__end_node_pointer>(__nd), __nd->__right_);
__node_ptr = std::addressof(__nd->__right_);
__nd = std::__static_fancy_pointer_cast<__node_pointer>(__nd->__right_);
} else {
return _Pair(std::__static_fancy_pointer_cast<__end_node_pointer>(__nd), *__node_ptr);
2010-05-11 11:42:16 -08:00
}
}
2010-05-11 11:42:16 -08:00
}
// Find __v
2010-05-11 11:42:16 -08:00
// First check prior to __hint.
// Next check after __hint.
// Next do O(log N) search.
// If __v exists, return the parent of the node of __v and a reference to the pointer to the node of __v.
// If __v doesn't exist, return the parent of the null leaf and a reference to the pointer to the null leaf.
2010-05-11 11:42:16 -08:00
template <class _Tp, class _Compare, class _Allocator>
template <class _Key>
_LIBCPP_HIDE_FROM_ABI
_LIBCPP_CONSTEXPR_SINCE_CXX26 pair<typename __tree<_Tp, _Compare, _Allocator>::__end_node_pointer,
typename __tree<_Tp, _Compare, _Allocator>::__node_base_pointer&>
__tree<_Tp, _Compare, _Allocator>::__find_equal(const_iterator __hint, __node_base_pointer& __dummy, const _Key& __v) {
using _Pair = pair<__end_node_pointer, __node_base_pointer&>;
if (__hint == end() || value_comp()(__v, *__hint)) { // check before
2010-05-11 11:42:16 -08:00
// __v < *__hint
const_iterator __prior = __hint;
if (__prior == begin() || value_comp()(*--__prior, __v)) {
// *prev(__hint) < __v < *__hint
if (__hint.__ptr_->__left_ == nullptr)
return _Pair(__hint.__ptr_, __hint.__ptr_->__left_);
return _Pair(__prior.__ptr_, std::__static_fancy_pointer_cast<__node_pointer>(__prior.__ptr_)->__right_);
2010-05-11 11:42:16 -08:00
}
// __v <= *prev(__hint)
return __find_equal(__v);
}
if (value_comp()(*__hint, __v)) { // check after
2010-05-11 11:42:16 -08:00
// *__hint < __v
const_iterator __next = std::next(__hint);
2010-05-11 11:42:16 -08:00
if (__next == end() || value_comp()(__v, *__next)) {
// *__hint < __v < *std::next(__hint)
if (__hint.__get_np()->__right_ == nullptr)
return _Pair(__hint.__ptr_, std::__static_fancy_pointer_cast<__node_pointer>(__hint.__ptr_)->__right_);
return _Pair(__next.__ptr_, __next.__ptr_->__left_);
2010-05-11 11:42:16 -08:00
}
// *next(__hint) <= __v
return __find_equal(__v);
}
2010-05-11 11:42:16 -08:00
// else __v == *__hint
__dummy = std::__static_fancy_pointer_cast<__node_base_pointer>(__hint.__ptr_);
return _Pair(__hint.__ptr_, __dummy);
2010-05-11 11:42:16 -08:00
}
template <class _Tp, class _Compare, class _Allocator>
_LIBCPP_CONSTEXPR_SINCE_CXX26 void __tree<_Tp, _Compare, _Allocator>::__insert_node_at(
__end_node_pointer __parent, __node_base_pointer& __child, __node_base_pointer __new_node) _NOEXCEPT {
2010-05-11 11:42:16 -08:00
__new_node->__left_ = nullptr;
__new_node->__right_ = nullptr;
__new_node->__parent_ = __parent;
Teach __tree how to handle map's __value_type This patch is fairly large and contains a number of changes. The changes all work towards allowing __tree to properly handle __value_type esspecially when inserting into the __tree. I chose not to break this change into smaller patches because it wouldn't be possible to write meaningful standard-compliant tests for each patch. It is very similar to r260513 "[libcxx] Teach __hash_table how to handle unordered_map's __hash_value_type". Changes in <map> * Remove __value_type's constructors because it should never be constructed directly. * Make map::emplace and multimap::emplace forward to __tree and remove the old definitions * Remove "__construct_node" map and multimap member functions. Almost all of the construction is done within __tree. * Fix map's move constructor to access "__value_type.__nc" directly and pass this object to __tree::insert. Changes in <__tree> * Add traits to detect, handle, and unwrap, map's "__value_type". * Convert methods taking "value_type" to take "__container_value_type" instead. Previously these methods caused unwanted implicit conversions from "std::pair<Key, Value>" to "__value_type<Key, Value>". * Delete __tree_node and __tree_node_base's constructors and assignment operators. The node types should never be constructed because the "__value_" member of __tree_node must be constructed directly by the allocator. * Make the __tree_node_destructor class and "__construct_node" methods unwrap "__node_value_type" into "__container_value_type" before invoking the allocator. The user's allocator can only be used to construct and destroy the container's value_type. Passing it map's "__value_type" was incorrect. * Cleanup the "__insert" and "__emplace" methods. Have __insert forward to an __emplace function wherever possible to reduce code duplication. __insert_unique(value_type const&) and __insert_unique(value_type&&) forward to __emplace_unique_key_args. These functions will not allocate a new node if the value is already in the tree. * Change the __find* functions to take the "key_type" directly instead of passing in "value_type" and unwrapping the key later. This change allows the find functions to be used without having to construct a "value_type" first. This allows for a number of optimizations. * Teach __move_assign and __assign_multi methods to unwrap map's __value_type. llvm-svn: 264986
2016-03-30 18:15:15 -08:00
// __new_node->__is_black_ is initialized in __tree_balance_after_insert
2010-05-11 11:42:16 -08:00
__child = __new_node;
if (__begin_node_->__left_ != nullptr)
__begin_node_ = std::__static_fancy_pointer_cast<__end_node_pointer>(__begin_node_->__left_);
std::__tree_balance_after_insert(__end_node()->__left_, __child);
++__size_;
2010-05-11 11:42:16 -08:00
}
template <class _Tp, class _Compare, class _Allocator>
template <class... _Args>
_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::__node_holder
2010-05-11 11:42:16 -08:00
__tree<_Tp, _Compare, _Allocator>::__construct_node(_Args&&... __args) {
__node_allocator& __na = __node_alloc();
__node_holder __h(__node_traits::allocate(__na, 1), _Dp(__na));
std::__construct_at(std::addressof(*__h), __na, std::forward<_Args>(__args)...);
2010-05-11 11:42:16 -08:00
__h.get_deleter().__value_constructed = true;
return __h;
}
template <class _Tp, class _Compare, class _Allocator>
template <class... _Args>
typename __tree<_Tp, _Compare, _Allocator>::iterator _LIBCPP_CONSTEXPR_SINCE_CXX26
2010-05-11 11:42:16 -08:00
__tree<_Tp, _Compare, _Allocator>::__emplace_multi(_Args&&... __args) {
__node_holder __h = __construct_node(std::forward<_Args>(__args)...);
__end_node_pointer __parent;
__node_base_pointer& __child = __find_leaf_high(__parent, __h->__get_value());
__insert_node_at(__parent, __child, std::__static_fancy_pointer_cast<__node_base_pointer>(__h.get()));
return iterator(std::__static_fancy_pointer_cast<__node_pointer>(__h.release()));
2010-05-11 11:42:16 -08:00
}
template <class _Tp, class _Compare, class _Allocator>
template <class... _Args>
typename __tree<_Tp, _Compare, _Allocator>::iterator _LIBCPP_CONSTEXPR_SINCE_CXX26
2010-05-11 11:42:16 -08:00
__tree<_Tp, _Compare, _Allocator>::__emplace_hint_multi(const_iterator __p, _Args&&... __args) {
__node_holder __h = __construct_node(std::forward<_Args>(__args)...);
__end_node_pointer __parent;
__node_base_pointer& __child = __find_leaf(__p, __parent, __h->__get_value());
__insert_node_at(__parent, __child, std::__static_fancy_pointer_cast<__node_base_pointer>(__h.get()));
return iterator(std::__static_fancy_pointer_cast<__node_pointer>(__h.release()));
2010-05-11 11:42:16 -08:00
}
template <class _Tp, class _Compare, class _Allocator>
typename __tree<_Tp, _Compare, _Allocator>::iterator _LIBCPP_CONSTEXPR_SINCE_CXX26
__tree<_Tp, _Compare, _Allocator>::__remove_node_pointer(__node_pointer __ptr) _NOEXCEPT {
iterator __r(__ptr);
2010-05-11 11:42:16 -08:00
++__r;
if (__begin_node_ == __ptr)
__begin_node_ = __r.__ptr_;
--__size_;
std::__tree_remove(__end_node()->__left_, std::__static_fancy_pointer_cast<__node_base_pointer>(__ptr));
return __r;
}
#if _LIBCPP_STD_VER >= 17
template <class _Tp, class _Compare, class _Allocator>
template <class _NodeHandle, class _InsertReturnType>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _InsertReturnType
__tree<_Tp, _Compare, _Allocator>::__node_handle_insert_unique(_NodeHandle&& __nh) {
if (__nh.empty())
return _InsertReturnType{end(), false, _NodeHandle()};
__node_pointer __ptr = __nh.__ptr_;
auto [__parent, __child] = __find_equal(__ptr->__get_value());
if (__child != nullptr)
return _InsertReturnType{
iterator(std::__static_fancy_pointer_cast<__node_pointer>(__child)), false, std::move(__nh)};
__insert_node_at(__parent, __child, std::__static_fancy_pointer_cast<__node_base_pointer>(__ptr));
__nh.__release_ptr();
return _InsertReturnType{iterator(__ptr), true, _NodeHandle()};
}
template <class _Tp, class _Compare, class _Allocator>
template <class _NodeHandle>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::iterator
__tree<_Tp, _Compare, _Allocator>::__node_handle_insert_unique(const_iterator __hint, _NodeHandle&& __nh) {
if (__nh.empty())
return end();
__node_pointer __ptr = __nh.__ptr_;
__node_base_pointer __dummy;
auto [__parent, __child] = __find_equal(__hint, __dummy, __ptr->__get_value());
__node_pointer __r = std::__static_fancy_pointer_cast<__node_pointer>(__child);
if (__child == nullptr) {
__insert_node_at(__parent, __child, std::__static_fancy_pointer_cast<__node_base_pointer>(__ptr));
__r = __ptr;
__nh.__release_ptr();
}
return iterator(__r);
}
template <class _Tp, class _Compare, class _Allocator>
template <class _NodeHandle>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodeHandle
__tree<_Tp, _Compare, _Allocator>::__node_handle_extract(key_type const& __key) {
iterator __it = __lower_bound_multi(__key);
if (__it == end() || __value_comp_(__key, *__it))
return _NodeHandle();
return __node_handle_extract<_NodeHandle>(__it);
}
template <class _Tp, class _Compare, class _Allocator>
template <class _NodeHandle>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodeHandle
__tree<_Tp, _Compare, _Allocator>::__node_handle_extract(const_iterator __p) {
__node_pointer __np = __p.__get_np();
__remove_node_pointer(__np);
return _NodeHandle(__np, __alloc());
}
template <class _Tp, class _Compare, class _Allocator>
template <class _Comp2>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
__tree<_Tp, _Compare, _Allocator>::__node_handle_merge_unique(__tree<_Tp, _Comp2, _Allocator>& __source) {
for (iterator __i = __source.begin(); __i != __source.end();) {
__node_pointer __src_ptr = __i.__get_np();
auto [__parent, __child] = __find_equal(__src_ptr->__get_value());
++__i;
if (__child != nullptr)
continue;
__source.__remove_node_pointer(__src_ptr);
__insert_node_at(__parent, __child, std::__static_fancy_pointer_cast<__node_base_pointer>(__src_ptr));
}
}
template <class _Tp, class _Compare, class _Allocator>
template <class _NodeHandle>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::iterator
__tree<_Tp, _Compare, _Allocator>::__node_handle_insert_multi(_NodeHandle&& __nh) {
if (__nh.empty())
return end();
__node_pointer __ptr = __nh.__ptr_;
__end_node_pointer __parent;
__node_base_pointer& __child = __find_leaf_high(__parent, __ptr->__get_value());
__insert_node_at(__parent, __child, std::__static_fancy_pointer_cast<__node_base_pointer>(__ptr));
__nh.__release_ptr();
return iterator(__ptr);
}
template <class _Tp, class _Compare, class _Allocator>
template <class _NodeHandle>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::iterator
__tree<_Tp, _Compare, _Allocator>::__node_handle_insert_multi(const_iterator __hint, _NodeHandle&& __nh) {
if (__nh.empty())
return end();
__node_pointer __ptr = __nh.__ptr_;
__end_node_pointer __parent;
__node_base_pointer& __child = __find_leaf(__hint, __parent, __ptr->__get_value());
__insert_node_at(__parent, __child, std::__static_fancy_pointer_cast<__node_base_pointer>(__ptr));
__nh.__release_ptr();
return iterator(__ptr);
}
template <class _Tp, class _Compare, class _Allocator>
template <class _Comp2>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
__tree<_Tp, _Compare, _Allocator>::__node_handle_merge_multi(__tree<_Tp, _Comp2, _Allocator>& __source) {
for (iterator __i = __source.begin(); __i != __source.end();) {
__node_pointer __src_ptr = __i.__get_np();
__end_node_pointer __parent;
__node_base_pointer& __child = __find_leaf_high(__parent, __src_ptr->__get_value());
++__i;
__source.__remove_node_pointer(__src_ptr);
__insert_node_at(__parent, __child, std::__static_fancy_pointer_cast<__node_base_pointer>(__src_ptr));
}
}
#endif // _LIBCPP_STD_VER >= 17
template <class _Tp, class _Compare, class _Allocator>
_LIBCPP_CONSTEXPR_SINCE_CXX26
typename __tree<_Tp, _Compare, _Allocator>::iterator __tree<_Tp, _Compare, _Allocator>::erase(const_iterator __p) {
__node_pointer __np = __p.__get_np();
iterator __r = __remove_node_pointer(__np);
__node_allocator& __na = __node_alloc();
__node_traits::destroy(__na, std::addressof(const_cast<value_type&>(*__p)));
2010-05-11 11:42:16 -08:00
__node_traits::deallocate(__na, __np, 1);
return __r;
}
template <class _Tp, class _Compare, class _Allocator>
_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::iterator
2010-05-11 11:42:16 -08:00
__tree<_Tp, _Compare, _Allocator>::erase(const_iterator __f, const_iterator __l) {
while (__f != __l)
__f = erase(__f);
return iterator(__l.__ptr_);
2010-05-11 11:42:16 -08:00
}
template <class _Tp, class _Compare, class _Allocator>
template <class _Key>
_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::size_type
2010-05-11 11:42:16 -08:00
__tree<_Tp, _Compare, _Allocator>::__erase_unique(const _Key& __k) {
iterator __i = find(__k);
if (__i == end())
return 0;
erase(__i);
return 1;
}
template <class _Tp, class _Compare, class _Allocator>
template <class _Key>
_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::size_type
2010-05-11 11:42:16 -08:00
__tree<_Tp, _Compare, _Allocator>::__erase_multi(const _Key& __k) {
pair<iterator, iterator> __p = __equal_range_multi(__k);
size_type __r = 0;
for (; __p.first != __p.second; ++__r)
__p.first = erase(__p.first);
return __r;
}
template <class _Tp, class _Compare, class _Allocator>
template <class _Key>
_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::size_type
2010-05-11 11:42:16 -08:00
__tree<_Tp, _Compare, _Allocator>::__count_unique(const _Key& __k) const {
__node_pointer __rt = __root();
[libc++] Optimize most of the __tree search algorithms (#155245) This patch introduces a new comparator, namely `__lazy_synth_three_way`, which tries to provide an efficient three way comparator for known types and falls back to using the provided comparator if it doesn't know how to do that. Currently, an efficient three way comparison is only provided when using one of the `less` comparions object from the standard library and `std::string`. This will be extended in future patches. ``` ------------------------------------------------------------------------------------------------------------------------------ Benchmark old new ------------------------------------------------------------------------------------------------------------------------------ std::map<std::string, int>::ctor(const&)/0 12.6 ns 12.6 ns std::map<std::string, int>::ctor(const&)/32 858 ns 837 ns std::map<std::string, int>::ctor(const&)/1024 46700 ns 46739 ns std::map<std::string, int>::ctor(const&)/8192 458100 ns 449806 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/0 12.8 ns 12.7 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/32 1286 ns 1266 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/1024 93812 ns 84686 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/8192 1480346 ns 1385924 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/0 12.9 ns 12.8 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/32 1044 ns 1055 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/1024 63071 ns 62861 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/8192 595046 ns 590223 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/0 13.6 ns 13.6 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/32 880 ns 911 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/1024 48627 ns 47808 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/8192 458552 ns 454497 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/0 13.8 ns 13.6 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/32 864 ns 851 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/1024 49483 ns 49555 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/8192 456977 ns 457894 ns std::map<std::string, int>::operator=(const&) (into populated Container)/0 1.31 ns 1.31 ns std::map<std::string, int>::operator=(const&) (into populated Container)/32 425 ns 415 ns std::map<std::string, int>::operator=(const&) (into populated Container)/1024 14248 ns 14225 ns std::map<std::string, int>::operator=(const&) (into populated Container)/8192 136684 ns 133696 ns std::map<std::string, int>::insert(value) (already present)/0 21.5 ns 16.2 ns std::map<std::string, int>::insert(value) (already present)/32 22.7 ns 25.1 ns std::map<std::string, int>::insert(value) (already present)/1024 54.5 ns 29.1 ns std::map<std::string, int>::insert(value) (already present)/8192 78.4 ns 30.4 ns std::map<std::string, int>::insert(value) (new value)/0 40.9 ns 39.0 ns std::map<std::string, int>::insert(value) (new value)/32 58.3 ns 47.2 ns std::map<std::string, int>::insert(value) (new value)/1024 120 ns 71.3 ns std::map<std::string, int>::insert(value) (new value)/8192 157 ns 129 ns std::map<std::string, int>::insert(hint, value) (good hint)/0 40.3 ns 40.7 ns std::map<std::string, int>::insert(hint, value) (good hint)/32 48.0 ns 30.0 ns std::map<std::string, int>::insert(hint, value) (good hint)/1024 107 ns 63.2 ns std::map<std::string, int>::insert(hint, value) (good hint)/8192 132 ns 107 ns std::map<std::string, int>::insert(hint, value) (bad hint)/0 27.0 ns 40.9 ns std::map<std::string, int>::insert(hint, value) (bad hint)/32 68.3 ns 58.4 ns std::map<std::string, int>::insert(hint, value) (bad hint)/1024 125 ns 82.0 ns std::map<std::string, int>::insert(hint, value) (bad hint)/8192 155 ns 150 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/0 404 ns 405 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/32 2004 ns 1805 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/1024 102820 ns 76102 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/8192 1144590 ns 949266 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/0 408 ns 404 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/32 1592 ns 1377 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/1024 74847 ns 53921 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/8192 828505 ns 698716 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/0 407 ns 407 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/32 1584 ns 1557 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/1024 47157 ns 47443 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/8192 623887 ns 628385 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/0 405 ns 403 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/32 1478 ns 1510 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/1024 47852 ns 47835 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/8192 605311 ns 606951 ns std::map<std::string, int>::erase(key) (existent)/0 129 ns 94.0 ns std::map<std::string, int>::erase(key) (existent)/32 110 ns 106 ns std::map<std::string, int>::erase(key) (existent)/1024 121 ns 128 ns std::map<std::string, int>::erase(key) (existent)/8192 165 ns 66.9 ns std::map<std::string, int>::erase(key) (non-existent)/0 0.269 ns 0.257 ns std::map<std::string, int>::erase(key) (non-existent)/32 21.9 ns 11.3 ns std::map<std::string, int>::erase(key) (non-existent)/1024 53.5 ns 25.4 ns std::map<std::string, int>::erase(key) (non-existent)/8192 67.3 ns 31.9 ns std::map<std::string, int>::erase(iterator)/0 46.3 ns 46.7 ns std::map<std::string, int>::erase(iterator)/32 44.4 ns 41.8 ns std::map<std::string, int>::erase(iterator)/1024 43.7 ns 46.4 ns std::map<std::string, int>::erase(iterator)/8192 45.2 ns 44.1 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/0 407 ns 407 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/32 876 ns 906 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/1024 20880 ns 20444 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/8192 252881 ns 241583 ns std::map<std::string, int>::clear()/0 407 ns 408 ns std::map<std::string, int>::clear()/32 1252 ns 1323 ns std::map<std::string, int>::clear()/1024 38488 ns 38017 ns std::map<std::string, int>::clear()/8192 416492 ns 428534 ns std::map<std::string, int>::find(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::find(key) (existent)/32 33.9 ns 15.3 ns std::map<std::string, int>::find(key) (existent)/1024 43.0 ns 25.5 ns std::map<std::string, int>::find(key) (existent)/8192 44.6 ns 29.3 ns std::map<std::string, int>::find(key) (non-existent)/0 0.259 ns 0.257 ns std::map<std::string, int>::find(key) (non-existent)/32 22.6 ns 11.4 ns std::map<std::string, int>::find(key) (non-existent)/1024 48.6 ns 25.1 ns std::map<std::string, int>::find(key) (non-existent)/8192 64.1 ns 31.1 ns std::map<std::string, int>::count(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::count(key) (existent)/32 32.2 ns 17.3 ns std::map<std::string, int>::count(key) (existent)/1024 42.4 ns 25.3 ns std::map<std::string, int>::count(key) (existent)/8192 44.4 ns 31.6 ns std::map<std::string, int>::count(key) (non-existent)/0 0.260 ns 0.259 ns std::map<std::string, int>::count(key) (non-existent)/32 22.9 ns 11.3 ns std::map<std::string, int>::count(key) (non-existent)/1024 49.8 ns 25.5 ns std::map<std::string, int>::count(key) (non-existent)/8192 66.3 ns 31.9 ns std::map<std::string, int>::contains(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::contains(key) (existent)/32 31.4 ns 18.0 ns std::map<std::string, int>::contains(key) (existent)/1024 44.3 ns 26.5 ns std::map<std::string, int>::contains(key) (existent)/8192 47.4 ns 30.2 ns std::map<std::string, int>::contains(key) (non-existent)/0 0.452 ns 0.441 ns std::map<std::string, int>::contains(key) (non-existent)/32 23.1 ns 11.5 ns std::map<std::string, int>::contains(key) (non-existent)/1024 46.2 ns 26.3 ns std::map<std::string, int>::contains(key) (non-existent)/8192 63.4 ns 31.4 ns std::map<std::string, int>::lower_bound(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::lower_bound(key) (existent)/32 17.2 ns 19.0 ns std::map<std::string, int>::lower_bound(key) (existent)/1024 27.1 ns 26.2 ns std::map<std::string, int>::lower_bound(key) (existent)/8192 34.0 ns 36.0 ns std::map<std::string, int>::lower_bound(key) (non-existent)/0 0.259 ns 0.257 ns std::map<std::string, int>::lower_bound(key) (non-existent)/32 11.6 ns 11.5 ns std::map<std::string, int>::lower_bound(key) (non-existent)/1024 24.8 ns 25.6 ns std::map<std::string, int>::lower_bound(key) (non-existent)/8192 31.7 ns 31.6 ns std::map<std::string, int>::upper_bound(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::upper_bound(key) (existent)/32 18.8 ns 19.7 ns std::map<std::string, int>::upper_bound(key) (existent)/1024 25.3 ns 27.7 ns std::map<std::string, int>::upper_bound(key) (existent)/8192 30.2 ns 29.9 ns std::map<std::string, int>::upper_bound(key) (non-existent)/0 0.260 ns 0.259 ns std::map<std::string, int>::upper_bound(key) (non-existent)/32 11.3 ns 12.0 ns std::map<std::string, int>::upper_bound(key) (non-existent)/1024 25.6 ns 25.9 ns std::map<std::string, int>::upper_bound(key) (non-existent)/8192 33.1 ns 34.2 ns std::map<std::string, int>::equal_range(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::equal_range(key) (existent)/32 33.5 ns 15.8 ns std::map<std::string, int>::equal_range(key) (existent)/1024 43.0 ns 25.1 ns std::map<std::string, int>::equal_range(key) (existent)/8192 54.1 ns 30.7 ns std::map<std::string, int>::equal_range(key) (non-existent)/0 0.265 ns 0.259 ns std::map<std::string, int>::equal_range(key) (non-existent)/32 22.1 ns 12.1 ns std::map<std::string, int>::equal_range(key) (non-existent)/1024 44.8 ns 24.4 ns std::map<std::string, int>::equal_range(key) (non-existent)/8192 62.2 ns 40.1 ns ``` Fixes #66577
2025-09-07 23:56:14 -08:00
auto __comp = __lazy_synth_three_way_comparator<value_compare, _Key, value_type>(value_comp());
2010-05-11 11:42:16 -08:00
while (__rt != nullptr) {
[libc++] Optimize most of the __tree search algorithms (#155245) This patch introduces a new comparator, namely `__lazy_synth_three_way`, which tries to provide an efficient three way comparator for known types and falls back to using the provided comparator if it doesn't know how to do that. Currently, an efficient three way comparison is only provided when using one of the `less` comparions object from the standard library and `std::string`. This will be extended in future patches. ``` ------------------------------------------------------------------------------------------------------------------------------ Benchmark old new ------------------------------------------------------------------------------------------------------------------------------ std::map<std::string, int>::ctor(const&)/0 12.6 ns 12.6 ns std::map<std::string, int>::ctor(const&)/32 858 ns 837 ns std::map<std::string, int>::ctor(const&)/1024 46700 ns 46739 ns std::map<std::string, int>::ctor(const&)/8192 458100 ns 449806 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/0 12.8 ns 12.7 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/32 1286 ns 1266 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/1024 93812 ns 84686 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/8192 1480346 ns 1385924 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/0 12.9 ns 12.8 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/32 1044 ns 1055 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/1024 63071 ns 62861 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/8192 595046 ns 590223 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/0 13.6 ns 13.6 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/32 880 ns 911 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/1024 48627 ns 47808 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/8192 458552 ns 454497 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/0 13.8 ns 13.6 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/32 864 ns 851 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/1024 49483 ns 49555 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/8192 456977 ns 457894 ns std::map<std::string, int>::operator=(const&) (into populated Container)/0 1.31 ns 1.31 ns std::map<std::string, int>::operator=(const&) (into populated Container)/32 425 ns 415 ns std::map<std::string, int>::operator=(const&) (into populated Container)/1024 14248 ns 14225 ns std::map<std::string, int>::operator=(const&) (into populated Container)/8192 136684 ns 133696 ns std::map<std::string, int>::insert(value) (already present)/0 21.5 ns 16.2 ns std::map<std::string, int>::insert(value) (already present)/32 22.7 ns 25.1 ns std::map<std::string, int>::insert(value) (already present)/1024 54.5 ns 29.1 ns std::map<std::string, int>::insert(value) (already present)/8192 78.4 ns 30.4 ns std::map<std::string, int>::insert(value) (new value)/0 40.9 ns 39.0 ns std::map<std::string, int>::insert(value) (new value)/32 58.3 ns 47.2 ns std::map<std::string, int>::insert(value) (new value)/1024 120 ns 71.3 ns std::map<std::string, int>::insert(value) (new value)/8192 157 ns 129 ns std::map<std::string, int>::insert(hint, value) (good hint)/0 40.3 ns 40.7 ns std::map<std::string, int>::insert(hint, value) (good hint)/32 48.0 ns 30.0 ns std::map<std::string, int>::insert(hint, value) (good hint)/1024 107 ns 63.2 ns std::map<std::string, int>::insert(hint, value) (good hint)/8192 132 ns 107 ns std::map<std::string, int>::insert(hint, value) (bad hint)/0 27.0 ns 40.9 ns std::map<std::string, int>::insert(hint, value) (bad hint)/32 68.3 ns 58.4 ns std::map<std::string, int>::insert(hint, value) (bad hint)/1024 125 ns 82.0 ns std::map<std::string, int>::insert(hint, value) (bad hint)/8192 155 ns 150 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/0 404 ns 405 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/32 2004 ns 1805 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/1024 102820 ns 76102 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/8192 1144590 ns 949266 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/0 408 ns 404 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/32 1592 ns 1377 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/1024 74847 ns 53921 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/8192 828505 ns 698716 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/0 407 ns 407 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/32 1584 ns 1557 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/1024 47157 ns 47443 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/8192 623887 ns 628385 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/0 405 ns 403 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/32 1478 ns 1510 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/1024 47852 ns 47835 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/8192 605311 ns 606951 ns std::map<std::string, int>::erase(key) (existent)/0 129 ns 94.0 ns std::map<std::string, int>::erase(key) (existent)/32 110 ns 106 ns std::map<std::string, int>::erase(key) (existent)/1024 121 ns 128 ns std::map<std::string, int>::erase(key) (existent)/8192 165 ns 66.9 ns std::map<std::string, int>::erase(key) (non-existent)/0 0.269 ns 0.257 ns std::map<std::string, int>::erase(key) (non-existent)/32 21.9 ns 11.3 ns std::map<std::string, int>::erase(key) (non-existent)/1024 53.5 ns 25.4 ns std::map<std::string, int>::erase(key) (non-existent)/8192 67.3 ns 31.9 ns std::map<std::string, int>::erase(iterator)/0 46.3 ns 46.7 ns std::map<std::string, int>::erase(iterator)/32 44.4 ns 41.8 ns std::map<std::string, int>::erase(iterator)/1024 43.7 ns 46.4 ns std::map<std::string, int>::erase(iterator)/8192 45.2 ns 44.1 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/0 407 ns 407 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/32 876 ns 906 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/1024 20880 ns 20444 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/8192 252881 ns 241583 ns std::map<std::string, int>::clear()/0 407 ns 408 ns std::map<std::string, int>::clear()/32 1252 ns 1323 ns std::map<std::string, int>::clear()/1024 38488 ns 38017 ns std::map<std::string, int>::clear()/8192 416492 ns 428534 ns std::map<std::string, int>::find(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::find(key) (existent)/32 33.9 ns 15.3 ns std::map<std::string, int>::find(key) (existent)/1024 43.0 ns 25.5 ns std::map<std::string, int>::find(key) (existent)/8192 44.6 ns 29.3 ns std::map<std::string, int>::find(key) (non-existent)/0 0.259 ns 0.257 ns std::map<std::string, int>::find(key) (non-existent)/32 22.6 ns 11.4 ns std::map<std::string, int>::find(key) (non-existent)/1024 48.6 ns 25.1 ns std::map<std::string, int>::find(key) (non-existent)/8192 64.1 ns 31.1 ns std::map<std::string, int>::count(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::count(key) (existent)/32 32.2 ns 17.3 ns std::map<std::string, int>::count(key) (existent)/1024 42.4 ns 25.3 ns std::map<std::string, int>::count(key) (existent)/8192 44.4 ns 31.6 ns std::map<std::string, int>::count(key) (non-existent)/0 0.260 ns 0.259 ns std::map<std::string, int>::count(key) (non-existent)/32 22.9 ns 11.3 ns std::map<std::string, int>::count(key) (non-existent)/1024 49.8 ns 25.5 ns std::map<std::string, int>::count(key) (non-existent)/8192 66.3 ns 31.9 ns std::map<std::string, int>::contains(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::contains(key) (existent)/32 31.4 ns 18.0 ns std::map<std::string, int>::contains(key) (existent)/1024 44.3 ns 26.5 ns std::map<std::string, int>::contains(key) (existent)/8192 47.4 ns 30.2 ns std::map<std::string, int>::contains(key) (non-existent)/0 0.452 ns 0.441 ns std::map<std::string, int>::contains(key) (non-existent)/32 23.1 ns 11.5 ns std::map<std::string, int>::contains(key) (non-existent)/1024 46.2 ns 26.3 ns std::map<std::string, int>::contains(key) (non-existent)/8192 63.4 ns 31.4 ns std::map<std::string, int>::lower_bound(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::lower_bound(key) (existent)/32 17.2 ns 19.0 ns std::map<std::string, int>::lower_bound(key) (existent)/1024 27.1 ns 26.2 ns std::map<std::string, int>::lower_bound(key) (existent)/8192 34.0 ns 36.0 ns std::map<std::string, int>::lower_bound(key) (non-existent)/0 0.259 ns 0.257 ns std::map<std::string, int>::lower_bound(key) (non-existent)/32 11.6 ns 11.5 ns std::map<std::string, int>::lower_bound(key) (non-existent)/1024 24.8 ns 25.6 ns std::map<std::string, int>::lower_bound(key) (non-existent)/8192 31.7 ns 31.6 ns std::map<std::string, int>::upper_bound(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::upper_bound(key) (existent)/32 18.8 ns 19.7 ns std::map<std::string, int>::upper_bound(key) (existent)/1024 25.3 ns 27.7 ns std::map<std::string, int>::upper_bound(key) (existent)/8192 30.2 ns 29.9 ns std::map<std::string, int>::upper_bound(key) (non-existent)/0 0.260 ns 0.259 ns std::map<std::string, int>::upper_bound(key) (non-existent)/32 11.3 ns 12.0 ns std::map<std::string, int>::upper_bound(key) (non-existent)/1024 25.6 ns 25.9 ns std::map<std::string, int>::upper_bound(key) (non-existent)/8192 33.1 ns 34.2 ns std::map<std::string, int>::equal_range(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::equal_range(key) (existent)/32 33.5 ns 15.8 ns std::map<std::string, int>::equal_range(key) (existent)/1024 43.0 ns 25.1 ns std::map<std::string, int>::equal_range(key) (existent)/8192 54.1 ns 30.7 ns std::map<std::string, int>::equal_range(key) (non-existent)/0 0.265 ns 0.259 ns std::map<std::string, int>::equal_range(key) (non-existent)/32 22.1 ns 12.1 ns std::map<std::string, int>::equal_range(key) (non-existent)/1024 44.8 ns 24.4 ns std::map<std::string, int>::equal_range(key) (non-existent)/8192 62.2 ns 40.1 ns ``` Fixes #66577
2025-09-07 23:56:14 -08:00
auto __comp_res = __comp(__k, __rt->__get_value());
if (__comp_res.__less()) {
__rt = std::__static_fancy_pointer_cast<__node_pointer>(__rt->__left_);
[libc++] Optimize most of the __tree search algorithms (#155245) This patch introduces a new comparator, namely `__lazy_synth_three_way`, which tries to provide an efficient three way comparator for known types and falls back to using the provided comparator if it doesn't know how to do that. Currently, an efficient three way comparison is only provided when using one of the `less` comparions object from the standard library and `std::string`. This will be extended in future patches. ``` ------------------------------------------------------------------------------------------------------------------------------ Benchmark old new ------------------------------------------------------------------------------------------------------------------------------ std::map<std::string, int>::ctor(const&)/0 12.6 ns 12.6 ns std::map<std::string, int>::ctor(const&)/32 858 ns 837 ns std::map<std::string, int>::ctor(const&)/1024 46700 ns 46739 ns std::map<std::string, int>::ctor(const&)/8192 458100 ns 449806 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/0 12.8 ns 12.7 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/32 1286 ns 1266 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/1024 93812 ns 84686 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/8192 1480346 ns 1385924 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/0 12.9 ns 12.8 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/32 1044 ns 1055 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/1024 63071 ns 62861 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/8192 595046 ns 590223 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/0 13.6 ns 13.6 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/32 880 ns 911 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/1024 48627 ns 47808 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/8192 458552 ns 454497 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/0 13.8 ns 13.6 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/32 864 ns 851 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/1024 49483 ns 49555 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/8192 456977 ns 457894 ns std::map<std::string, int>::operator=(const&) (into populated Container)/0 1.31 ns 1.31 ns std::map<std::string, int>::operator=(const&) (into populated Container)/32 425 ns 415 ns std::map<std::string, int>::operator=(const&) (into populated Container)/1024 14248 ns 14225 ns std::map<std::string, int>::operator=(const&) (into populated Container)/8192 136684 ns 133696 ns std::map<std::string, int>::insert(value) (already present)/0 21.5 ns 16.2 ns std::map<std::string, int>::insert(value) (already present)/32 22.7 ns 25.1 ns std::map<std::string, int>::insert(value) (already present)/1024 54.5 ns 29.1 ns std::map<std::string, int>::insert(value) (already present)/8192 78.4 ns 30.4 ns std::map<std::string, int>::insert(value) (new value)/0 40.9 ns 39.0 ns std::map<std::string, int>::insert(value) (new value)/32 58.3 ns 47.2 ns std::map<std::string, int>::insert(value) (new value)/1024 120 ns 71.3 ns std::map<std::string, int>::insert(value) (new value)/8192 157 ns 129 ns std::map<std::string, int>::insert(hint, value) (good hint)/0 40.3 ns 40.7 ns std::map<std::string, int>::insert(hint, value) (good hint)/32 48.0 ns 30.0 ns std::map<std::string, int>::insert(hint, value) (good hint)/1024 107 ns 63.2 ns std::map<std::string, int>::insert(hint, value) (good hint)/8192 132 ns 107 ns std::map<std::string, int>::insert(hint, value) (bad hint)/0 27.0 ns 40.9 ns std::map<std::string, int>::insert(hint, value) (bad hint)/32 68.3 ns 58.4 ns std::map<std::string, int>::insert(hint, value) (bad hint)/1024 125 ns 82.0 ns std::map<std::string, int>::insert(hint, value) (bad hint)/8192 155 ns 150 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/0 404 ns 405 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/32 2004 ns 1805 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/1024 102820 ns 76102 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/8192 1144590 ns 949266 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/0 408 ns 404 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/32 1592 ns 1377 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/1024 74847 ns 53921 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/8192 828505 ns 698716 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/0 407 ns 407 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/32 1584 ns 1557 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/1024 47157 ns 47443 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/8192 623887 ns 628385 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/0 405 ns 403 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/32 1478 ns 1510 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/1024 47852 ns 47835 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/8192 605311 ns 606951 ns std::map<std::string, int>::erase(key) (existent)/0 129 ns 94.0 ns std::map<std::string, int>::erase(key) (existent)/32 110 ns 106 ns std::map<std::string, int>::erase(key) (existent)/1024 121 ns 128 ns std::map<std::string, int>::erase(key) (existent)/8192 165 ns 66.9 ns std::map<std::string, int>::erase(key) (non-existent)/0 0.269 ns 0.257 ns std::map<std::string, int>::erase(key) (non-existent)/32 21.9 ns 11.3 ns std::map<std::string, int>::erase(key) (non-existent)/1024 53.5 ns 25.4 ns std::map<std::string, int>::erase(key) (non-existent)/8192 67.3 ns 31.9 ns std::map<std::string, int>::erase(iterator)/0 46.3 ns 46.7 ns std::map<std::string, int>::erase(iterator)/32 44.4 ns 41.8 ns std::map<std::string, int>::erase(iterator)/1024 43.7 ns 46.4 ns std::map<std::string, int>::erase(iterator)/8192 45.2 ns 44.1 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/0 407 ns 407 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/32 876 ns 906 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/1024 20880 ns 20444 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/8192 252881 ns 241583 ns std::map<std::string, int>::clear()/0 407 ns 408 ns std::map<std::string, int>::clear()/32 1252 ns 1323 ns std::map<std::string, int>::clear()/1024 38488 ns 38017 ns std::map<std::string, int>::clear()/8192 416492 ns 428534 ns std::map<std::string, int>::find(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::find(key) (existent)/32 33.9 ns 15.3 ns std::map<std::string, int>::find(key) (existent)/1024 43.0 ns 25.5 ns std::map<std::string, int>::find(key) (existent)/8192 44.6 ns 29.3 ns std::map<std::string, int>::find(key) (non-existent)/0 0.259 ns 0.257 ns std::map<std::string, int>::find(key) (non-existent)/32 22.6 ns 11.4 ns std::map<std::string, int>::find(key) (non-existent)/1024 48.6 ns 25.1 ns std::map<std::string, int>::find(key) (non-existent)/8192 64.1 ns 31.1 ns std::map<std::string, int>::count(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::count(key) (existent)/32 32.2 ns 17.3 ns std::map<std::string, int>::count(key) (existent)/1024 42.4 ns 25.3 ns std::map<std::string, int>::count(key) (existent)/8192 44.4 ns 31.6 ns std::map<std::string, int>::count(key) (non-existent)/0 0.260 ns 0.259 ns std::map<std::string, int>::count(key) (non-existent)/32 22.9 ns 11.3 ns std::map<std::string, int>::count(key) (non-existent)/1024 49.8 ns 25.5 ns std::map<std::string, int>::count(key) (non-existent)/8192 66.3 ns 31.9 ns std::map<std::string, int>::contains(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::contains(key) (existent)/32 31.4 ns 18.0 ns std::map<std::string, int>::contains(key) (existent)/1024 44.3 ns 26.5 ns std::map<std::string, int>::contains(key) (existent)/8192 47.4 ns 30.2 ns std::map<std::string, int>::contains(key) (non-existent)/0 0.452 ns 0.441 ns std::map<std::string, int>::contains(key) (non-existent)/32 23.1 ns 11.5 ns std::map<std::string, int>::contains(key) (non-existent)/1024 46.2 ns 26.3 ns std::map<std::string, int>::contains(key) (non-existent)/8192 63.4 ns 31.4 ns std::map<std::string, int>::lower_bound(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::lower_bound(key) (existent)/32 17.2 ns 19.0 ns std::map<std::string, int>::lower_bound(key) (existent)/1024 27.1 ns 26.2 ns std::map<std::string, int>::lower_bound(key) (existent)/8192 34.0 ns 36.0 ns std::map<std::string, int>::lower_bound(key) (non-existent)/0 0.259 ns 0.257 ns std::map<std::string, int>::lower_bound(key) (non-existent)/32 11.6 ns 11.5 ns std::map<std::string, int>::lower_bound(key) (non-existent)/1024 24.8 ns 25.6 ns std::map<std::string, int>::lower_bound(key) (non-existent)/8192 31.7 ns 31.6 ns std::map<std::string, int>::upper_bound(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::upper_bound(key) (existent)/32 18.8 ns 19.7 ns std::map<std::string, int>::upper_bound(key) (existent)/1024 25.3 ns 27.7 ns std::map<std::string, int>::upper_bound(key) (existent)/8192 30.2 ns 29.9 ns std::map<std::string, int>::upper_bound(key) (non-existent)/0 0.260 ns 0.259 ns std::map<std::string, int>::upper_bound(key) (non-existent)/32 11.3 ns 12.0 ns std::map<std::string, int>::upper_bound(key) (non-existent)/1024 25.6 ns 25.9 ns std::map<std::string, int>::upper_bound(key) (non-existent)/8192 33.1 ns 34.2 ns std::map<std::string, int>::equal_range(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::equal_range(key) (existent)/32 33.5 ns 15.8 ns std::map<std::string, int>::equal_range(key) (existent)/1024 43.0 ns 25.1 ns std::map<std::string, int>::equal_range(key) (existent)/8192 54.1 ns 30.7 ns std::map<std::string, int>::equal_range(key) (non-existent)/0 0.265 ns 0.259 ns std::map<std::string, int>::equal_range(key) (non-existent)/32 22.1 ns 12.1 ns std::map<std::string, int>::equal_range(key) (non-existent)/1024 44.8 ns 24.4 ns std::map<std::string, int>::equal_range(key) (non-existent)/8192 62.2 ns 40.1 ns ``` Fixes #66577
2025-09-07 23:56:14 -08:00
} else if (__comp_res.__greater())
__rt = std::__static_fancy_pointer_cast<__node_pointer>(__rt->__right_);
2010-05-11 11:42:16 -08:00
else
return 1;
}
return 0;
}
template <class _Tp, class _Compare, class _Allocator>
template <class _Key>
_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::size_type
2010-05-11 11:42:16 -08:00
__tree<_Tp, _Compare, _Allocator>::__count_multi(const _Key& __k) const {
__end_node_pointer __result = __end_node();
__node_pointer __rt = __root();
[libc++] Optimize most of the __tree search algorithms (#155245) This patch introduces a new comparator, namely `__lazy_synth_three_way`, which tries to provide an efficient three way comparator for known types and falls back to using the provided comparator if it doesn't know how to do that. Currently, an efficient three way comparison is only provided when using one of the `less` comparions object from the standard library and `std::string`. This will be extended in future patches. ``` ------------------------------------------------------------------------------------------------------------------------------ Benchmark old new ------------------------------------------------------------------------------------------------------------------------------ std::map<std::string, int>::ctor(const&)/0 12.6 ns 12.6 ns std::map<std::string, int>::ctor(const&)/32 858 ns 837 ns std::map<std::string, int>::ctor(const&)/1024 46700 ns 46739 ns std::map<std::string, int>::ctor(const&)/8192 458100 ns 449806 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/0 12.8 ns 12.7 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/32 1286 ns 1266 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/1024 93812 ns 84686 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/8192 1480346 ns 1385924 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/0 12.9 ns 12.8 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/32 1044 ns 1055 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/1024 63071 ns 62861 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/8192 595046 ns 590223 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/0 13.6 ns 13.6 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/32 880 ns 911 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/1024 48627 ns 47808 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/8192 458552 ns 454497 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/0 13.8 ns 13.6 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/32 864 ns 851 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/1024 49483 ns 49555 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/8192 456977 ns 457894 ns std::map<std::string, int>::operator=(const&) (into populated Container)/0 1.31 ns 1.31 ns std::map<std::string, int>::operator=(const&) (into populated Container)/32 425 ns 415 ns std::map<std::string, int>::operator=(const&) (into populated Container)/1024 14248 ns 14225 ns std::map<std::string, int>::operator=(const&) (into populated Container)/8192 136684 ns 133696 ns std::map<std::string, int>::insert(value) (already present)/0 21.5 ns 16.2 ns std::map<std::string, int>::insert(value) (already present)/32 22.7 ns 25.1 ns std::map<std::string, int>::insert(value) (already present)/1024 54.5 ns 29.1 ns std::map<std::string, int>::insert(value) (already present)/8192 78.4 ns 30.4 ns std::map<std::string, int>::insert(value) (new value)/0 40.9 ns 39.0 ns std::map<std::string, int>::insert(value) (new value)/32 58.3 ns 47.2 ns std::map<std::string, int>::insert(value) (new value)/1024 120 ns 71.3 ns std::map<std::string, int>::insert(value) (new value)/8192 157 ns 129 ns std::map<std::string, int>::insert(hint, value) (good hint)/0 40.3 ns 40.7 ns std::map<std::string, int>::insert(hint, value) (good hint)/32 48.0 ns 30.0 ns std::map<std::string, int>::insert(hint, value) (good hint)/1024 107 ns 63.2 ns std::map<std::string, int>::insert(hint, value) (good hint)/8192 132 ns 107 ns std::map<std::string, int>::insert(hint, value) (bad hint)/0 27.0 ns 40.9 ns std::map<std::string, int>::insert(hint, value) (bad hint)/32 68.3 ns 58.4 ns std::map<std::string, int>::insert(hint, value) (bad hint)/1024 125 ns 82.0 ns std::map<std::string, int>::insert(hint, value) (bad hint)/8192 155 ns 150 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/0 404 ns 405 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/32 2004 ns 1805 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/1024 102820 ns 76102 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/8192 1144590 ns 949266 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/0 408 ns 404 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/32 1592 ns 1377 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/1024 74847 ns 53921 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/8192 828505 ns 698716 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/0 407 ns 407 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/32 1584 ns 1557 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/1024 47157 ns 47443 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/8192 623887 ns 628385 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/0 405 ns 403 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/32 1478 ns 1510 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/1024 47852 ns 47835 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/8192 605311 ns 606951 ns std::map<std::string, int>::erase(key) (existent)/0 129 ns 94.0 ns std::map<std::string, int>::erase(key) (existent)/32 110 ns 106 ns std::map<std::string, int>::erase(key) (existent)/1024 121 ns 128 ns std::map<std::string, int>::erase(key) (existent)/8192 165 ns 66.9 ns std::map<std::string, int>::erase(key) (non-existent)/0 0.269 ns 0.257 ns std::map<std::string, int>::erase(key) (non-existent)/32 21.9 ns 11.3 ns std::map<std::string, int>::erase(key) (non-existent)/1024 53.5 ns 25.4 ns std::map<std::string, int>::erase(key) (non-existent)/8192 67.3 ns 31.9 ns std::map<std::string, int>::erase(iterator)/0 46.3 ns 46.7 ns std::map<std::string, int>::erase(iterator)/32 44.4 ns 41.8 ns std::map<std::string, int>::erase(iterator)/1024 43.7 ns 46.4 ns std::map<std::string, int>::erase(iterator)/8192 45.2 ns 44.1 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/0 407 ns 407 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/32 876 ns 906 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/1024 20880 ns 20444 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/8192 252881 ns 241583 ns std::map<std::string, int>::clear()/0 407 ns 408 ns std::map<std::string, int>::clear()/32 1252 ns 1323 ns std::map<std::string, int>::clear()/1024 38488 ns 38017 ns std::map<std::string, int>::clear()/8192 416492 ns 428534 ns std::map<std::string, int>::find(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::find(key) (existent)/32 33.9 ns 15.3 ns std::map<std::string, int>::find(key) (existent)/1024 43.0 ns 25.5 ns std::map<std::string, int>::find(key) (existent)/8192 44.6 ns 29.3 ns std::map<std::string, int>::find(key) (non-existent)/0 0.259 ns 0.257 ns std::map<std::string, int>::find(key) (non-existent)/32 22.6 ns 11.4 ns std::map<std::string, int>::find(key) (non-existent)/1024 48.6 ns 25.1 ns std::map<std::string, int>::find(key) (non-existent)/8192 64.1 ns 31.1 ns std::map<std::string, int>::count(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::count(key) (existent)/32 32.2 ns 17.3 ns std::map<std::string, int>::count(key) (existent)/1024 42.4 ns 25.3 ns std::map<std::string, int>::count(key) (existent)/8192 44.4 ns 31.6 ns std::map<std::string, int>::count(key) (non-existent)/0 0.260 ns 0.259 ns std::map<std::string, int>::count(key) (non-existent)/32 22.9 ns 11.3 ns std::map<std::string, int>::count(key) (non-existent)/1024 49.8 ns 25.5 ns std::map<std::string, int>::count(key) (non-existent)/8192 66.3 ns 31.9 ns std::map<std::string, int>::contains(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::contains(key) (existent)/32 31.4 ns 18.0 ns std::map<std::string, int>::contains(key) (existent)/1024 44.3 ns 26.5 ns std::map<std::string, int>::contains(key) (existent)/8192 47.4 ns 30.2 ns std::map<std::string, int>::contains(key) (non-existent)/0 0.452 ns 0.441 ns std::map<std::string, int>::contains(key) (non-existent)/32 23.1 ns 11.5 ns std::map<std::string, int>::contains(key) (non-existent)/1024 46.2 ns 26.3 ns std::map<std::string, int>::contains(key) (non-existent)/8192 63.4 ns 31.4 ns std::map<std::string, int>::lower_bound(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::lower_bound(key) (existent)/32 17.2 ns 19.0 ns std::map<std::string, int>::lower_bound(key) (existent)/1024 27.1 ns 26.2 ns std::map<std::string, int>::lower_bound(key) (existent)/8192 34.0 ns 36.0 ns std::map<std::string, int>::lower_bound(key) (non-existent)/0 0.259 ns 0.257 ns std::map<std::string, int>::lower_bound(key) (non-existent)/32 11.6 ns 11.5 ns std::map<std::string, int>::lower_bound(key) (non-existent)/1024 24.8 ns 25.6 ns std::map<std::string, int>::lower_bound(key) (non-existent)/8192 31.7 ns 31.6 ns std::map<std::string, int>::upper_bound(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::upper_bound(key) (existent)/32 18.8 ns 19.7 ns std::map<std::string, int>::upper_bound(key) (existent)/1024 25.3 ns 27.7 ns std::map<std::string, int>::upper_bound(key) (existent)/8192 30.2 ns 29.9 ns std::map<std::string, int>::upper_bound(key) (non-existent)/0 0.260 ns 0.259 ns std::map<std::string, int>::upper_bound(key) (non-existent)/32 11.3 ns 12.0 ns std::map<std::string, int>::upper_bound(key) (non-existent)/1024 25.6 ns 25.9 ns std::map<std::string, int>::upper_bound(key) (non-existent)/8192 33.1 ns 34.2 ns std::map<std::string, int>::equal_range(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::equal_range(key) (existent)/32 33.5 ns 15.8 ns std::map<std::string, int>::equal_range(key) (existent)/1024 43.0 ns 25.1 ns std::map<std::string, int>::equal_range(key) (existent)/8192 54.1 ns 30.7 ns std::map<std::string, int>::equal_range(key) (non-existent)/0 0.265 ns 0.259 ns std::map<std::string, int>::equal_range(key) (non-existent)/32 22.1 ns 12.1 ns std::map<std::string, int>::equal_range(key) (non-existent)/1024 44.8 ns 24.4 ns std::map<std::string, int>::equal_range(key) (non-existent)/8192 62.2 ns 40.1 ns ``` Fixes #66577
2025-09-07 23:56:14 -08:00
auto __comp = __lazy_synth_three_way_comparator<value_compare, _Key, value_type>(value_comp());
2010-05-11 11:42:16 -08:00
while (__rt != nullptr) {
[libc++] Optimize most of the __tree search algorithms (#155245) This patch introduces a new comparator, namely `__lazy_synth_three_way`, which tries to provide an efficient three way comparator for known types and falls back to using the provided comparator if it doesn't know how to do that. Currently, an efficient three way comparison is only provided when using one of the `less` comparions object from the standard library and `std::string`. This will be extended in future patches. ``` ------------------------------------------------------------------------------------------------------------------------------ Benchmark old new ------------------------------------------------------------------------------------------------------------------------------ std::map<std::string, int>::ctor(const&)/0 12.6 ns 12.6 ns std::map<std::string, int>::ctor(const&)/32 858 ns 837 ns std::map<std::string, int>::ctor(const&)/1024 46700 ns 46739 ns std::map<std::string, int>::ctor(const&)/8192 458100 ns 449806 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/0 12.8 ns 12.7 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/32 1286 ns 1266 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/1024 93812 ns 84686 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/8192 1480346 ns 1385924 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/0 12.9 ns 12.8 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/32 1044 ns 1055 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/1024 63071 ns 62861 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/8192 595046 ns 590223 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/0 13.6 ns 13.6 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/32 880 ns 911 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/1024 48627 ns 47808 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/8192 458552 ns 454497 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/0 13.8 ns 13.6 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/32 864 ns 851 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/1024 49483 ns 49555 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/8192 456977 ns 457894 ns std::map<std::string, int>::operator=(const&) (into populated Container)/0 1.31 ns 1.31 ns std::map<std::string, int>::operator=(const&) (into populated Container)/32 425 ns 415 ns std::map<std::string, int>::operator=(const&) (into populated Container)/1024 14248 ns 14225 ns std::map<std::string, int>::operator=(const&) (into populated Container)/8192 136684 ns 133696 ns std::map<std::string, int>::insert(value) (already present)/0 21.5 ns 16.2 ns std::map<std::string, int>::insert(value) (already present)/32 22.7 ns 25.1 ns std::map<std::string, int>::insert(value) (already present)/1024 54.5 ns 29.1 ns std::map<std::string, int>::insert(value) (already present)/8192 78.4 ns 30.4 ns std::map<std::string, int>::insert(value) (new value)/0 40.9 ns 39.0 ns std::map<std::string, int>::insert(value) (new value)/32 58.3 ns 47.2 ns std::map<std::string, int>::insert(value) (new value)/1024 120 ns 71.3 ns std::map<std::string, int>::insert(value) (new value)/8192 157 ns 129 ns std::map<std::string, int>::insert(hint, value) (good hint)/0 40.3 ns 40.7 ns std::map<std::string, int>::insert(hint, value) (good hint)/32 48.0 ns 30.0 ns std::map<std::string, int>::insert(hint, value) (good hint)/1024 107 ns 63.2 ns std::map<std::string, int>::insert(hint, value) (good hint)/8192 132 ns 107 ns std::map<std::string, int>::insert(hint, value) (bad hint)/0 27.0 ns 40.9 ns std::map<std::string, int>::insert(hint, value) (bad hint)/32 68.3 ns 58.4 ns std::map<std::string, int>::insert(hint, value) (bad hint)/1024 125 ns 82.0 ns std::map<std::string, int>::insert(hint, value) (bad hint)/8192 155 ns 150 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/0 404 ns 405 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/32 2004 ns 1805 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/1024 102820 ns 76102 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/8192 1144590 ns 949266 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/0 408 ns 404 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/32 1592 ns 1377 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/1024 74847 ns 53921 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/8192 828505 ns 698716 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/0 407 ns 407 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/32 1584 ns 1557 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/1024 47157 ns 47443 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/8192 623887 ns 628385 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/0 405 ns 403 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/32 1478 ns 1510 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/1024 47852 ns 47835 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/8192 605311 ns 606951 ns std::map<std::string, int>::erase(key) (existent)/0 129 ns 94.0 ns std::map<std::string, int>::erase(key) (existent)/32 110 ns 106 ns std::map<std::string, int>::erase(key) (existent)/1024 121 ns 128 ns std::map<std::string, int>::erase(key) (existent)/8192 165 ns 66.9 ns std::map<std::string, int>::erase(key) (non-existent)/0 0.269 ns 0.257 ns std::map<std::string, int>::erase(key) (non-existent)/32 21.9 ns 11.3 ns std::map<std::string, int>::erase(key) (non-existent)/1024 53.5 ns 25.4 ns std::map<std::string, int>::erase(key) (non-existent)/8192 67.3 ns 31.9 ns std::map<std::string, int>::erase(iterator)/0 46.3 ns 46.7 ns std::map<std::string, int>::erase(iterator)/32 44.4 ns 41.8 ns std::map<std::string, int>::erase(iterator)/1024 43.7 ns 46.4 ns std::map<std::string, int>::erase(iterator)/8192 45.2 ns 44.1 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/0 407 ns 407 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/32 876 ns 906 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/1024 20880 ns 20444 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/8192 252881 ns 241583 ns std::map<std::string, int>::clear()/0 407 ns 408 ns std::map<std::string, int>::clear()/32 1252 ns 1323 ns std::map<std::string, int>::clear()/1024 38488 ns 38017 ns std::map<std::string, int>::clear()/8192 416492 ns 428534 ns std::map<std::string, int>::find(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::find(key) (existent)/32 33.9 ns 15.3 ns std::map<std::string, int>::find(key) (existent)/1024 43.0 ns 25.5 ns std::map<std::string, int>::find(key) (existent)/8192 44.6 ns 29.3 ns std::map<std::string, int>::find(key) (non-existent)/0 0.259 ns 0.257 ns std::map<std::string, int>::find(key) (non-existent)/32 22.6 ns 11.4 ns std::map<std::string, int>::find(key) (non-existent)/1024 48.6 ns 25.1 ns std::map<std::string, int>::find(key) (non-existent)/8192 64.1 ns 31.1 ns std::map<std::string, int>::count(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::count(key) (existent)/32 32.2 ns 17.3 ns std::map<std::string, int>::count(key) (existent)/1024 42.4 ns 25.3 ns std::map<std::string, int>::count(key) (existent)/8192 44.4 ns 31.6 ns std::map<std::string, int>::count(key) (non-existent)/0 0.260 ns 0.259 ns std::map<std::string, int>::count(key) (non-existent)/32 22.9 ns 11.3 ns std::map<std::string, int>::count(key) (non-existent)/1024 49.8 ns 25.5 ns std::map<std::string, int>::count(key) (non-existent)/8192 66.3 ns 31.9 ns std::map<std::string, int>::contains(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::contains(key) (existent)/32 31.4 ns 18.0 ns std::map<std::string, int>::contains(key) (existent)/1024 44.3 ns 26.5 ns std::map<std::string, int>::contains(key) (existent)/8192 47.4 ns 30.2 ns std::map<std::string, int>::contains(key) (non-existent)/0 0.452 ns 0.441 ns std::map<std::string, int>::contains(key) (non-existent)/32 23.1 ns 11.5 ns std::map<std::string, int>::contains(key) (non-existent)/1024 46.2 ns 26.3 ns std::map<std::string, int>::contains(key) (non-existent)/8192 63.4 ns 31.4 ns std::map<std::string, int>::lower_bound(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::lower_bound(key) (existent)/32 17.2 ns 19.0 ns std::map<std::string, int>::lower_bound(key) (existent)/1024 27.1 ns 26.2 ns std::map<std::string, int>::lower_bound(key) (existent)/8192 34.0 ns 36.0 ns std::map<std::string, int>::lower_bound(key) (non-existent)/0 0.259 ns 0.257 ns std::map<std::string, int>::lower_bound(key) (non-existent)/32 11.6 ns 11.5 ns std::map<std::string, int>::lower_bound(key) (non-existent)/1024 24.8 ns 25.6 ns std::map<std::string, int>::lower_bound(key) (non-existent)/8192 31.7 ns 31.6 ns std::map<std::string, int>::upper_bound(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::upper_bound(key) (existent)/32 18.8 ns 19.7 ns std::map<std::string, int>::upper_bound(key) (existent)/1024 25.3 ns 27.7 ns std::map<std::string, int>::upper_bound(key) (existent)/8192 30.2 ns 29.9 ns std::map<std::string, int>::upper_bound(key) (non-existent)/0 0.260 ns 0.259 ns std::map<std::string, int>::upper_bound(key) (non-existent)/32 11.3 ns 12.0 ns std::map<std::string, int>::upper_bound(key) (non-existent)/1024 25.6 ns 25.9 ns std::map<std::string, int>::upper_bound(key) (non-existent)/8192 33.1 ns 34.2 ns std::map<std::string, int>::equal_range(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::equal_range(key) (existent)/32 33.5 ns 15.8 ns std::map<std::string, int>::equal_range(key) (existent)/1024 43.0 ns 25.1 ns std::map<std::string, int>::equal_range(key) (existent)/8192 54.1 ns 30.7 ns std::map<std::string, int>::equal_range(key) (non-existent)/0 0.265 ns 0.259 ns std::map<std::string, int>::equal_range(key) (non-existent)/32 22.1 ns 12.1 ns std::map<std::string, int>::equal_range(key) (non-existent)/1024 44.8 ns 24.4 ns std::map<std::string, int>::equal_range(key) (non-existent)/8192 62.2 ns 40.1 ns ``` Fixes #66577
2025-09-07 23:56:14 -08:00
auto __comp_res = __comp(__k, __rt->__get_value());
if (__comp_res.__less()) {
__result = std::__static_fancy_pointer_cast<__end_node_pointer>(__rt);
__rt = std::__static_fancy_pointer_cast<__node_pointer>(__rt->__left_);
[libc++] Optimize most of the __tree search algorithms (#155245) This patch introduces a new comparator, namely `__lazy_synth_three_way`, which tries to provide an efficient three way comparator for known types and falls back to using the provided comparator if it doesn't know how to do that. Currently, an efficient three way comparison is only provided when using one of the `less` comparions object from the standard library and `std::string`. This will be extended in future patches. ``` ------------------------------------------------------------------------------------------------------------------------------ Benchmark old new ------------------------------------------------------------------------------------------------------------------------------ std::map<std::string, int>::ctor(const&)/0 12.6 ns 12.6 ns std::map<std::string, int>::ctor(const&)/32 858 ns 837 ns std::map<std::string, int>::ctor(const&)/1024 46700 ns 46739 ns std::map<std::string, int>::ctor(const&)/8192 458100 ns 449806 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/0 12.8 ns 12.7 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/32 1286 ns 1266 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/1024 93812 ns 84686 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/8192 1480346 ns 1385924 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/0 12.9 ns 12.8 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/32 1044 ns 1055 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/1024 63071 ns 62861 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/8192 595046 ns 590223 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/0 13.6 ns 13.6 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/32 880 ns 911 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/1024 48627 ns 47808 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/8192 458552 ns 454497 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/0 13.8 ns 13.6 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/32 864 ns 851 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/1024 49483 ns 49555 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/8192 456977 ns 457894 ns std::map<std::string, int>::operator=(const&) (into populated Container)/0 1.31 ns 1.31 ns std::map<std::string, int>::operator=(const&) (into populated Container)/32 425 ns 415 ns std::map<std::string, int>::operator=(const&) (into populated Container)/1024 14248 ns 14225 ns std::map<std::string, int>::operator=(const&) (into populated Container)/8192 136684 ns 133696 ns std::map<std::string, int>::insert(value) (already present)/0 21.5 ns 16.2 ns std::map<std::string, int>::insert(value) (already present)/32 22.7 ns 25.1 ns std::map<std::string, int>::insert(value) (already present)/1024 54.5 ns 29.1 ns std::map<std::string, int>::insert(value) (already present)/8192 78.4 ns 30.4 ns std::map<std::string, int>::insert(value) (new value)/0 40.9 ns 39.0 ns std::map<std::string, int>::insert(value) (new value)/32 58.3 ns 47.2 ns std::map<std::string, int>::insert(value) (new value)/1024 120 ns 71.3 ns std::map<std::string, int>::insert(value) (new value)/8192 157 ns 129 ns std::map<std::string, int>::insert(hint, value) (good hint)/0 40.3 ns 40.7 ns std::map<std::string, int>::insert(hint, value) (good hint)/32 48.0 ns 30.0 ns std::map<std::string, int>::insert(hint, value) (good hint)/1024 107 ns 63.2 ns std::map<std::string, int>::insert(hint, value) (good hint)/8192 132 ns 107 ns std::map<std::string, int>::insert(hint, value) (bad hint)/0 27.0 ns 40.9 ns std::map<std::string, int>::insert(hint, value) (bad hint)/32 68.3 ns 58.4 ns std::map<std::string, int>::insert(hint, value) (bad hint)/1024 125 ns 82.0 ns std::map<std::string, int>::insert(hint, value) (bad hint)/8192 155 ns 150 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/0 404 ns 405 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/32 2004 ns 1805 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/1024 102820 ns 76102 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/8192 1144590 ns 949266 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/0 408 ns 404 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/32 1592 ns 1377 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/1024 74847 ns 53921 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/8192 828505 ns 698716 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/0 407 ns 407 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/32 1584 ns 1557 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/1024 47157 ns 47443 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/8192 623887 ns 628385 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/0 405 ns 403 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/32 1478 ns 1510 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/1024 47852 ns 47835 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/8192 605311 ns 606951 ns std::map<std::string, int>::erase(key) (existent)/0 129 ns 94.0 ns std::map<std::string, int>::erase(key) (existent)/32 110 ns 106 ns std::map<std::string, int>::erase(key) (existent)/1024 121 ns 128 ns std::map<std::string, int>::erase(key) (existent)/8192 165 ns 66.9 ns std::map<std::string, int>::erase(key) (non-existent)/0 0.269 ns 0.257 ns std::map<std::string, int>::erase(key) (non-existent)/32 21.9 ns 11.3 ns std::map<std::string, int>::erase(key) (non-existent)/1024 53.5 ns 25.4 ns std::map<std::string, int>::erase(key) (non-existent)/8192 67.3 ns 31.9 ns std::map<std::string, int>::erase(iterator)/0 46.3 ns 46.7 ns std::map<std::string, int>::erase(iterator)/32 44.4 ns 41.8 ns std::map<std::string, int>::erase(iterator)/1024 43.7 ns 46.4 ns std::map<std::string, int>::erase(iterator)/8192 45.2 ns 44.1 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/0 407 ns 407 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/32 876 ns 906 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/1024 20880 ns 20444 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/8192 252881 ns 241583 ns std::map<std::string, int>::clear()/0 407 ns 408 ns std::map<std::string, int>::clear()/32 1252 ns 1323 ns std::map<std::string, int>::clear()/1024 38488 ns 38017 ns std::map<std::string, int>::clear()/8192 416492 ns 428534 ns std::map<std::string, int>::find(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::find(key) (existent)/32 33.9 ns 15.3 ns std::map<std::string, int>::find(key) (existent)/1024 43.0 ns 25.5 ns std::map<std::string, int>::find(key) (existent)/8192 44.6 ns 29.3 ns std::map<std::string, int>::find(key) (non-existent)/0 0.259 ns 0.257 ns std::map<std::string, int>::find(key) (non-existent)/32 22.6 ns 11.4 ns std::map<std::string, int>::find(key) (non-existent)/1024 48.6 ns 25.1 ns std::map<std::string, int>::find(key) (non-existent)/8192 64.1 ns 31.1 ns std::map<std::string, int>::count(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::count(key) (existent)/32 32.2 ns 17.3 ns std::map<std::string, int>::count(key) (existent)/1024 42.4 ns 25.3 ns std::map<std::string, int>::count(key) (existent)/8192 44.4 ns 31.6 ns std::map<std::string, int>::count(key) (non-existent)/0 0.260 ns 0.259 ns std::map<std::string, int>::count(key) (non-existent)/32 22.9 ns 11.3 ns std::map<std::string, int>::count(key) (non-existent)/1024 49.8 ns 25.5 ns std::map<std::string, int>::count(key) (non-existent)/8192 66.3 ns 31.9 ns std::map<std::string, int>::contains(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::contains(key) (existent)/32 31.4 ns 18.0 ns std::map<std::string, int>::contains(key) (existent)/1024 44.3 ns 26.5 ns std::map<std::string, int>::contains(key) (existent)/8192 47.4 ns 30.2 ns std::map<std::string, int>::contains(key) (non-existent)/0 0.452 ns 0.441 ns std::map<std::string, int>::contains(key) (non-existent)/32 23.1 ns 11.5 ns std::map<std::string, int>::contains(key) (non-existent)/1024 46.2 ns 26.3 ns std::map<std::string, int>::contains(key) (non-existent)/8192 63.4 ns 31.4 ns std::map<std::string, int>::lower_bound(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::lower_bound(key) (existent)/32 17.2 ns 19.0 ns std::map<std::string, int>::lower_bound(key) (existent)/1024 27.1 ns 26.2 ns std::map<std::string, int>::lower_bound(key) (existent)/8192 34.0 ns 36.0 ns std::map<std::string, int>::lower_bound(key) (non-existent)/0 0.259 ns 0.257 ns std::map<std::string, int>::lower_bound(key) (non-existent)/32 11.6 ns 11.5 ns std::map<std::string, int>::lower_bound(key) (non-existent)/1024 24.8 ns 25.6 ns std::map<std::string, int>::lower_bound(key) (non-existent)/8192 31.7 ns 31.6 ns std::map<std::string, int>::upper_bound(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::upper_bound(key) (existent)/32 18.8 ns 19.7 ns std::map<std::string, int>::upper_bound(key) (existent)/1024 25.3 ns 27.7 ns std::map<std::string, int>::upper_bound(key) (existent)/8192 30.2 ns 29.9 ns std::map<std::string, int>::upper_bound(key) (non-existent)/0 0.260 ns 0.259 ns std::map<std::string, int>::upper_bound(key) (non-existent)/32 11.3 ns 12.0 ns std::map<std::string, int>::upper_bound(key) (non-existent)/1024 25.6 ns 25.9 ns std::map<std::string, int>::upper_bound(key) (non-existent)/8192 33.1 ns 34.2 ns std::map<std::string, int>::equal_range(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::equal_range(key) (existent)/32 33.5 ns 15.8 ns std::map<std::string, int>::equal_range(key) (existent)/1024 43.0 ns 25.1 ns std::map<std::string, int>::equal_range(key) (existent)/8192 54.1 ns 30.7 ns std::map<std::string, int>::equal_range(key) (non-existent)/0 0.265 ns 0.259 ns std::map<std::string, int>::equal_range(key) (non-existent)/32 22.1 ns 12.1 ns std::map<std::string, int>::equal_range(key) (non-existent)/1024 44.8 ns 24.4 ns std::map<std::string, int>::equal_range(key) (non-existent)/8192 62.2 ns 40.1 ns ``` Fixes #66577
2025-09-07 23:56:14 -08:00
} else if (__comp_res.__greater())
__rt = std::__static_fancy_pointer_cast<__node_pointer>(__rt->__right_);
2010-05-11 11:42:16 -08:00
else
return std::distance(
__lower_bound_multi(__k,
std::__static_fancy_pointer_cast<__node_pointer>(__rt->__left_),
std::__static_fancy_pointer_cast<__end_node_pointer>(__rt)),
__upper_bound_multi(__k, std::__static_fancy_pointer_cast<__node_pointer>(__rt->__right_), __result));
2010-05-11 11:42:16 -08:00
}
return 0;
}
template <class _Tp, class _Compare, class _Allocator>
template <class _Key>
_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::iterator
__tree<_Tp, _Compare, _Allocator>::__lower_bound_multi(
[libc++] Optimize {set,map}::{lower,upper}_bound (#161366) Apple M4: ``` Benchmark Baseline Candidate Difference % Difference --------------------------------------------------------------------------------------------- ---------- ----------- ------------ -------------- std::map<int,_int>::lower_bound(key)_(existent)/0 0.01 0.01 -0.00 -25.78 std::map<int,_int>::lower_bound(key)_(existent)/1024 7.94 4.28 -3.66 -46.09 std::map<int,_int>::lower_bound(key)_(existent)/32 2.73 1.69 -1.03 -37.89 std::map<int,_int>::lower_bound(key)_(existent)/8192 11.63 5.52 -6.11 -52.55 std::map<int,_int>::lower_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -1.35 std::map<int,_int>::lower_bound(key)_(non-existent)/1024 17.21 7.63 -9.58 -55.67 std::map<int,_int>::lower_bound(key)_(non-existent)/32 4.71 3.26 -1.45 -30.71 std::map<int,_int>::lower_bound(key)_(non-existent)/8192 26.82 10.58 -16.24 -60.55 std::map<int,_int>::upper_bound(key)_(existent)/0 0.01 0.01 0.00 20.62 std::map<int,_int>::upper_bound(key)_(existent)/1024 7.93 3.61 -4.32 -54.49 std::map<int,_int>::upper_bound(key)_(existent)/32 2.83 1.98 -0.85 -30.01 std::map<int,_int>::upper_bound(key)_(existent)/8192 11.69 5.72 -5.97 -51.06 std::map<int,_int>::upper_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -1.36 std::map<int,_int>::upper_bound(key)_(non-existent)/1024 17.21 8.00 -9.21 -53.53 std::map<int,_int>::upper_bound(key)_(non-existent)/32 4.70 2.93 -1.78 -37.76 std::map<int,_int>::upper_bound(key)_(non-existent)/8192 26.54 11.18 -15.36 -57.89 std::map<std::string,_int>::lower_bound(key)_(existent)/0 0.04 0.04 -0.00 -3.26 std::map<std::string,_int>::lower_bound(key)_(existent)/1024 27.46 26.25 -1.22 -4.43 std::map<std::string,_int>::lower_bound(key)_(existent)/32 19.17 15.71 -3.46 -18.07 std::map<std::string,_int>::lower_bound(key)_(existent)/8192 35.33 35.03 -0.30 -0.84 std::map<std::string,_int>::lower_bound(key)_(non-existent)/0 0.27 0.27 -0.00 -1.45 std::map<std::string,_int>::lower_bound(key)_(non-existent)/1024 24.88 24.17 -0.70 -2.83 std::map<std::string,_int>::lower_bound(key)_(non-existent)/32 11.67 11.63 -0.04 -0.32 std::map<std::string,_int>::lower_bound(key)_(non-existent)/8192 31.81 32.33 0.52 1.64 std::map<std::string,_int>::upper_bound(key)_(existent)/0 0.04 0.04 -0.00 -2.22 std::map<std::string,_int>::upper_bound(key)_(existent)/1024 29.91 26.51 -3.40 -11.38 std::map<std::string,_int>::upper_bound(key)_(existent)/32 19.69 17.74 -1.95 -9.92 std::map<std::string,_int>::upper_bound(key)_(existent)/8192 32.55 35.24 2.69 8.25 std::map<std::string,_int>::upper_bound(key)_(non-existent)/0 0.27 0.27 -0.00 -1.74 std::map<std::string,_int>::upper_bound(key)_(non-existent)/1024 23.87 26.77 2.91 12.18 std::map<std::string,_int>::upper_bound(key)_(non-existent)/32 11.44 11.81 0.37 3.24 std::map<std::string,_int>::upper_bound(key)_(non-existent)/8192 33.02 32.59 -0.43 -1.29 std::set<int>::lower_bound(key)_(existent)/0 0.01 0.01 0.00 0.48 std::set<int>::lower_bound(key)_(existent)/1024 7.83 4.21 -3.62 -46.23 std::set<int>::lower_bound(key)_(existent)/32 2.74 1.68 -1.06 -38.81 std::set<int>::lower_bound(key)_(existent)/8192 22.75 11.12 -11.63 -51.12 std::set<int>::lower_bound(key)_(non-existent)/0 0.28 0.27 -0.01 -3.52 std::set<int>::lower_bound(key)_(non-existent)/1024 17.15 8.40 -8.75 -51.03 std::set<int>::lower_bound(key)_(non-existent)/32 4.63 2.50 -2.13 -46.03 std::set<int>::lower_bound(key)_(non-existent)/8192 28.88 11.05 -17.82 -61.72 std::set<int>::upper_bound(key)_(existent)/0 0.01 0.01 -0.00 -7.79 std::set<int>::upper_bound(key)_(existent)/1024 7.80 3.63 -4.16 -53.42 std::set<int>::upper_bound(key)_(existent)/32 2.81 1.90 -0.91 -32.44 std::set<int>::upper_bound(key)_(existent)/8192 21.93 11.35 -10.58 -48.26 std::set<int>::upper_bound(key)_(non-existent)/0 0.28 0.27 -0.01 -3.81 std::set<int>::upper_bound(key)_(non-existent)/1024 16.76 7.38 -9.38 -55.98 std::set<int>::upper_bound(key)_(non-existent)/32 4.58 3.10 -1.48 -32.31 std::set<int>::upper_bound(key)_(non-existent)/8192 26.95 10.70 -16.25 -60.29 std::set<std::string>::lower_bound(key)_(existent)/0 0.04 0.04 0.00 0.02 std::set<std::string>::lower_bound(key)_(existent)/1024 28.08 27.04 -1.04 -3.71 std::set<std::string>::lower_bound(key)_(existent)/32 17.53 16.94 -0.58 -3.34 std::set<std::string>::lower_bound(key)_(existent)/8192 32.79 33.28 0.49 1.49 std::set<std::string>::lower_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -0.06 std::set<std::string>::lower_bound(key)_(non-existent)/1024 25.23 24.38 -0.85 -3.38 std::set<std::string>::lower_bound(key)_(non-existent)/32 11.45 11.68 0.24 2.07 std::set<std::string>::lower_bound(key)_(non-existent)/8192 32.30 36.80 4.50 13.95 std::set<std::string>::upper_bound(key)_(existent)/0 0.04 0.04 -0.00 -0.14 std::set<std::string>::upper_bound(key)_(existent)/1024 26.71 26.37 -0.34 -1.27 std::set<std::string>::upper_bound(key)_(existent)/32 20.07 19.06 -1.02 -5.06 std::set<std::string>::upper_bound(key)_(existent)/8192 36.69 35.50 -1.19 -3.25 std::set<std::string>::upper_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -0.16 std::set<std::string>::upper_bound(key)_(non-existent)/1024 24.48 24.90 0.42 1.73 std::set<std::string>::upper_bound(key)_(non-existent)/32 11.68 11.77 0.09 0.77 std::set<std::string>::upper_bound(key)_(non-existent)/8192 33.16 34.12 0.96 2.89 ```
2025-10-08 22:45:03 -08:00
const _Key& __v, __node_pointer __root, __end_node_pointer __result) {
2010-05-11 11:42:16 -08:00
while (__root != nullptr) {
if (!value_comp()(__root->__get_value(), __v)) {
__result = std::__static_fancy_pointer_cast<__end_node_pointer>(__root);
__root = std::__static_fancy_pointer_cast<__node_pointer>(__root->__left_);
2010-05-11 11:42:16 -08:00
} else
__root = std::__static_fancy_pointer_cast<__node_pointer>(__root->__right_);
2010-05-11 11:42:16 -08:00
}
return iterator(__result);
}
template <class _Tp, class _Compare, class _Allocator>
template <class _Key>
_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::const_iterator
__tree<_Tp, _Compare, _Allocator>::__lower_bound_multi(
const _Key& __v, __node_pointer __root, __end_node_pointer __result) const {
2010-05-11 11:42:16 -08:00
while (__root != nullptr) {
if (!value_comp()(__root->__get_value(), __v)) {
__result = std::__static_fancy_pointer_cast<__end_node_pointer>(__root);
__root = std::__static_fancy_pointer_cast<__node_pointer>(__root->__left_);
2010-05-11 11:42:16 -08:00
} else
__root = std::__static_fancy_pointer_cast<__node_pointer>(__root->__right_);
2010-05-11 11:42:16 -08:00
}
return const_iterator(__result);
}
template <class _Tp, class _Compare, class _Allocator>
template <class _Key>
_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::iterator
__tree<_Tp, _Compare, _Allocator>::__upper_bound_multi(
[libc++] Optimize {set,map}::{lower,upper}_bound (#161366) Apple M4: ``` Benchmark Baseline Candidate Difference % Difference --------------------------------------------------------------------------------------------- ---------- ----------- ------------ -------------- std::map<int,_int>::lower_bound(key)_(existent)/0 0.01 0.01 -0.00 -25.78 std::map<int,_int>::lower_bound(key)_(existent)/1024 7.94 4.28 -3.66 -46.09 std::map<int,_int>::lower_bound(key)_(existent)/32 2.73 1.69 -1.03 -37.89 std::map<int,_int>::lower_bound(key)_(existent)/8192 11.63 5.52 -6.11 -52.55 std::map<int,_int>::lower_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -1.35 std::map<int,_int>::lower_bound(key)_(non-existent)/1024 17.21 7.63 -9.58 -55.67 std::map<int,_int>::lower_bound(key)_(non-existent)/32 4.71 3.26 -1.45 -30.71 std::map<int,_int>::lower_bound(key)_(non-existent)/8192 26.82 10.58 -16.24 -60.55 std::map<int,_int>::upper_bound(key)_(existent)/0 0.01 0.01 0.00 20.62 std::map<int,_int>::upper_bound(key)_(existent)/1024 7.93 3.61 -4.32 -54.49 std::map<int,_int>::upper_bound(key)_(existent)/32 2.83 1.98 -0.85 -30.01 std::map<int,_int>::upper_bound(key)_(existent)/8192 11.69 5.72 -5.97 -51.06 std::map<int,_int>::upper_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -1.36 std::map<int,_int>::upper_bound(key)_(non-existent)/1024 17.21 8.00 -9.21 -53.53 std::map<int,_int>::upper_bound(key)_(non-existent)/32 4.70 2.93 -1.78 -37.76 std::map<int,_int>::upper_bound(key)_(non-existent)/8192 26.54 11.18 -15.36 -57.89 std::map<std::string,_int>::lower_bound(key)_(existent)/0 0.04 0.04 -0.00 -3.26 std::map<std::string,_int>::lower_bound(key)_(existent)/1024 27.46 26.25 -1.22 -4.43 std::map<std::string,_int>::lower_bound(key)_(existent)/32 19.17 15.71 -3.46 -18.07 std::map<std::string,_int>::lower_bound(key)_(existent)/8192 35.33 35.03 -0.30 -0.84 std::map<std::string,_int>::lower_bound(key)_(non-existent)/0 0.27 0.27 -0.00 -1.45 std::map<std::string,_int>::lower_bound(key)_(non-existent)/1024 24.88 24.17 -0.70 -2.83 std::map<std::string,_int>::lower_bound(key)_(non-existent)/32 11.67 11.63 -0.04 -0.32 std::map<std::string,_int>::lower_bound(key)_(non-existent)/8192 31.81 32.33 0.52 1.64 std::map<std::string,_int>::upper_bound(key)_(existent)/0 0.04 0.04 -0.00 -2.22 std::map<std::string,_int>::upper_bound(key)_(existent)/1024 29.91 26.51 -3.40 -11.38 std::map<std::string,_int>::upper_bound(key)_(existent)/32 19.69 17.74 -1.95 -9.92 std::map<std::string,_int>::upper_bound(key)_(existent)/8192 32.55 35.24 2.69 8.25 std::map<std::string,_int>::upper_bound(key)_(non-existent)/0 0.27 0.27 -0.00 -1.74 std::map<std::string,_int>::upper_bound(key)_(non-existent)/1024 23.87 26.77 2.91 12.18 std::map<std::string,_int>::upper_bound(key)_(non-existent)/32 11.44 11.81 0.37 3.24 std::map<std::string,_int>::upper_bound(key)_(non-existent)/8192 33.02 32.59 -0.43 -1.29 std::set<int>::lower_bound(key)_(existent)/0 0.01 0.01 0.00 0.48 std::set<int>::lower_bound(key)_(existent)/1024 7.83 4.21 -3.62 -46.23 std::set<int>::lower_bound(key)_(existent)/32 2.74 1.68 -1.06 -38.81 std::set<int>::lower_bound(key)_(existent)/8192 22.75 11.12 -11.63 -51.12 std::set<int>::lower_bound(key)_(non-existent)/0 0.28 0.27 -0.01 -3.52 std::set<int>::lower_bound(key)_(non-existent)/1024 17.15 8.40 -8.75 -51.03 std::set<int>::lower_bound(key)_(non-existent)/32 4.63 2.50 -2.13 -46.03 std::set<int>::lower_bound(key)_(non-existent)/8192 28.88 11.05 -17.82 -61.72 std::set<int>::upper_bound(key)_(existent)/0 0.01 0.01 -0.00 -7.79 std::set<int>::upper_bound(key)_(existent)/1024 7.80 3.63 -4.16 -53.42 std::set<int>::upper_bound(key)_(existent)/32 2.81 1.90 -0.91 -32.44 std::set<int>::upper_bound(key)_(existent)/8192 21.93 11.35 -10.58 -48.26 std::set<int>::upper_bound(key)_(non-existent)/0 0.28 0.27 -0.01 -3.81 std::set<int>::upper_bound(key)_(non-existent)/1024 16.76 7.38 -9.38 -55.98 std::set<int>::upper_bound(key)_(non-existent)/32 4.58 3.10 -1.48 -32.31 std::set<int>::upper_bound(key)_(non-existent)/8192 26.95 10.70 -16.25 -60.29 std::set<std::string>::lower_bound(key)_(existent)/0 0.04 0.04 0.00 0.02 std::set<std::string>::lower_bound(key)_(existent)/1024 28.08 27.04 -1.04 -3.71 std::set<std::string>::lower_bound(key)_(existent)/32 17.53 16.94 -0.58 -3.34 std::set<std::string>::lower_bound(key)_(existent)/8192 32.79 33.28 0.49 1.49 std::set<std::string>::lower_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -0.06 std::set<std::string>::lower_bound(key)_(non-existent)/1024 25.23 24.38 -0.85 -3.38 std::set<std::string>::lower_bound(key)_(non-existent)/32 11.45 11.68 0.24 2.07 std::set<std::string>::lower_bound(key)_(non-existent)/8192 32.30 36.80 4.50 13.95 std::set<std::string>::upper_bound(key)_(existent)/0 0.04 0.04 -0.00 -0.14 std::set<std::string>::upper_bound(key)_(existent)/1024 26.71 26.37 -0.34 -1.27 std::set<std::string>::upper_bound(key)_(existent)/32 20.07 19.06 -1.02 -5.06 std::set<std::string>::upper_bound(key)_(existent)/8192 36.69 35.50 -1.19 -3.25 std::set<std::string>::upper_bound(key)_(non-existent)/0 0.28 0.28 -0.00 -0.16 std::set<std::string>::upper_bound(key)_(non-existent)/1024 24.48 24.90 0.42 1.73 std::set<std::string>::upper_bound(key)_(non-existent)/32 11.68 11.77 0.09 0.77 std::set<std::string>::upper_bound(key)_(non-existent)/8192 33.16 34.12 0.96 2.89 ```
2025-10-08 22:45:03 -08:00
const _Key& __v, __node_pointer __root, __end_node_pointer __result) {
2010-05-11 11:42:16 -08:00
while (__root != nullptr) {
if (value_comp()(__v, __root->__get_value())) {
__result = std::__static_fancy_pointer_cast<__end_node_pointer>(__root);
__root = std::__static_fancy_pointer_cast<__node_pointer>(__root->__left_);
2010-05-11 11:42:16 -08:00
} else
__root = std::__static_fancy_pointer_cast<__node_pointer>(__root->__right_);
2010-05-11 11:42:16 -08:00
}
return iterator(__result);
}
template <class _Tp, class _Compare, class _Allocator>
template <class _Key>
_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::const_iterator
__tree<_Tp, _Compare, _Allocator>::__upper_bound_multi(
const _Key& __v, __node_pointer __root, __end_node_pointer __result) const {
2010-05-11 11:42:16 -08:00
while (__root != nullptr) {
if (value_comp()(__v, __root->__get_value())) {
__result = std::__static_fancy_pointer_cast<__end_node_pointer>(__root);
__root = std::__static_fancy_pointer_cast<__node_pointer>(__root->__left_);
2010-05-11 11:42:16 -08:00
} else
__root = std::__static_fancy_pointer_cast<__node_pointer>(__root->__right_);
2010-05-11 11:42:16 -08:00
}
return const_iterator(__result);
}
template <class _Tp, class _Compare, class _Allocator>
template <class _Key>
_LIBCPP_CONSTEXPR_SINCE_CXX26
pair<typename __tree<_Tp, _Compare, _Allocator>::iterator, typename __tree<_Tp, _Compare, _Allocator>::iterator>
__tree<_Tp, _Compare, _Allocator>::__equal_range_unique(const _Key& __k) {
using _Pp = pair<iterator, iterator>;
__end_node_pointer __result = __end_node();
__node_pointer __rt = __root();
[libc++] Optimize most of the __tree search algorithms (#155245) This patch introduces a new comparator, namely `__lazy_synth_three_way`, which tries to provide an efficient three way comparator for known types and falls back to using the provided comparator if it doesn't know how to do that. Currently, an efficient three way comparison is only provided when using one of the `less` comparions object from the standard library and `std::string`. This will be extended in future patches. ``` ------------------------------------------------------------------------------------------------------------------------------ Benchmark old new ------------------------------------------------------------------------------------------------------------------------------ std::map<std::string, int>::ctor(const&)/0 12.6 ns 12.6 ns std::map<std::string, int>::ctor(const&)/32 858 ns 837 ns std::map<std::string, int>::ctor(const&)/1024 46700 ns 46739 ns std::map<std::string, int>::ctor(const&)/8192 458100 ns 449806 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/0 12.8 ns 12.7 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/32 1286 ns 1266 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/1024 93812 ns 84686 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/8192 1480346 ns 1385924 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/0 12.9 ns 12.8 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/32 1044 ns 1055 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/1024 63071 ns 62861 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/8192 595046 ns 590223 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/0 13.6 ns 13.6 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/32 880 ns 911 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/1024 48627 ns 47808 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/8192 458552 ns 454497 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/0 13.8 ns 13.6 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/32 864 ns 851 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/1024 49483 ns 49555 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/8192 456977 ns 457894 ns std::map<std::string, int>::operator=(const&) (into populated Container)/0 1.31 ns 1.31 ns std::map<std::string, int>::operator=(const&) (into populated Container)/32 425 ns 415 ns std::map<std::string, int>::operator=(const&) (into populated Container)/1024 14248 ns 14225 ns std::map<std::string, int>::operator=(const&) (into populated Container)/8192 136684 ns 133696 ns std::map<std::string, int>::insert(value) (already present)/0 21.5 ns 16.2 ns std::map<std::string, int>::insert(value) (already present)/32 22.7 ns 25.1 ns std::map<std::string, int>::insert(value) (already present)/1024 54.5 ns 29.1 ns std::map<std::string, int>::insert(value) (already present)/8192 78.4 ns 30.4 ns std::map<std::string, int>::insert(value) (new value)/0 40.9 ns 39.0 ns std::map<std::string, int>::insert(value) (new value)/32 58.3 ns 47.2 ns std::map<std::string, int>::insert(value) (new value)/1024 120 ns 71.3 ns std::map<std::string, int>::insert(value) (new value)/8192 157 ns 129 ns std::map<std::string, int>::insert(hint, value) (good hint)/0 40.3 ns 40.7 ns std::map<std::string, int>::insert(hint, value) (good hint)/32 48.0 ns 30.0 ns std::map<std::string, int>::insert(hint, value) (good hint)/1024 107 ns 63.2 ns std::map<std::string, int>::insert(hint, value) (good hint)/8192 132 ns 107 ns std::map<std::string, int>::insert(hint, value) (bad hint)/0 27.0 ns 40.9 ns std::map<std::string, int>::insert(hint, value) (bad hint)/32 68.3 ns 58.4 ns std::map<std::string, int>::insert(hint, value) (bad hint)/1024 125 ns 82.0 ns std::map<std::string, int>::insert(hint, value) (bad hint)/8192 155 ns 150 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/0 404 ns 405 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/32 2004 ns 1805 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/1024 102820 ns 76102 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/8192 1144590 ns 949266 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/0 408 ns 404 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/32 1592 ns 1377 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/1024 74847 ns 53921 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/8192 828505 ns 698716 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/0 407 ns 407 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/32 1584 ns 1557 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/1024 47157 ns 47443 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/8192 623887 ns 628385 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/0 405 ns 403 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/32 1478 ns 1510 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/1024 47852 ns 47835 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/8192 605311 ns 606951 ns std::map<std::string, int>::erase(key) (existent)/0 129 ns 94.0 ns std::map<std::string, int>::erase(key) (existent)/32 110 ns 106 ns std::map<std::string, int>::erase(key) (existent)/1024 121 ns 128 ns std::map<std::string, int>::erase(key) (existent)/8192 165 ns 66.9 ns std::map<std::string, int>::erase(key) (non-existent)/0 0.269 ns 0.257 ns std::map<std::string, int>::erase(key) (non-existent)/32 21.9 ns 11.3 ns std::map<std::string, int>::erase(key) (non-existent)/1024 53.5 ns 25.4 ns std::map<std::string, int>::erase(key) (non-existent)/8192 67.3 ns 31.9 ns std::map<std::string, int>::erase(iterator)/0 46.3 ns 46.7 ns std::map<std::string, int>::erase(iterator)/32 44.4 ns 41.8 ns std::map<std::string, int>::erase(iterator)/1024 43.7 ns 46.4 ns std::map<std::string, int>::erase(iterator)/8192 45.2 ns 44.1 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/0 407 ns 407 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/32 876 ns 906 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/1024 20880 ns 20444 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/8192 252881 ns 241583 ns std::map<std::string, int>::clear()/0 407 ns 408 ns std::map<std::string, int>::clear()/32 1252 ns 1323 ns std::map<std::string, int>::clear()/1024 38488 ns 38017 ns std::map<std::string, int>::clear()/8192 416492 ns 428534 ns std::map<std::string, int>::find(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::find(key) (existent)/32 33.9 ns 15.3 ns std::map<std::string, int>::find(key) (existent)/1024 43.0 ns 25.5 ns std::map<std::string, int>::find(key) (existent)/8192 44.6 ns 29.3 ns std::map<std::string, int>::find(key) (non-existent)/0 0.259 ns 0.257 ns std::map<std::string, int>::find(key) (non-existent)/32 22.6 ns 11.4 ns std::map<std::string, int>::find(key) (non-existent)/1024 48.6 ns 25.1 ns std::map<std::string, int>::find(key) (non-existent)/8192 64.1 ns 31.1 ns std::map<std::string, int>::count(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::count(key) (existent)/32 32.2 ns 17.3 ns std::map<std::string, int>::count(key) (existent)/1024 42.4 ns 25.3 ns std::map<std::string, int>::count(key) (existent)/8192 44.4 ns 31.6 ns std::map<std::string, int>::count(key) (non-existent)/0 0.260 ns 0.259 ns std::map<std::string, int>::count(key) (non-existent)/32 22.9 ns 11.3 ns std::map<std::string, int>::count(key) (non-existent)/1024 49.8 ns 25.5 ns std::map<std::string, int>::count(key) (non-existent)/8192 66.3 ns 31.9 ns std::map<std::string, int>::contains(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::contains(key) (existent)/32 31.4 ns 18.0 ns std::map<std::string, int>::contains(key) (existent)/1024 44.3 ns 26.5 ns std::map<std::string, int>::contains(key) (existent)/8192 47.4 ns 30.2 ns std::map<std::string, int>::contains(key) (non-existent)/0 0.452 ns 0.441 ns std::map<std::string, int>::contains(key) (non-existent)/32 23.1 ns 11.5 ns std::map<std::string, int>::contains(key) (non-existent)/1024 46.2 ns 26.3 ns std::map<std::string, int>::contains(key) (non-existent)/8192 63.4 ns 31.4 ns std::map<std::string, int>::lower_bound(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::lower_bound(key) (existent)/32 17.2 ns 19.0 ns std::map<std::string, int>::lower_bound(key) (existent)/1024 27.1 ns 26.2 ns std::map<std::string, int>::lower_bound(key) (existent)/8192 34.0 ns 36.0 ns std::map<std::string, int>::lower_bound(key) (non-existent)/0 0.259 ns 0.257 ns std::map<std::string, int>::lower_bound(key) (non-existent)/32 11.6 ns 11.5 ns std::map<std::string, int>::lower_bound(key) (non-existent)/1024 24.8 ns 25.6 ns std::map<std::string, int>::lower_bound(key) (non-existent)/8192 31.7 ns 31.6 ns std::map<std::string, int>::upper_bound(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::upper_bound(key) (existent)/32 18.8 ns 19.7 ns std::map<std::string, int>::upper_bound(key) (existent)/1024 25.3 ns 27.7 ns std::map<std::string, int>::upper_bound(key) (existent)/8192 30.2 ns 29.9 ns std::map<std::string, int>::upper_bound(key) (non-existent)/0 0.260 ns 0.259 ns std::map<std::string, int>::upper_bound(key) (non-existent)/32 11.3 ns 12.0 ns std::map<std::string, int>::upper_bound(key) (non-existent)/1024 25.6 ns 25.9 ns std::map<std::string, int>::upper_bound(key) (non-existent)/8192 33.1 ns 34.2 ns std::map<std::string, int>::equal_range(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::equal_range(key) (existent)/32 33.5 ns 15.8 ns std::map<std::string, int>::equal_range(key) (existent)/1024 43.0 ns 25.1 ns std::map<std::string, int>::equal_range(key) (existent)/8192 54.1 ns 30.7 ns std::map<std::string, int>::equal_range(key) (non-existent)/0 0.265 ns 0.259 ns std::map<std::string, int>::equal_range(key) (non-existent)/32 22.1 ns 12.1 ns std::map<std::string, int>::equal_range(key) (non-existent)/1024 44.8 ns 24.4 ns std::map<std::string, int>::equal_range(key) (non-existent)/8192 62.2 ns 40.1 ns ``` Fixes #66577
2025-09-07 23:56:14 -08:00
auto __comp = __lazy_synth_three_way_comparator<value_compare, _Key, value_type>(value_comp());
2010-05-11 11:42:16 -08:00
while (__rt != nullptr) {
[libc++] Optimize most of the __tree search algorithms (#155245) This patch introduces a new comparator, namely `__lazy_synth_three_way`, which tries to provide an efficient three way comparator for known types and falls back to using the provided comparator if it doesn't know how to do that. Currently, an efficient three way comparison is only provided when using one of the `less` comparions object from the standard library and `std::string`. This will be extended in future patches. ``` ------------------------------------------------------------------------------------------------------------------------------ Benchmark old new ------------------------------------------------------------------------------------------------------------------------------ std::map<std::string, int>::ctor(const&)/0 12.6 ns 12.6 ns std::map<std::string, int>::ctor(const&)/32 858 ns 837 ns std::map<std::string, int>::ctor(const&)/1024 46700 ns 46739 ns std::map<std::string, int>::ctor(const&)/8192 458100 ns 449806 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/0 12.8 ns 12.7 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/32 1286 ns 1266 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/1024 93812 ns 84686 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/8192 1480346 ns 1385924 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/0 12.9 ns 12.8 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/32 1044 ns 1055 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/1024 63071 ns 62861 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/8192 595046 ns 590223 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/0 13.6 ns 13.6 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/32 880 ns 911 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/1024 48627 ns 47808 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/8192 458552 ns 454497 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/0 13.8 ns 13.6 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/32 864 ns 851 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/1024 49483 ns 49555 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/8192 456977 ns 457894 ns std::map<std::string, int>::operator=(const&) (into populated Container)/0 1.31 ns 1.31 ns std::map<std::string, int>::operator=(const&) (into populated Container)/32 425 ns 415 ns std::map<std::string, int>::operator=(const&) (into populated Container)/1024 14248 ns 14225 ns std::map<std::string, int>::operator=(const&) (into populated Container)/8192 136684 ns 133696 ns std::map<std::string, int>::insert(value) (already present)/0 21.5 ns 16.2 ns std::map<std::string, int>::insert(value) (already present)/32 22.7 ns 25.1 ns std::map<std::string, int>::insert(value) (already present)/1024 54.5 ns 29.1 ns std::map<std::string, int>::insert(value) (already present)/8192 78.4 ns 30.4 ns std::map<std::string, int>::insert(value) (new value)/0 40.9 ns 39.0 ns std::map<std::string, int>::insert(value) (new value)/32 58.3 ns 47.2 ns std::map<std::string, int>::insert(value) (new value)/1024 120 ns 71.3 ns std::map<std::string, int>::insert(value) (new value)/8192 157 ns 129 ns std::map<std::string, int>::insert(hint, value) (good hint)/0 40.3 ns 40.7 ns std::map<std::string, int>::insert(hint, value) (good hint)/32 48.0 ns 30.0 ns std::map<std::string, int>::insert(hint, value) (good hint)/1024 107 ns 63.2 ns std::map<std::string, int>::insert(hint, value) (good hint)/8192 132 ns 107 ns std::map<std::string, int>::insert(hint, value) (bad hint)/0 27.0 ns 40.9 ns std::map<std::string, int>::insert(hint, value) (bad hint)/32 68.3 ns 58.4 ns std::map<std::string, int>::insert(hint, value) (bad hint)/1024 125 ns 82.0 ns std::map<std::string, int>::insert(hint, value) (bad hint)/8192 155 ns 150 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/0 404 ns 405 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/32 2004 ns 1805 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/1024 102820 ns 76102 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/8192 1144590 ns 949266 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/0 408 ns 404 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/32 1592 ns 1377 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/1024 74847 ns 53921 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/8192 828505 ns 698716 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/0 407 ns 407 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/32 1584 ns 1557 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/1024 47157 ns 47443 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/8192 623887 ns 628385 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/0 405 ns 403 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/32 1478 ns 1510 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/1024 47852 ns 47835 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/8192 605311 ns 606951 ns std::map<std::string, int>::erase(key) (existent)/0 129 ns 94.0 ns std::map<std::string, int>::erase(key) (existent)/32 110 ns 106 ns std::map<std::string, int>::erase(key) (existent)/1024 121 ns 128 ns std::map<std::string, int>::erase(key) (existent)/8192 165 ns 66.9 ns std::map<std::string, int>::erase(key) (non-existent)/0 0.269 ns 0.257 ns std::map<std::string, int>::erase(key) (non-existent)/32 21.9 ns 11.3 ns std::map<std::string, int>::erase(key) (non-existent)/1024 53.5 ns 25.4 ns std::map<std::string, int>::erase(key) (non-existent)/8192 67.3 ns 31.9 ns std::map<std::string, int>::erase(iterator)/0 46.3 ns 46.7 ns std::map<std::string, int>::erase(iterator)/32 44.4 ns 41.8 ns std::map<std::string, int>::erase(iterator)/1024 43.7 ns 46.4 ns std::map<std::string, int>::erase(iterator)/8192 45.2 ns 44.1 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/0 407 ns 407 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/32 876 ns 906 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/1024 20880 ns 20444 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/8192 252881 ns 241583 ns std::map<std::string, int>::clear()/0 407 ns 408 ns std::map<std::string, int>::clear()/32 1252 ns 1323 ns std::map<std::string, int>::clear()/1024 38488 ns 38017 ns std::map<std::string, int>::clear()/8192 416492 ns 428534 ns std::map<std::string, int>::find(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::find(key) (existent)/32 33.9 ns 15.3 ns std::map<std::string, int>::find(key) (existent)/1024 43.0 ns 25.5 ns std::map<std::string, int>::find(key) (existent)/8192 44.6 ns 29.3 ns std::map<std::string, int>::find(key) (non-existent)/0 0.259 ns 0.257 ns std::map<std::string, int>::find(key) (non-existent)/32 22.6 ns 11.4 ns std::map<std::string, int>::find(key) (non-existent)/1024 48.6 ns 25.1 ns std::map<std::string, int>::find(key) (non-existent)/8192 64.1 ns 31.1 ns std::map<std::string, int>::count(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::count(key) (existent)/32 32.2 ns 17.3 ns std::map<std::string, int>::count(key) (existent)/1024 42.4 ns 25.3 ns std::map<std::string, int>::count(key) (existent)/8192 44.4 ns 31.6 ns std::map<std::string, int>::count(key) (non-existent)/0 0.260 ns 0.259 ns std::map<std::string, int>::count(key) (non-existent)/32 22.9 ns 11.3 ns std::map<std::string, int>::count(key) (non-existent)/1024 49.8 ns 25.5 ns std::map<std::string, int>::count(key) (non-existent)/8192 66.3 ns 31.9 ns std::map<std::string, int>::contains(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::contains(key) (existent)/32 31.4 ns 18.0 ns std::map<std::string, int>::contains(key) (existent)/1024 44.3 ns 26.5 ns std::map<std::string, int>::contains(key) (existent)/8192 47.4 ns 30.2 ns std::map<std::string, int>::contains(key) (non-existent)/0 0.452 ns 0.441 ns std::map<std::string, int>::contains(key) (non-existent)/32 23.1 ns 11.5 ns std::map<std::string, int>::contains(key) (non-existent)/1024 46.2 ns 26.3 ns std::map<std::string, int>::contains(key) (non-existent)/8192 63.4 ns 31.4 ns std::map<std::string, int>::lower_bound(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::lower_bound(key) (existent)/32 17.2 ns 19.0 ns std::map<std::string, int>::lower_bound(key) (existent)/1024 27.1 ns 26.2 ns std::map<std::string, int>::lower_bound(key) (existent)/8192 34.0 ns 36.0 ns std::map<std::string, int>::lower_bound(key) (non-existent)/0 0.259 ns 0.257 ns std::map<std::string, int>::lower_bound(key) (non-existent)/32 11.6 ns 11.5 ns std::map<std::string, int>::lower_bound(key) (non-existent)/1024 24.8 ns 25.6 ns std::map<std::string, int>::lower_bound(key) (non-existent)/8192 31.7 ns 31.6 ns std::map<std::string, int>::upper_bound(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::upper_bound(key) (existent)/32 18.8 ns 19.7 ns std::map<std::string, int>::upper_bound(key) (existent)/1024 25.3 ns 27.7 ns std::map<std::string, int>::upper_bound(key) (existent)/8192 30.2 ns 29.9 ns std::map<std::string, int>::upper_bound(key) (non-existent)/0 0.260 ns 0.259 ns std::map<std::string, int>::upper_bound(key) (non-existent)/32 11.3 ns 12.0 ns std::map<std::string, int>::upper_bound(key) (non-existent)/1024 25.6 ns 25.9 ns std::map<std::string, int>::upper_bound(key) (non-existent)/8192 33.1 ns 34.2 ns std::map<std::string, int>::equal_range(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::equal_range(key) (existent)/32 33.5 ns 15.8 ns std::map<std::string, int>::equal_range(key) (existent)/1024 43.0 ns 25.1 ns std::map<std::string, int>::equal_range(key) (existent)/8192 54.1 ns 30.7 ns std::map<std::string, int>::equal_range(key) (non-existent)/0 0.265 ns 0.259 ns std::map<std::string, int>::equal_range(key) (non-existent)/32 22.1 ns 12.1 ns std::map<std::string, int>::equal_range(key) (non-existent)/1024 44.8 ns 24.4 ns std::map<std::string, int>::equal_range(key) (non-existent)/8192 62.2 ns 40.1 ns ``` Fixes #66577
2025-09-07 23:56:14 -08:00
auto __comp_res = __comp(__k, __rt->__get_value());
if (__comp_res.__less()) {
__result = std::__static_fancy_pointer_cast<__end_node_pointer>(__rt);
__rt = std::__static_fancy_pointer_cast<__node_pointer>(__rt->__left_);
[libc++] Optimize most of the __tree search algorithms (#155245) This patch introduces a new comparator, namely `__lazy_synth_three_way`, which tries to provide an efficient three way comparator for known types and falls back to using the provided comparator if it doesn't know how to do that. Currently, an efficient three way comparison is only provided when using one of the `less` comparions object from the standard library and `std::string`. This will be extended in future patches. ``` ------------------------------------------------------------------------------------------------------------------------------ Benchmark old new ------------------------------------------------------------------------------------------------------------------------------ std::map<std::string, int>::ctor(const&)/0 12.6 ns 12.6 ns std::map<std::string, int>::ctor(const&)/32 858 ns 837 ns std::map<std::string, int>::ctor(const&)/1024 46700 ns 46739 ns std::map<std::string, int>::ctor(const&)/8192 458100 ns 449806 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/0 12.8 ns 12.7 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/32 1286 ns 1266 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/1024 93812 ns 84686 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/8192 1480346 ns 1385924 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/0 12.9 ns 12.8 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/32 1044 ns 1055 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/1024 63071 ns 62861 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/8192 595046 ns 590223 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/0 13.6 ns 13.6 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/32 880 ns 911 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/1024 48627 ns 47808 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/8192 458552 ns 454497 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/0 13.8 ns 13.6 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/32 864 ns 851 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/1024 49483 ns 49555 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/8192 456977 ns 457894 ns std::map<std::string, int>::operator=(const&) (into populated Container)/0 1.31 ns 1.31 ns std::map<std::string, int>::operator=(const&) (into populated Container)/32 425 ns 415 ns std::map<std::string, int>::operator=(const&) (into populated Container)/1024 14248 ns 14225 ns std::map<std::string, int>::operator=(const&) (into populated Container)/8192 136684 ns 133696 ns std::map<std::string, int>::insert(value) (already present)/0 21.5 ns 16.2 ns std::map<std::string, int>::insert(value) (already present)/32 22.7 ns 25.1 ns std::map<std::string, int>::insert(value) (already present)/1024 54.5 ns 29.1 ns std::map<std::string, int>::insert(value) (already present)/8192 78.4 ns 30.4 ns std::map<std::string, int>::insert(value) (new value)/0 40.9 ns 39.0 ns std::map<std::string, int>::insert(value) (new value)/32 58.3 ns 47.2 ns std::map<std::string, int>::insert(value) (new value)/1024 120 ns 71.3 ns std::map<std::string, int>::insert(value) (new value)/8192 157 ns 129 ns std::map<std::string, int>::insert(hint, value) (good hint)/0 40.3 ns 40.7 ns std::map<std::string, int>::insert(hint, value) (good hint)/32 48.0 ns 30.0 ns std::map<std::string, int>::insert(hint, value) (good hint)/1024 107 ns 63.2 ns std::map<std::string, int>::insert(hint, value) (good hint)/8192 132 ns 107 ns std::map<std::string, int>::insert(hint, value) (bad hint)/0 27.0 ns 40.9 ns std::map<std::string, int>::insert(hint, value) (bad hint)/32 68.3 ns 58.4 ns std::map<std::string, int>::insert(hint, value) (bad hint)/1024 125 ns 82.0 ns std::map<std::string, int>::insert(hint, value) (bad hint)/8192 155 ns 150 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/0 404 ns 405 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/32 2004 ns 1805 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/1024 102820 ns 76102 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/8192 1144590 ns 949266 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/0 408 ns 404 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/32 1592 ns 1377 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/1024 74847 ns 53921 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/8192 828505 ns 698716 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/0 407 ns 407 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/32 1584 ns 1557 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/1024 47157 ns 47443 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/8192 623887 ns 628385 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/0 405 ns 403 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/32 1478 ns 1510 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/1024 47852 ns 47835 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/8192 605311 ns 606951 ns std::map<std::string, int>::erase(key) (existent)/0 129 ns 94.0 ns std::map<std::string, int>::erase(key) (existent)/32 110 ns 106 ns std::map<std::string, int>::erase(key) (existent)/1024 121 ns 128 ns std::map<std::string, int>::erase(key) (existent)/8192 165 ns 66.9 ns std::map<std::string, int>::erase(key) (non-existent)/0 0.269 ns 0.257 ns std::map<std::string, int>::erase(key) (non-existent)/32 21.9 ns 11.3 ns std::map<std::string, int>::erase(key) (non-existent)/1024 53.5 ns 25.4 ns std::map<std::string, int>::erase(key) (non-existent)/8192 67.3 ns 31.9 ns std::map<std::string, int>::erase(iterator)/0 46.3 ns 46.7 ns std::map<std::string, int>::erase(iterator)/32 44.4 ns 41.8 ns std::map<std::string, int>::erase(iterator)/1024 43.7 ns 46.4 ns std::map<std::string, int>::erase(iterator)/8192 45.2 ns 44.1 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/0 407 ns 407 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/32 876 ns 906 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/1024 20880 ns 20444 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/8192 252881 ns 241583 ns std::map<std::string, int>::clear()/0 407 ns 408 ns std::map<std::string, int>::clear()/32 1252 ns 1323 ns std::map<std::string, int>::clear()/1024 38488 ns 38017 ns std::map<std::string, int>::clear()/8192 416492 ns 428534 ns std::map<std::string, int>::find(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::find(key) (existent)/32 33.9 ns 15.3 ns std::map<std::string, int>::find(key) (existent)/1024 43.0 ns 25.5 ns std::map<std::string, int>::find(key) (existent)/8192 44.6 ns 29.3 ns std::map<std::string, int>::find(key) (non-existent)/0 0.259 ns 0.257 ns std::map<std::string, int>::find(key) (non-existent)/32 22.6 ns 11.4 ns std::map<std::string, int>::find(key) (non-existent)/1024 48.6 ns 25.1 ns std::map<std::string, int>::find(key) (non-existent)/8192 64.1 ns 31.1 ns std::map<std::string, int>::count(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::count(key) (existent)/32 32.2 ns 17.3 ns std::map<std::string, int>::count(key) (existent)/1024 42.4 ns 25.3 ns std::map<std::string, int>::count(key) (existent)/8192 44.4 ns 31.6 ns std::map<std::string, int>::count(key) (non-existent)/0 0.260 ns 0.259 ns std::map<std::string, int>::count(key) (non-existent)/32 22.9 ns 11.3 ns std::map<std::string, int>::count(key) (non-existent)/1024 49.8 ns 25.5 ns std::map<std::string, int>::count(key) (non-existent)/8192 66.3 ns 31.9 ns std::map<std::string, int>::contains(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::contains(key) (existent)/32 31.4 ns 18.0 ns std::map<std::string, int>::contains(key) (existent)/1024 44.3 ns 26.5 ns std::map<std::string, int>::contains(key) (existent)/8192 47.4 ns 30.2 ns std::map<std::string, int>::contains(key) (non-existent)/0 0.452 ns 0.441 ns std::map<std::string, int>::contains(key) (non-existent)/32 23.1 ns 11.5 ns std::map<std::string, int>::contains(key) (non-existent)/1024 46.2 ns 26.3 ns std::map<std::string, int>::contains(key) (non-existent)/8192 63.4 ns 31.4 ns std::map<std::string, int>::lower_bound(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::lower_bound(key) (existent)/32 17.2 ns 19.0 ns std::map<std::string, int>::lower_bound(key) (existent)/1024 27.1 ns 26.2 ns std::map<std::string, int>::lower_bound(key) (existent)/8192 34.0 ns 36.0 ns std::map<std::string, int>::lower_bound(key) (non-existent)/0 0.259 ns 0.257 ns std::map<std::string, int>::lower_bound(key) (non-existent)/32 11.6 ns 11.5 ns std::map<std::string, int>::lower_bound(key) (non-existent)/1024 24.8 ns 25.6 ns std::map<std::string, int>::lower_bound(key) (non-existent)/8192 31.7 ns 31.6 ns std::map<std::string, int>::upper_bound(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::upper_bound(key) (existent)/32 18.8 ns 19.7 ns std::map<std::string, int>::upper_bound(key) (existent)/1024 25.3 ns 27.7 ns std::map<std::string, int>::upper_bound(key) (existent)/8192 30.2 ns 29.9 ns std::map<std::string, int>::upper_bound(key) (non-existent)/0 0.260 ns 0.259 ns std::map<std::string, int>::upper_bound(key) (non-existent)/32 11.3 ns 12.0 ns std::map<std::string, int>::upper_bound(key) (non-existent)/1024 25.6 ns 25.9 ns std::map<std::string, int>::upper_bound(key) (non-existent)/8192 33.1 ns 34.2 ns std::map<std::string, int>::equal_range(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::equal_range(key) (existent)/32 33.5 ns 15.8 ns std::map<std::string, int>::equal_range(key) (existent)/1024 43.0 ns 25.1 ns std::map<std::string, int>::equal_range(key) (existent)/8192 54.1 ns 30.7 ns std::map<std::string, int>::equal_range(key) (non-existent)/0 0.265 ns 0.259 ns std::map<std::string, int>::equal_range(key) (non-existent)/32 22.1 ns 12.1 ns std::map<std::string, int>::equal_range(key) (non-existent)/1024 44.8 ns 24.4 ns std::map<std::string, int>::equal_range(key) (non-existent)/8192 62.2 ns 40.1 ns ``` Fixes #66577
2025-09-07 23:56:14 -08:00
} else if (__comp_res.__greater())
__rt = std::__static_fancy_pointer_cast<__node_pointer>(__rt->__right_);
2010-05-11 11:42:16 -08:00
else
return _Pp(iterator(__rt),
iterator(__rt->__right_ != nullptr
? std::__static_fancy_pointer_cast<__end_node_pointer>(std::__tree_min(__rt->__right_))
: __result));
2010-05-11 11:42:16 -08:00
}
return _Pp(iterator(__result), iterator(__result));
2010-05-11 11:42:16 -08:00
}
template <class _Tp, class _Compare, class _Allocator>
template <class _Key>
_LIBCPP_CONSTEXPR_SINCE_CXX26 pair<typename __tree<_Tp, _Compare, _Allocator>::const_iterator,
typename __tree<_Tp, _Compare, _Allocator>::const_iterator>
2010-05-11 11:42:16 -08:00
__tree<_Tp, _Compare, _Allocator>::__equal_range_unique(const _Key& __k) const {
using _Pp = pair<const_iterator, const_iterator>;
__end_node_pointer __result = __end_node();
__node_pointer __rt = __root();
[libc++] Optimize most of the __tree search algorithms (#155245) This patch introduces a new comparator, namely `__lazy_synth_three_way`, which tries to provide an efficient three way comparator for known types and falls back to using the provided comparator if it doesn't know how to do that. Currently, an efficient three way comparison is only provided when using one of the `less` comparions object from the standard library and `std::string`. This will be extended in future patches. ``` ------------------------------------------------------------------------------------------------------------------------------ Benchmark old new ------------------------------------------------------------------------------------------------------------------------------ std::map<std::string, int>::ctor(const&)/0 12.6 ns 12.6 ns std::map<std::string, int>::ctor(const&)/32 858 ns 837 ns std::map<std::string, int>::ctor(const&)/1024 46700 ns 46739 ns std::map<std::string, int>::ctor(const&)/8192 458100 ns 449806 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/0 12.8 ns 12.7 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/32 1286 ns 1266 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/1024 93812 ns 84686 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/8192 1480346 ns 1385924 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/0 12.9 ns 12.8 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/32 1044 ns 1055 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/1024 63071 ns 62861 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/8192 595046 ns 590223 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/0 13.6 ns 13.6 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/32 880 ns 911 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/1024 48627 ns 47808 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/8192 458552 ns 454497 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/0 13.8 ns 13.6 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/32 864 ns 851 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/1024 49483 ns 49555 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/8192 456977 ns 457894 ns std::map<std::string, int>::operator=(const&) (into populated Container)/0 1.31 ns 1.31 ns std::map<std::string, int>::operator=(const&) (into populated Container)/32 425 ns 415 ns std::map<std::string, int>::operator=(const&) (into populated Container)/1024 14248 ns 14225 ns std::map<std::string, int>::operator=(const&) (into populated Container)/8192 136684 ns 133696 ns std::map<std::string, int>::insert(value) (already present)/0 21.5 ns 16.2 ns std::map<std::string, int>::insert(value) (already present)/32 22.7 ns 25.1 ns std::map<std::string, int>::insert(value) (already present)/1024 54.5 ns 29.1 ns std::map<std::string, int>::insert(value) (already present)/8192 78.4 ns 30.4 ns std::map<std::string, int>::insert(value) (new value)/0 40.9 ns 39.0 ns std::map<std::string, int>::insert(value) (new value)/32 58.3 ns 47.2 ns std::map<std::string, int>::insert(value) (new value)/1024 120 ns 71.3 ns std::map<std::string, int>::insert(value) (new value)/8192 157 ns 129 ns std::map<std::string, int>::insert(hint, value) (good hint)/0 40.3 ns 40.7 ns std::map<std::string, int>::insert(hint, value) (good hint)/32 48.0 ns 30.0 ns std::map<std::string, int>::insert(hint, value) (good hint)/1024 107 ns 63.2 ns std::map<std::string, int>::insert(hint, value) (good hint)/8192 132 ns 107 ns std::map<std::string, int>::insert(hint, value) (bad hint)/0 27.0 ns 40.9 ns std::map<std::string, int>::insert(hint, value) (bad hint)/32 68.3 ns 58.4 ns std::map<std::string, int>::insert(hint, value) (bad hint)/1024 125 ns 82.0 ns std::map<std::string, int>::insert(hint, value) (bad hint)/8192 155 ns 150 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/0 404 ns 405 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/32 2004 ns 1805 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/1024 102820 ns 76102 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/8192 1144590 ns 949266 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/0 408 ns 404 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/32 1592 ns 1377 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/1024 74847 ns 53921 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/8192 828505 ns 698716 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/0 407 ns 407 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/32 1584 ns 1557 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/1024 47157 ns 47443 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/8192 623887 ns 628385 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/0 405 ns 403 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/32 1478 ns 1510 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/1024 47852 ns 47835 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/8192 605311 ns 606951 ns std::map<std::string, int>::erase(key) (existent)/0 129 ns 94.0 ns std::map<std::string, int>::erase(key) (existent)/32 110 ns 106 ns std::map<std::string, int>::erase(key) (existent)/1024 121 ns 128 ns std::map<std::string, int>::erase(key) (existent)/8192 165 ns 66.9 ns std::map<std::string, int>::erase(key) (non-existent)/0 0.269 ns 0.257 ns std::map<std::string, int>::erase(key) (non-existent)/32 21.9 ns 11.3 ns std::map<std::string, int>::erase(key) (non-existent)/1024 53.5 ns 25.4 ns std::map<std::string, int>::erase(key) (non-existent)/8192 67.3 ns 31.9 ns std::map<std::string, int>::erase(iterator)/0 46.3 ns 46.7 ns std::map<std::string, int>::erase(iterator)/32 44.4 ns 41.8 ns std::map<std::string, int>::erase(iterator)/1024 43.7 ns 46.4 ns std::map<std::string, int>::erase(iterator)/8192 45.2 ns 44.1 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/0 407 ns 407 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/32 876 ns 906 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/1024 20880 ns 20444 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/8192 252881 ns 241583 ns std::map<std::string, int>::clear()/0 407 ns 408 ns std::map<std::string, int>::clear()/32 1252 ns 1323 ns std::map<std::string, int>::clear()/1024 38488 ns 38017 ns std::map<std::string, int>::clear()/8192 416492 ns 428534 ns std::map<std::string, int>::find(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::find(key) (existent)/32 33.9 ns 15.3 ns std::map<std::string, int>::find(key) (existent)/1024 43.0 ns 25.5 ns std::map<std::string, int>::find(key) (existent)/8192 44.6 ns 29.3 ns std::map<std::string, int>::find(key) (non-existent)/0 0.259 ns 0.257 ns std::map<std::string, int>::find(key) (non-existent)/32 22.6 ns 11.4 ns std::map<std::string, int>::find(key) (non-existent)/1024 48.6 ns 25.1 ns std::map<std::string, int>::find(key) (non-existent)/8192 64.1 ns 31.1 ns std::map<std::string, int>::count(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::count(key) (existent)/32 32.2 ns 17.3 ns std::map<std::string, int>::count(key) (existent)/1024 42.4 ns 25.3 ns std::map<std::string, int>::count(key) (existent)/8192 44.4 ns 31.6 ns std::map<std::string, int>::count(key) (non-existent)/0 0.260 ns 0.259 ns std::map<std::string, int>::count(key) (non-existent)/32 22.9 ns 11.3 ns std::map<std::string, int>::count(key) (non-existent)/1024 49.8 ns 25.5 ns std::map<std::string, int>::count(key) (non-existent)/8192 66.3 ns 31.9 ns std::map<std::string, int>::contains(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::contains(key) (existent)/32 31.4 ns 18.0 ns std::map<std::string, int>::contains(key) (existent)/1024 44.3 ns 26.5 ns std::map<std::string, int>::contains(key) (existent)/8192 47.4 ns 30.2 ns std::map<std::string, int>::contains(key) (non-existent)/0 0.452 ns 0.441 ns std::map<std::string, int>::contains(key) (non-existent)/32 23.1 ns 11.5 ns std::map<std::string, int>::contains(key) (non-existent)/1024 46.2 ns 26.3 ns std::map<std::string, int>::contains(key) (non-existent)/8192 63.4 ns 31.4 ns std::map<std::string, int>::lower_bound(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::lower_bound(key) (existent)/32 17.2 ns 19.0 ns std::map<std::string, int>::lower_bound(key) (existent)/1024 27.1 ns 26.2 ns std::map<std::string, int>::lower_bound(key) (existent)/8192 34.0 ns 36.0 ns std::map<std::string, int>::lower_bound(key) (non-existent)/0 0.259 ns 0.257 ns std::map<std::string, int>::lower_bound(key) (non-existent)/32 11.6 ns 11.5 ns std::map<std::string, int>::lower_bound(key) (non-existent)/1024 24.8 ns 25.6 ns std::map<std::string, int>::lower_bound(key) (non-existent)/8192 31.7 ns 31.6 ns std::map<std::string, int>::upper_bound(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::upper_bound(key) (existent)/32 18.8 ns 19.7 ns std::map<std::string, int>::upper_bound(key) (existent)/1024 25.3 ns 27.7 ns std::map<std::string, int>::upper_bound(key) (existent)/8192 30.2 ns 29.9 ns std::map<std::string, int>::upper_bound(key) (non-existent)/0 0.260 ns 0.259 ns std::map<std::string, int>::upper_bound(key) (non-existent)/32 11.3 ns 12.0 ns std::map<std::string, int>::upper_bound(key) (non-existent)/1024 25.6 ns 25.9 ns std::map<std::string, int>::upper_bound(key) (non-existent)/8192 33.1 ns 34.2 ns std::map<std::string, int>::equal_range(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::equal_range(key) (existent)/32 33.5 ns 15.8 ns std::map<std::string, int>::equal_range(key) (existent)/1024 43.0 ns 25.1 ns std::map<std::string, int>::equal_range(key) (existent)/8192 54.1 ns 30.7 ns std::map<std::string, int>::equal_range(key) (non-existent)/0 0.265 ns 0.259 ns std::map<std::string, int>::equal_range(key) (non-existent)/32 22.1 ns 12.1 ns std::map<std::string, int>::equal_range(key) (non-existent)/1024 44.8 ns 24.4 ns std::map<std::string, int>::equal_range(key) (non-existent)/8192 62.2 ns 40.1 ns ``` Fixes #66577
2025-09-07 23:56:14 -08:00
auto __comp = __lazy_synth_three_way_comparator<value_compare, _Key, value_type>(value_comp());
2010-05-11 11:42:16 -08:00
while (__rt != nullptr) {
[libc++] Optimize most of the __tree search algorithms (#155245) This patch introduces a new comparator, namely `__lazy_synth_three_way`, which tries to provide an efficient three way comparator for known types and falls back to using the provided comparator if it doesn't know how to do that. Currently, an efficient three way comparison is only provided when using one of the `less` comparions object from the standard library and `std::string`. This will be extended in future patches. ``` ------------------------------------------------------------------------------------------------------------------------------ Benchmark old new ------------------------------------------------------------------------------------------------------------------------------ std::map<std::string, int>::ctor(const&)/0 12.6 ns 12.6 ns std::map<std::string, int>::ctor(const&)/32 858 ns 837 ns std::map<std::string, int>::ctor(const&)/1024 46700 ns 46739 ns std::map<std::string, int>::ctor(const&)/8192 458100 ns 449806 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/0 12.8 ns 12.7 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/32 1286 ns 1266 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/1024 93812 ns 84686 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/8192 1480346 ns 1385924 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/0 12.9 ns 12.8 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/32 1044 ns 1055 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/1024 63071 ns 62861 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/8192 595046 ns 590223 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/0 13.6 ns 13.6 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/32 880 ns 911 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/1024 48627 ns 47808 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/8192 458552 ns 454497 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/0 13.8 ns 13.6 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/32 864 ns 851 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/1024 49483 ns 49555 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/8192 456977 ns 457894 ns std::map<std::string, int>::operator=(const&) (into populated Container)/0 1.31 ns 1.31 ns std::map<std::string, int>::operator=(const&) (into populated Container)/32 425 ns 415 ns std::map<std::string, int>::operator=(const&) (into populated Container)/1024 14248 ns 14225 ns std::map<std::string, int>::operator=(const&) (into populated Container)/8192 136684 ns 133696 ns std::map<std::string, int>::insert(value) (already present)/0 21.5 ns 16.2 ns std::map<std::string, int>::insert(value) (already present)/32 22.7 ns 25.1 ns std::map<std::string, int>::insert(value) (already present)/1024 54.5 ns 29.1 ns std::map<std::string, int>::insert(value) (already present)/8192 78.4 ns 30.4 ns std::map<std::string, int>::insert(value) (new value)/0 40.9 ns 39.0 ns std::map<std::string, int>::insert(value) (new value)/32 58.3 ns 47.2 ns std::map<std::string, int>::insert(value) (new value)/1024 120 ns 71.3 ns std::map<std::string, int>::insert(value) (new value)/8192 157 ns 129 ns std::map<std::string, int>::insert(hint, value) (good hint)/0 40.3 ns 40.7 ns std::map<std::string, int>::insert(hint, value) (good hint)/32 48.0 ns 30.0 ns std::map<std::string, int>::insert(hint, value) (good hint)/1024 107 ns 63.2 ns std::map<std::string, int>::insert(hint, value) (good hint)/8192 132 ns 107 ns std::map<std::string, int>::insert(hint, value) (bad hint)/0 27.0 ns 40.9 ns std::map<std::string, int>::insert(hint, value) (bad hint)/32 68.3 ns 58.4 ns std::map<std::string, int>::insert(hint, value) (bad hint)/1024 125 ns 82.0 ns std::map<std::string, int>::insert(hint, value) (bad hint)/8192 155 ns 150 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/0 404 ns 405 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/32 2004 ns 1805 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/1024 102820 ns 76102 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/8192 1144590 ns 949266 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/0 408 ns 404 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/32 1592 ns 1377 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/1024 74847 ns 53921 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/8192 828505 ns 698716 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/0 407 ns 407 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/32 1584 ns 1557 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/1024 47157 ns 47443 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/8192 623887 ns 628385 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/0 405 ns 403 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/32 1478 ns 1510 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/1024 47852 ns 47835 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/8192 605311 ns 606951 ns std::map<std::string, int>::erase(key) (existent)/0 129 ns 94.0 ns std::map<std::string, int>::erase(key) (existent)/32 110 ns 106 ns std::map<std::string, int>::erase(key) (existent)/1024 121 ns 128 ns std::map<std::string, int>::erase(key) (existent)/8192 165 ns 66.9 ns std::map<std::string, int>::erase(key) (non-existent)/0 0.269 ns 0.257 ns std::map<std::string, int>::erase(key) (non-existent)/32 21.9 ns 11.3 ns std::map<std::string, int>::erase(key) (non-existent)/1024 53.5 ns 25.4 ns std::map<std::string, int>::erase(key) (non-existent)/8192 67.3 ns 31.9 ns std::map<std::string, int>::erase(iterator)/0 46.3 ns 46.7 ns std::map<std::string, int>::erase(iterator)/32 44.4 ns 41.8 ns std::map<std::string, int>::erase(iterator)/1024 43.7 ns 46.4 ns std::map<std::string, int>::erase(iterator)/8192 45.2 ns 44.1 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/0 407 ns 407 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/32 876 ns 906 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/1024 20880 ns 20444 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/8192 252881 ns 241583 ns std::map<std::string, int>::clear()/0 407 ns 408 ns std::map<std::string, int>::clear()/32 1252 ns 1323 ns std::map<std::string, int>::clear()/1024 38488 ns 38017 ns std::map<std::string, int>::clear()/8192 416492 ns 428534 ns std::map<std::string, int>::find(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::find(key) (existent)/32 33.9 ns 15.3 ns std::map<std::string, int>::find(key) (existent)/1024 43.0 ns 25.5 ns std::map<std::string, int>::find(key) (existent)/8192 44.6 ns 29.3 ns std::map<std::string, int>::find(key) (non-existent)/0 0.259 ns 0.257 ns std::map<std::string, int>::find(key) (non-existent)/32 22.6 ns 11.4 ns std::map<std::string, int>::find(key) (non-existent)/1024 48.6 ns 25.1 ns std::map<std::string, int>::find(key) (non-existent)/8192 64.1 ns 31.1 ns std::map<std::string, int>::count(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::count(key) (existent)/32 32.2 ns 17.3 ns std::map<std::string, int>::count(key) (existent)/1024 42.4 ns 25.3 ns std::map<std::string, int>::count(key) (existent)/8192 44.4 ns 31.6 ns std::map<std::string, int>::count(key) (non-existent)/0 0.260 ns 0.259 ns std::map<std::string, int>::count(key) (non-existent)/32 22.9 ns 11.3 ns std::map<std::string, int>::count(key) (non-existent)/1024 49.8 ns 25.5 ns std::map<std::string, int>::count(key) (non-existent)/8192 66.3 ns 31.9 ns std::map<std::string, int>::contains(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::contains(key) (existent)/32 31.4 ns 18.0 ns std::map<std::string, int>::contains(key) (existent)/1024 44.3 ns 26.5 ns std::map<std::string, int>::contains(key) (existent)/8192 47.4 ns 30.2 ns std::map<std::string, int>::contains(key) (non-existent)/0 0.452 ns 0.441 ns std::map<std::string, int>::contains(key) (non-existent)/32 23.1 ns 11.5 ns std::map<std::string, int>::contains(key) (non-existent)/1024 46.2 ns 26.3 ns std::map<std::string, int>::contains(key) (non-existent)/8192 63.4 ns 31.4 ns std::map<std::string, int>::lower_bound(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::lower_bound(key) (existent)/32 17.2 ns 19.0 ns std::map<std::string, int>::lower_bound(key) (existent)/1024 27.1 ns 26.2 ns std::map<std::string, int>::lower_bound(key) (existent)/8192 34.0 ns 36.0 ns std::map<std::string, int>::lower_bound(key) (non-existent)/0 0.259 ns 0.257 ns std::map<std::string, int>::lower_bound(key) (non-existent)/32 11.6 ns 11.5 ns std::map<std::string, int>::lower_bound(key) (non-existent)/1024 24.8 ns 25.6 ns std::map<std::string, int>::lower_bound(key) (non-existent)/8192 31.7 ns 31.6 ns std::map<std::string, int>::upper_bound(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::upper_bound(key) (existent)/32 18.8 ns 19.7 ns std::map<std::string, int>::upper_bound(key) (existent)/1024 25.3 ns 27.7 ns std::map<std::string, int>::upper_bound(key) (existent)/8192 30.2 ns 29.9 ns std::map<std::string, int>::upper_bound(key) (non-existent)/0 0.260 ns 0.259 ns std::map<std::string, int>::upper_bound(key) (non-existent)/32 11.3 ns 12.0 ns std::map<std::string, int>::upper_bound(key) (non-existent)/1024 25.6 ns 25.9 ns std::map<std::string, int>::upper_bound(key) (non-existent)/8192 33.1 ns 34.2 ns std::map<std::string, int>::equal_range(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::equal_range(key) (existent)/32 33.5 ns 15.8 ns std::map<std::string, int>::equal_range(key) (existent)/1024 43.0 ns 25.1 ns std::map<std::string, int>::equal_range(key) (existent)/8192 54.1 ns 30.7 ns std::map<std::string, int>::equal_range(key) (non-existent)/0 0.265 ns 0.259 ns std::map<std::string, int>::equal_range(key) (non-existent)/32 22.1 ns 12.1 ns std::map<std::string, int>::equal_range(key) (non-existent)/1024 44.8 ns 24.4 ns std::map<std::string, int>::equal_range(key) (non-existent)/8192 62.2 ns 40.1 ns ``` Fixes #66577
2025-09-07 23:56:14 -08:00
auto __comp_res = __comp(__k, __rt->__get_value());
if (__comp_res.__less()) {
__result = std::__static_fancy_pointer_cast<__end_node_pointer>(__rt);
__rt = std::__static_fancy_pointer_cast<__node_pointer>(__rt->__left_);
[libc++] Optimize most of the __tree search algorithms (#155245) This patch introduces a new comparator, namely `__lazy_synth_three_way`, which tries to provide an efficient three way comparator for known types and falls back to using the provided comparator if it doesn't know how to do that. Currently, an efficient three way comparison is only provided when using one of the `less` comparions object from the standard library and `std::string`. This will be extended in future patches. ``` ------------------------------------------------------------------------------------------------------------------------------ Benchmark old new ------------------------------------------------------------------------------------------------------------------------------ std::map<std::string, int>::ctor(const&)/0 12.6 ns 12.6 ns std::map<std::string, int>::ctor(const&)/32 858 ns 837 ns std::map<std::string, int>::ctor(const&)/1024 46700 ns 46739 ns std::map<std::string, int>::ctor(const&)/8192 458100 ns 449806 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/0 12.8 ns 12.7 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/32 1286 ns 1266 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/1024 93812 ns 84686 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/8192 1480346 ns 1385924 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/0 12.9 ns 12.8 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/32 1044 ns 1055 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/1024 63071 ns 62861 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/8192 595046 ns 590223 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/0 13.6 ns 13.6 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/32 880 ns 911 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/1024 48627 ns 47808 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/8192 458552 ns 454497 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/0 13.8 ns 13.6 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/32 864 ns 851 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/1024 49483 ns 49555 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/8192 456977 ns 457894 ns std::map<std::string, int>::operator=(const&) (into populated Container)/0 1.31 ns 1.31 ns std::map<std::string, int>::operator=(const&) (into populated Container)/32 425 ns 415 ns std::map<std::string, int>::operator=(const&) (into populated Container)/1024 14248 ns 14225 ns std::map<std::string, int>::operator=(const&) (into populated Container)/8192 136684 ns 133696 ns std::map<std::string, int>::insert(value) (already present)/0 21.5 ns 16.2 ns std::map<std::string, int>::insert(value) (already present)/32 22.7 ns 25.1 ns std::map<std::string, int>::insert(value) (already present)/1024 54.5 ns 29.1 ns std::map<std::string, int>::insert(value) (already present)/8192 78.4 ns 30.4 ns std::map<std::string, int>::insert(value) (new value)/0 40.9 ns 39.0 ns std::map<std::string, int>::insert(value) (new value)/32 58.3 ns 47.2 ns std::map<std::string, int>::insert(value) (new value)/1024 120 ns 71.3 ns std::map<std::string, int>::insert(value) (new value)/8192 157 ns 129 ns std::map<std::string, int>::insert(hint, value) (good hint)/0 40.3 ns 40.7 ns std::map<std::string, int>::insert(hint, value) (good hint)/32 48.0 ns 30.0 ns std::map<std::string, int>::insert(hint, value) (good hint)/1024 107 ns 63.2 ns std::map<std::string, int>::insert(hint, value) (good hint)/8192 132 ns 107 ns std::map<std::string, int>::insert(hint, value) (bad hint)/0 27.0 ns 40.9 ns std::map<std::string, int>::insert(hint, value) (bad hint)/32 68.3 ns 58.4 ns std::map<std::string, int>::insert(hint, value) (bad hint)/1024 125 ns 82.0 ns std::map<std::string, int>::insert(hint, value) (bad hint)/8192 155 ns 150 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/0 404 ns 405 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/32 2004 ns 1805 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/1024 102820 ns 76102 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/8192 1144590 ns 949266 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/0 408 ns 404 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/32 1592 ns 1377 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/1024 74847 ns 53921 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/8192 828505 ns 698716 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/0 407 ns 407 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/32 1584 ns 1557 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/1024 47157 ns 47443 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/8192 623887 ns 628385 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/0 405 ns 403 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/32 1478 ns 1510 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/1024 47852 ns 47835 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/8192 605311 ns 606951 ns std::map<std::string, int>::erase(key) (existent)/0 129 ns 94.0 ns std::map<std::string, int>::erase(key) (existent)/32 110 ns 106 ns std::map<std::string, int>::erase(key) (existent)/1024 121 ns 128 ns std::map<std::string, int>::erase(key) (existent)/8192 165 ns 66.9 ns std::map<std::string, int>::erase(key) (non-existent)/0 0.269 ns 0.257 ns std::map<std::string, int>::erase(key) (non-existent)/32 21.9 ns 11.3 ns std::map<std::string, int>::erase(key) (non-existent)/1024 53.5 ns 25.4 ns std::map<std::string, int>::erase(key) (non-existent)/8192 67.3 ns 31.9 ns std::map<std::string, int>::erase(iterator)/0 46.3 ns 46.7 ns std::map<std::string, int>::erase(iterator)/32 44.4 ns 41.8 ns std::map<std::string, int>::erase(iterator)/1024 43.7 ns 46.4 ns std::map<std::string, int>::erase(iterator)/8192 45.2 ns 44.1 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/0 407 ns 407 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/32 876 ns 906 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/1024 20880 ns 20444 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/8192 252881 ns 241583 ns std::map<std::string, int>::clear()/0 407 ns 408 ns std::map<std::string, int>::clear()/32 1252 ns 1323 ns std::map<std::string, int>::clear()/1024 38488 ns 38017 ns std::map<std::string, int>::clear()/8192 416492 ns 428534 ns std::map<std::string, int>::find(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::find(key) (existent)/32 33.9 ns 15.3 ns std::map<std::string, int>::find(key) (existent)/1024 43.0 ns 25.5 ns std::map<std::string, int>::find(key) (existent)/8192 44.6 ns 29.3 ns std::map<std::string, int>::find(key) (non-existent)/0 0.259 ns 0.257 ns std::map<std::string, int>::find(key) (non-existent)/32 22.6 ns 11.4 ns std::map<std::string, int>::find(key) (non-existent)/1024 48.6 ns 25.1 ns std::map<std::string, int>::find(key) (non-existent)/8192 64.1 ns 31.1 ns std::map<std::string, int>::count(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::count(key) (existent)/32 32.2 ns 17.3 ns std::map<std::string, int>::count(key) (existent)/1024 42.4 ns 25.3 ns std::map<std::string, int>::count(key) (existent)/8192 44.4 ns 31.6 ns std::map<std::string, int>::count(key) (non-existent)/0 0.260 ns 0.259 ns std::map<std::string, int>::count(key) (non-existent)/32 22.9 ns 11.3 ns std::map<std::string, int>::count(key) (non-existent)/1024 49.8 ns 25.5 ns std::map<std::string, int>::count(key) (non-existent)/8192 66.3 ns 31.9 ns std::map<std::string, int>::contains(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::contains(key) (existent)/32 31.4 ns 18.0 ns std::map<std::string, int>::contains(key) (existent)/1024 44.3 ns 26.5 ns std::map<std::string, int>::contains(key) (existent)/8192 47.4 ns 30.2 ns std::map<std::string, int>::contains(key) (non-existent)/0 0.452 ns 0.441 ns std::map<std::string, int>::contains(key) (non-existent)/32 23.1 ns 11.5 ns std::map<std::string, int>::contains(key) (non-existent)/1024 46.2 ns 26.3 ns std::map<std::string, int>::contains(key) (non-existent)/8192 63.4 ns 31.4 ns std::map<std::string, int>::lower_bound(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::lower_bound(key) (existent)/32 17.2 ns 19.0 ns std::map<std::string, int>::lower_bound(key) (existent)/1024 27.1 ns 26.2 ns std::map<std::string, int>::lower_bound(key) (existent)/8192 34.0 ns 36.0 ns std::map<std::string, int>::lower_bound(key) (non-existent)/0 0.259 ns 0.257 ns std::map<std::string, int>::lower_bound(key) (non-existent)/32 11.6 ns 11.5 ns std::map<std::string, int>::lower_bound(key) (non-existent)/1024 24.8 ns 25.6 ns std::map<std::string, int>::lower_bound(key) (non-existent)/8192 31.7 ns 31.6 ns std::map<std::string, int>::upper_bound(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::upper_bound(key) (existent)/32 18.8 ns 19.7 ns std::map<std::string, int>::upper_bound(key) (existent)/1024 25.3 ns 27.7 ns std::map<std::string, int>::upper_bound(key) (existent)/8192 30.2 ns 29.9 ns std::map<std::string, int>::upper_bound(key) (non-existent)/0 0.260 ns 0.259 ns std::map<std::string, int>::upper_bound(key) (non-existent)/32 11.3 ns 12.0 ns std::map<std::string, int>::upper_bound(key) (non-existent)/1024 25.6 ns 25.9 ns std::map<std::string, int>::upper_bound(key) (non-existent)/8192 33.1 ns 34.2 ns std::map<std::string, int>::equal_range(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::equal_range(key) (existent)/32 33.5 ns 15.8 ns std::map<std::string, int>::equal_range(key) (existent)/1024 43.0 ns 25.1 ns std::map<std::string, int>::equal_range(key) (existent)/8192 54.1 ns 30.7 ns std::map<std::string, int>::equal_range(key) (non-existent)/0 0.265 ns 0.259 ns std::map<std::string, int>::equal_range(key) (non-existent)/32 22.1 ns 12.1 ns std::map<std::string, int>::equal_range(key) (non-existent)/1024 44.8 ns 24.4 ns std::map<std::string, int>::equal_range(key) (non-existent)/8192 62.2 ns 40.1 ns ``` Fixes #66577
2025-09-07 23:56:14 -08:00
} else if (__comp_res.__greater())
__rt = std::__static_fancy_pointer_cast<__node_pointer>(__rt->__right_);
2010-05-11 11:42:16 -08:00
else
return _Pp(
const_iterator(__rt),
const_iterator(__rt->__right_ != nullptr
? std::__static_fancy_pointer_cast<__end_node_pointer>(std::__tree_min(__rt->__right_))
: __result));
2010-05-11 11:42:16 -08:00
}
return _Pp(const_iterator(__result), const_iterator(__result));
2010-05-11 11:42:16 -08:00
}
template <class _Tp, class _Compare, class _Allocator>
template <class _Key>
_LIBCPP_CONSTEXPR_SINCE_CXX26
pair<typename __tree<_Tp, _Compare, _Allocator>::iterator, typename __tree<_Tp, _Compare, _Allocator>::iterator>
__tree<_Tp, _Compare, _Allocator>::__equal_range_multi(const _Key& __k) {
using _Pp = pair<iterator, iterator>;
__end_node_pointer __result = __end_node();
[libc++] Optimize most of the __tree search algorithms (#155245) This patch introduces a new comparator, namely `__lazy_synth_three_way`, which tries to provide an efficient three way comparator for known types and falls back to using the provided comparator if it doesn't know how to do that. Currently, an efficient three way comparison is only provided when using one of the `less` comparions object from the standard library and `std::string`. This will be extended in future patches. ``` ------------------------------------------------------------------------------------------------------------------------------ Benchmark old new ------------------------------------------------------------------------------------------------------------------------------ std::map<std::string, int>::ctor(const&)/0 12.6 ns 12.6 ns std::map<std::string, int>::ctor(const&)/32 858 ns 837 ns std::map<std::string, int>::ctor(const&)/1024 46700 ns 46739 ns std::map<std::string, int>::ctor(const&)/8192 458100 ns 449806 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/0 12.8 ns 12.7 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/32 1286 ns 1266 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/1024 93812 ns 84686 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/8192 1480346 ns 1385924 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/0 12.9 ns 12.8 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/32 1044 ns 1055 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/1024 63071 ns 62861 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/8192 595046 ns 590223 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/0 13.6 ns 13.6 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/32 880 ns 911 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/1024 48627 ns 47808 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/8192 458552 ns 454497 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/0 13.8 ns 13.6 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/32 864 ns 851 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/1024 49483 ns 49555 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/8192 456977 ns 457894 ns std::map<std::string, int>::operator=(const&) (into populated Container)/0 1.31 ns 1.31 ns std::map<std::string, int>::operator=(const&) (into populated Container)/32 425 ns 415 ns std::map<std::string, int>::operator=(const&) (into populated Container)/1024 14248 ns 14225 ns std::map<std::string, int>::operator=(const&) (into populated Container)/8192 136684 ns 133696 ns std::map<std::string, int>::insert(value) (already present)/0 21.5 ns 16.2 ns std::map<std::string, int>::insert(value) (already present)/32 22.7 ns 25.1 ns std::map<std::string, int>::insert(value) (already present)/1024 54.5 ns 29.1 ns std::map<std::string, int>::insert(value) (already present)/8192 78.4 ns 30.4 ns std::map<std::string, int>::insert(value) (new value)/0 40.9 ns 39.0 ns std::map<std::string, int>::insert(value) (new value)/32 58.3 ns 47.2 ns std::map<std::string, int>::insert(value) (new value)/1024 120 ns 71.3 ns std::map<std::string, int>::insert(value) (new value)/8192 157 ns 129 ns std::map<std::string, int>::insert(hint, value) (good hint)/0 40.3 ns 40.7 ns std::map<std::string, int>::insert(hint, value) (good hint)/32 48.0 ns 30.0 ns std::map<std::string, int>::insert(hint, value) (good hint)/1024 107 ns 63.2 ns std::map<std::string, int>::insert(hint, value) (good hint)/8192 132 ns 107 ns std::map<std::string, int>::insert(hint, value) (bad hint)/0 27.0 ns 40.9 ns std::map<std::string, int>::insert(hint, value) (bad hint)/32 68.3 ns 58.4 ns std::map<std::string, int>::insert(hint, value) (bad hint)/1024 125 ns 82.0 ns std::map<std::string, int>::insert(hint, value) (bad hint)/8192 155 ns 150 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/0 404 ns 405 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/32 2004 ns 1805 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/1024 102820 ns 76102 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/8192 1144590 ns 949266 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/0 408 ns 404 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/32 1592 ns 1377 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/1024 74847 ns 53921 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/8192 828505 ns 698716 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/0 407 ns 407 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/32 1584 ns 1557 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/1024 47157 ns 47443 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/8192 623887 ns 628385 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/0 405 ns 403 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/32 1478 ns 1510 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/1024 47852 ns 47835 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/8192 605311 ns 606951 ns std::map<std::string, int>::erase(key) (existent)/0 129 ns 94.0 ns std::map<std::string, int>::erase(key) (existent)/32 110 ns 106 ns std::map<std::string, int>::erase(key) (existent)/1024 121 ns 128 ns std::map<std::string, int>::erase(key) (existent)/8192 165 ns 66.9 ns std::map<std::string, int>::erase(key) (non-existent)/0 0.269 ns 0.257 ns std::map<std::string, int>::erase(key) (non-existent)/32 21.9 ns 11.3 ns std::map<std::string, int>::erase(key) (non-existent)/1024 53.5 ns 25.4 ns std::map<std::string, int>::erase(key) (non-existent)/8192 67.3 ns 31.9 ns std::map<std::string, int>::erase(iterator)/0 46.3 ns 46.7 ns std::map<std::string, int>::erase(iterator)/32 44.4 ns 41.8 ns std::map<std::string, int>::erase(iterator)/1024 43.7 ns 46.4 ns std::map<std::string, int>::erase(iterator)/8192 45.2 ns 44.1 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/0 407 ns 407 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/32 876 ns 906 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/1024 20880 ns 20444 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/8192 252881 ns 241583 ns std::map<std::string, int>::clear()/0 407 ns 408 ns std::map<std::string, int>::clear()/32 1252 ns 1323 ns std::map<std::string, int>::clear()/1024 38488 ns 38017 ns std::map<std::string, int>::clear()/8192 416492 ns 428534 ns std::map<std::string, int>::find(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::find(key) (existent)/32 33.9 ns 15.3 ns std::map<std::string, int>::find(key) (existent)/1024 43.0 ns 25.5 ns std::map<std::string, int>::find(key) (existent)/8192 44.6 ns 29.3 ns std::map<std::string, int>::find(key) (non-existent)/0 0.259 ns 0.257 ns std::map<std::string, int>::find(key) (non-existent)/32 22.6 ns 11.4 ns std::map<std::string, int>::find(key) (non-existent)/1024 48.6 ns 25.1 ns std::map<std::string, int>::find(key) (non-existent)/8192 64.1 ns 31.1 ns std::map<std::string, int>::count(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::count(key) (existent)/32 32.2 ns 17.3 ns std::map<std::string, int>::count(key) (existent)/1024 42.4 ns 25.3 ns std::map<std::string, int>::count(key) (existent)/8192 44.4 ns 31.6 ns std::map<std::string, int>::count(key) (non-existent)/0 0.260 ns 0.259 ns std::map<std::string, int>::count(key) (non-existent)/32 22.9 ns 11.3 ns std::map<std::string, int>::count(key) (non-existent)/1024 49.8 ns 25.5 ns std::map<std::string, int>::count(key) (non-existent)/8192 66.3 ns 31.9 ns std::map<std::string, int>::contains(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::contains(key) (existent)/32 31.4 ns 18.0 ns std::map<std::string, int>::contains(key) (existent)/1024 44.3 ns 26.5 ns std::map<std::string, int>::contains(key) (existent)/8192 47.4 ns 30.2 ns std::map<std::string, int>::contains(key) (non-existent)/0 0.452 ns 0.441 ns std::map<std::string, int>::contains(key) (non-existent)/32 23.1 ns 11.5 ns std::map<std::string, int>::contains(key) (non-existent)/1024 46.2 ns 26.3 ns std::map<std::string, int>::contains(key) (non-existent)/8192 63.4 ns 31.4 ns std::map<std::string, int>::lower_bound(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::lower_bound(key) (existent)/32 17.2 ns 19.0 ns std::map<std::string, int>::lower_bound(key) (existent)/1024 27.1 ns 26.2 ns std::map<std::string, int>::lower_bound(key) (existent)/8192 34.0 ns 36.0 ns std::map<std::string, int>::lower_bound(key) (non-existent)/0 0.259 ns 0.257 ns std::map<std::string, int>::lower_bound(key) (non-existent)/32 11.6 ns 11.5 ns std::map<std::string, int>::lower_bound(key) (non-existent)/1024 24.8 ns 25.6 ns std::map<std::string, int>::lower_bound(key) (non-existent)/8192 31.7 ns 31.6 ns std::map<std::string, int>::upper_bound(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::upper_bound(key) (existent)/32 18.8 ns 19.7 ns std::map<std::string, int>::upper_bound(key) (existent)/1024 25.3 ns 27.7 ns std::map<std::string, int>::upper_bound(key) (existent)/8192 30.2 ns 29.9 ns std::map<std::string, int>::upper_bound(key) (non-existent)/0 0.260 ns 0.259 ns std::map<std::string, int>::upper_bound(key) (non-existent)/32 11.3 ns 12.0 ns std::map<std::string, int>::upper_bound(key) (non-existent)/1024 25.6 ns 25.9 ns std::map<std::string, int>::upper_bound(key) (non-existent)/8192 33.1 ns 34.2 ns std::map<std::string, int>::equal_range(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::equal_range(key) (existent)/32 33.5 ns 15.8 ns std::map<std::string, int>::equal_range(key) (existent)/1024 43.0 ns 25.1 ns std::map<std::string, int>::equal_range(key) (existent)/8192 54.1 ns 30.7 ns std::map<std::string, int>::equal_range(key) (non-existent)/0 0.265 ns 0.259 ns std::map<std::string, int>::equal_range(key) (non-existent)/32 22.1 ns 12.1 ns std::map<std::string, int>::equal_range(key) (non-existent)/1024 44.8 ns 24.4 ns std::map<std::string, int>::equal_range(key) (non-existent)/8192 62.2 ns 40.1 ns ``` Fixes #66577
2025-09-07 23:56:14 -08:00
__node_pointer __rt = __root();
auto __comp = __lazy_synth_three_way_comparator<value_compare, _Key, value_type>(value_comp());
2010-05-11 11:42:16 -08:00
while (__rt != nullptr) {
[libc++] Optimize most of the __tree search algorithms (#155245) This patch introduces a new comparator, namely `__lazy_synth_three_way`, which tries to provide an efficient three way comparator for known types and falls back to using the provided comparator if it doesn't know how to do that. Currently, an efficient three way comparison is only provided when using one of the `less` comparions object from the standard library and `std::string`. This will be extended in future patches. ``` ------------------------------------------------------------------------------------------------------------------------------ Benchmark old new ------------------------------------------------------------------------------------------------------------------------------ std::map<std::string, int>::ctor(const&)/0 12.6 ns 12.6 ns std::map<std::string, int>::ctor(const&)/32 858 ns 837 ns std::map<std::string, int>::ctor(const&)/1024 46700 ns 46739 ns std::map<std::string, int>::ctor(const&)/8192 458100 ns 449806 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/0 12.8 ns 12.7 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/32 1286 ns 1266 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/1024 93812 ns 84686 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/8192 1480346 ns 1385924 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/0 12.9 ns 12.8 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/32 1044 ns 1055 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/1024 63071 ns 62861 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/8192 595046 ns 590223 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/0 13.6 ns 13.6 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/32 880 ns 911 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/1024 48627 ns 47808 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/8192 458552 ns 454497 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/0 13.8 ns 13.6 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/32 864 ns 851 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/1024 49483 ns 49555 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/8192 456977 ns 457894 ns std::map<std::string, int>::operator=(const&) (into populated Container)/0 1.31 ns 1.31 ns std::map<std::string, int>::operator=(const&) (into populated Container)/32 425 ns 415 ns std::map<std::string, int>::operator=(const&) (into populated Container)/1024 14248 ns 14225 ns std::map<std::string, int>::operator=(const&) (into populated Container)/8192 136684 ns 133696 ns std::map<std::string, int>::insert(value) (already present)/0 21.5 ns 16.2 ns std::map<std::string, int>::insert(value) (already present)/32 22.7 ns 25.1 ns std::map<std::string, int>::insert(value) (already present)/1024 54.5 ns 29.1 ns std::map<std::string, int>::insert(value) (already present)/8192 78.4 ns 30.4 ns std::map<std::string, int>::insert(value) (new value)/0 40.9 ns 39.0 ns std::map<std::string, int>::insert(value) (new value)/32 58.3 ns 47.2 ns std::map<std::string, int>::insert(value) (new value)/1024 120 ns 71.3 ns std::map<std::string, int>::insert(value) (new value)/8192 157 ns 129 ns std::map<std::string, int>::insert(hint, value) (good hint)/0 40.3 ns 40.7 ns std::map<std::string, int>::insert(hint, value) (good hint)/32 48.0 ns 30.0 ns std::map<std::string, int>::insert(hint, value) (good hint)/1024 107 ns 63.2 ns std::map<std::string, int>::insert(hint, value) (good hint)/8192 132 ns 107 ns std::map<std::string, int>::insert(hint, value) (bad hint)/0 27.0 ns 40.9 ns std::map<std::string, int>::insert(hint, value) (bad hint)/32 68.3 ns 58.4 ns std::map<std::string, int>::insert(hint, value) (bad hint)/1024 125 ns 82.0 ns std::map<std::string, int>::insert(hint, value) (bad hint)/8192 155 ns 150 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/0 404 ns 405 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/32 2004 ns 1805 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/1024 102820 ns 76102 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/8192 1144590 ns 949266 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/0 408 ns 404 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/32 1592 ns 1377 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/1024 74847 ns 53921 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/8192 828505 ns 698716 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/0 407 ns 407 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/32 1584 ns 1557 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/1024 47157 ns 47443 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/8192 623887 ns 628385 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/0 405 ns 403 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/32 1478 ns 1510 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/1024 47852 ns 47835 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/8192 605311 ns 606951 ns std::map<std::string, int>::erase(key) (existent)/0 129 ns 94.0 ns std::map<std::string, int>::erase(key) (existent)/32 110 ns 106 ns std::map<std::string, int>::erase(key) (existent)/1024 121 ns 128 ns std::map<std::string, int>::erase(key) (existent)/8192 165 ns 66.9 ns std::map<std::string, int>::erase(key) (non-existent)/0 0.269 ns 0.257 ns std::map<std::string, int>::erase(key) (non-existent)/32 21.9 ns 11.3 ns std::map<std::string, int>::erase(key) (non-existent)/1024 53.5 ns 25.4 ns std::map<std::string, int>::erase(key) (non-existent)/8192 67.3 ns 31.9 ns std::map<std::string, int>::erase(iterator)/0 46.3 ns 46.7 ns std::map<std::string, int>::erase(iterator)/32 44.4 ns 41.8 ns std::map<std::string, int>::erase(iterator)/1024 43.7 ns 46.4 ns std::map<std::string, int>::erase(iterator)/8192 45.2 ns 44.1 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/0 407 ns 407 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/32 876 ns 906 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/1024 20880 ns 20444 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/8192 252881 ns 241583 ns std::map<std::string, int>::clear()/0 407 ns 408 ns std::map<std::string, int>::clear()/32 1252 ns 1323 ns std::map<std::string, int>::clear()/1024 38488 ns 38017 ns std::map<std::string, int>::clear()/8192 416492 ns 428534 ns std::map<std::string, int>::find(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::find(key) (existent)/32 33.9 ns 15.3 ns std::map<std::string, int>::find(key) (existent)/1024 43.0 ns 25.5 ns std::map<std::string, int>::find(key) (existent)/8192 44.6 ns 29.3 ns std::map<std::string, int>::find(key) (non-existent)/0 0.259 ns 0.257 ns std::map<std::string, int>::find(key) (non-existent)/32 22.6 ns 11.4 ns std::map<std::string, int>::find(key) (non-existent)/1024 48.6 ns 25.1 ns std::map<std::string, int>::find(key) (non-existent)/8192 64.1 ns 31.1 ns std::map<std::string, int>::count(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::count(key) (existent)/32 32.2 ns 17.3 ns std::map<std::string, int>::count(key) (existent)/1024 42.4 ns 25.3 ns std::map<std::string, int>::count(key) (existent)/8192 44.4 ns 31.6 ns std::map<std::string, int>::count(key) (non-existent)/0 0.260 ns 0.259 ns std::map<std::string, int>::count(key) (non-existent)/32 22.9 ns 11.3 ns std::map<std::string, int>::count(key) (non-existent)/1024 49.8 ns 25.5 ns std::map<std::string, int>::count(key) (non-existent)/8192 66.3 ns 31.9 ns std::map<std::string, int>::contains(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::contains(key) (existent)/32 31.4 ns 18.0 ns std::map<std::string, int>::contains(key) (existent)/1024 44.3 ns 26.5 ns std::map<std::string, int>::contains(key) (existent)/8192 47.4 ns 30.2 ns std::map<std::string, int>::contains(key) (non-existent)/0 0.452 ns 0.441 ns std::map<std::string, int>::contains(key) (non-existent)/32 23.1 ns 11.5 ns std::map<std::string, int>::contains(key) (non-existent)/1024 46.2 ns 26.3 ns std::map<std::string, int>::contains(key) (non-existent)/8192 63.4 ns 31.4 ns std::map<std::string, int>::lower_bound(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::lower_bound(key) (existent)/32 17.2 ns 19.0 ns std::map<std::string, int>::lower_bound(key) (existent)/1024 27.1 ns 26.2 ns std::map<std::string, int>::lower_bound(key) (existent)/8192 34.0 ns 36.0 ns std::map<std::string, int>::lower_bound(key) (non-existent)/0 0.259 ns 0.257 ns std::map<std::string, int>::lower_bound(key) (non-existent)/32 11.6 ns 11.5 ns std::map<std::string, int>::lower_bound(key) (non-existent)/1024 24.8 ns 25.6 ns std::map<std::string, int>::lower_bound(key) (non-existent)/8192 31.7 ns 31.6 ns std::map<std::string, int>::upper_bound(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::upper_bound(key) (existent)/32 18.8 ns 19.7 ns std::map<std::string, int>::upper_bound(key) (existent)/1024 25.3 ns 27.7 ns std::map<std::string, int>::upper_bound(key) (existent)/8192 30.2 ns 29.9 ns std::map<std::string, int>::upper_bound(key) (non-existent)/0 0.260 ns 0.259 ns std::map<std::string, int>::upper_bound(key) (non-existent)/32 11.3 ns 12.0 ns std::map<std::string, int>::upper_bound(key) (non-existent)/1024 25.6 ns 25.9 ns std::map<std::string, int>::upper_bound(key) (non-existent)/8192 33.1 ns 34.2 ns std::map<std::string, int>::equal_range(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::equal_range(key) (existent)/32 33.5 ns 15.8 ns std::map<std::string, int>::equal_range(key) (existent)/1024 43.0 ns 25.1 ns std::map<std::string, int>::equal_range(key) (existent)/8192 54.1 ns 30.7 ns std::map<std::string, int>::equal_range(key) (non-existent)/0 0.265 ns 0.259 ns std::map<std::string, int>::equal_range(key) (non-existent)/32 22.1 ns 12.1 ns std::map<std::string, int>::equal_range(key) (non-existent)/1024 44.8 ns 24.4 ns std::map<std::string, int>::equal_range(key) (non-existent)/8192 62.2 ns 40.1 ns ``` Fixes #66577
2025-09-07 23:56:14 -08:00
auto __comp_res = __comp(__k, __rt->__get_value());
if (__comp_res.__less()) {
__result = std::__static_fancy_pointer_cast<__end_node_pointer>(__rt);
__rt = std::__static_fancy_pointer_cast<__node_pointer>(__rt->__left_);
[libc++] Optimize most of the __tree search algorithms (#155245) This patch introduces a new comparator, namely `__lazy_synth_three_way`, which tries to provide an efficient three way comparator for known types and falls back to using the provided comparator if it doesn't know how to do that. Currently, an efficient three way comparison is only provided when using one of the `less` comparions object from the standard library and `std::string`. This will be extended in future patches. ``` ------------------------------------------------------------------------------------------------------------------------------ Benchmark old new ------------------------------------------------------------------------------------------------------------------------------ std::map<std::string, int>::ctor(const&)/0 12.6 ns 12.6 ns std::map<std::string, int>::ctor(const&)/32 858 ns 837 ns std::map<std::string, int>::ctor(const&)/1024 46700 ns 46739 ns std::map<std::string, int>::ctor(const&)/8192 458100 ns 449806 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/0 12.8 ns 12.7 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/32 1286 ns 1266 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/1024 93812 ns 84686 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/8192 1480346 ns 1385924 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/0 12.9 ns 12.8 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/32 1044 ns 1055 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/1024 63071 ns 62861 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/8192 595046 ns 590223 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/0 13.6 ns 13.6 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/32 880 ns 911 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/1024 48627 ns 47808 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/8192 458552 ns 454497 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/0 13.8 ns 13.6 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/32 864 ns 851 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/1024 49483 ns 49555 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/8192 456977 ns 457894 ns std::map<std::string, int>::operator=(const&) (into populated Container)/0 1.31 ns 1.31 ns std::map<std::string, int>::operator=(const&) (into populated Container)/32 425 ns 415 ns std::map<std::string, int>::operator=(const&) (into populated Container)/1024 14248 ns 14225 ns std::map<std::string, int>::operator=(const&) (into populated Container)/8192 136684 ns 133696 ns std::map<std::string, int>::insert(value) (already present)/0 21.5 ns 16.2 ns std::map<std::string, int>::insert(value) (already present)/32 22.7 ns 25.1 ns std::map<std::string, int>::insert(value) (already present)/1024 54.5 ns 29.1 ns std::map<std::string, int>::insert(value) (already present)/8192 78.4 ns 30.4 ns std::map<std::string, int>::insert(value) (new value)/0 40.9 ns 39.0 ns std::map<std::string, int>::insert(value) (new value)/32 58.3 ns 47.2 ns std::map<std::string, int>::insert(value) (new value)/1024 120 ns 71.3 ns std::map<std::string, int>::insert(value) (new value)/8192 157 ns 129 ns std::map<std::string, int>::insert(hint, value) (good hint)/0 40.3 ns 40.7 ns std::map<std::string, int>::insert(hint, value) (good hint)/32 48.0 ns 30.0 ns std::map<std::string, int>::insert(hint, value) (good hint)/1024 107 ns 63.2 ns std::map<std::string, int>::insert(hint, value) (good hint)/8192 132 ns 107 ns std::map<std::string, int>::insert(hint, value) (bad hint)/0 27.0 ns 40.9 ns std::map<std::string, int>::insert(hint, value) (bad hint)/32 68.3 ns 58.4 ns std::map<std::string, int>::insert(hint, value) (bad hint)/1024 125 ns 82.0 ns std::map<std::string, int>::insert(hint, value) (bad hint)/8192 155 ns 150 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/0 404 ns 405 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/32 2004 ns 1805 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/1024 102820 ns 76102 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/8192 1144590 ns 949266 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/0 408 ns 404 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/32 1592 ns 1377 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/1024 74847 ns 53921 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/8192 828505 ns 698716 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/0 407 ns 407 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/32 1584 ns 1557 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/1024 47157 ns 47443 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/8192 623887 ns 628385 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/0 405 ns 403 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/32 1478 ns 1510 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/1024 47852 ns 47835 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/8192 605311 ns 606951 ns std::map<std::string, int>::erase(key) (existent)/0 129 ns 94.0 ns std::map<std::string, int>::erase(key) (existent)/32 110 ns 106 ns std::map<std::string, int>::erase(key) (existent)/1024 121 ns 128 ns std::map<std::string, int>::erase(key) (existent)/8192 165 ns 66.9 ns std::map<std::string, int>::erase(key) (non-existent)/0 0.269 ns 0.257 ns std::map<std::string, int>::erase(key) (non-existent)/32 21.9 ns 11.3 ns std::map<std::string, int>::erase(key) (non-existent)/1024 53.5 ns 25.4 ns std::map<std::string, int>::erase(key) (non-existent)/8192 67.3 ns 31.9 ns std::map<std::string, int>::erase(iterator)/0 46.3 ns 46.7 ns std::map<std::string, int>::erase(iterator)/32 44.4 ns 41.8 ns std::map<std::string, int>::erase(iterator)/1024 43.7 ns 46.4 ns std::map<std::string, int>::erase(iterator)/8192 45.2 ns 44.1 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/0 407 ns 407 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/32 876 ns 906 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/1024 20880 ns 20444 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/8192 252881 ns 241583 ns std::map<std::string, int>::clear()/0 407 ns 408 ns std::map<std::string, int>::clear()/32 1252 ns 1323 ns std::map<std::string, int>::clear()/1024 38488 ns 38017 ns std::map<std::string, int>::clear()/8192 416492 ns 428534 ns std::map<std::string, int>::find(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::find(key) (existent)/32 33.9 ns 15.3 ns std::map<std::string, int>::find(key) (existent)/1024 43.0 ns 25.5 ns std::map<std::string, int>::find(key) (existent)/8192 44.6 ns 29.3 ns std::map<std::string, int>::find(key) (non-existent)/0 0.259 ns 0.257 ns std::map<std::string, int>::find(key) (non-existent)/32 22.6 ns 11.4 ns std::map<std::string, int>::find(key) (non-existent)/1024 48.6 ns 25.1 ns std::map<std::string, int>::find(key) (non-existent)/8192 64.1 ns 31.1 ns std::map<std::string, int>::count(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::count(key) (existent)/32 32.2 ns 17.3 ns std::map<std::string, int>::count(key) (existent)/1024 42.4 ns 25.3 ns std::map<std::string, int>::count(key) (existent)/8192 44.4 ns 31.6 ns std::map<std::string, int>::count(key) (non-existent)/0 0.260 ns 0.259 ns std::map<std::string, int>::count(key) (non-existent)/32 22.9 ns 11.3 ns std::map<std::string, int>::count(key) (non-existent)/1024 49.8 ns 25.5 ns std::map<std::string, int>::count(key) (non-existent)/8192 66.3 ns 31.9 ns std::map<std::string, int>::contains(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::contains(key) (existent)/32 31.4 ns 18.0 ns std::map<std::string, int>::contains(key) (existent)/1024 44.3 ns 26.5 ns std::map<std::string, int>::contains(key) (existent)/8192 47.4 ns 30.2 ns std::map<std::string, int>::contains(key) (non-existent)/0 0.452 ns 0.441 ns std::map<std::string, int>::contains(key) (non-existent)/32 23.1 ns 11.5 ns std::map<std::string, int>::contains(key) (non-existent)/1024 46.2 ns 26.3 ns std::map<std::string, int>::contains(key) (non-existent)/8192 63.4 ns 31.4 ns std::map<std::string, int>::lower_bound(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::lower_bound(key) (existent)/32 17.2 ns 19.0 ns std::map<std::string, int>::lower_bound(key) (existent)/1024 27.1 ns 26.2 ns std::map<std::string, int>::lower_bound(key) (existent)/8192 34.0 ns 36.0 ns std::map<std::string, int>::lower_bound(key) (non-existent)/0 0.259 ns 0.257 ns std::map<std::string, int>::lower_bound(key) (non-existent)/32 11.6 ns 11.5 ns std::map<std::string, int>::lower_bound(key) (non-existent)/1024 24.8 ns 25.6 ns std::map<std::string, int>::lower_bound(key) (non-existent)/8192 31.7 ns 31.6 ns std::map<std::string, int>::upper_bound(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::upper_bound(key) (existent)/32 18.8 ns 19.7 ns std::map<std::string, int>::upper_bound(key) (existent)/1024 25.3 ns 27.7 ns std::map<std::string, int>::upper_bound(key) (existent)/8192 30.2 ns 29.9 ns std::map<std::string, int>::upper_bound(key) (non-existent)/0 0.260 ns 0.259 ns std::map<std::string, int>::upper_bound(key) (non-existent)/32 11.3 ns 12.0 ns std::map<std::string, int>::upper_bound(key) (non-existent)/1024 25.6 ns 25.9 ns std::map<std::string, int>::upper_bound(key) (non-existent)/8192 33.1 ns 34.2 ns std::map<std::string, int>::equal_range(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::equal_range(key) (existent)/32 33.5 ns 15.8 ns std::map<std::string, int>::equal_range(key) (existent)/1024 43.0 ns 25.1 ns std::map<std::string, int>::equal_range(key) (existent)/8192 54.1 ns 30.7 ns std::map<std::string, int>::equal_range(key) (non-existent)/0 0.265 ns 0.259 ns std::map<std::string, int>::equal_range(key) (non-existent)/32 22.1 ns 12.1 ns std::map<std::string, int>::equal_range(key) (non-existent)/1024 44.8 ns 24.4 ns std::map<std::string, int>::equal_range(key) (non-existent)/8192 62.2 ns 40.1 ns ``` Fixes #66577
2025-09-07 23:56:14 -08:00
} else if (__comp_res.__greater())
__rt = std::__static_fancy_pointer_cast<__node_pointer>(__rt->__right_);
2010-05-11 11:42:16 -08:00
else
return _Pp(__lower_bound_multi(__k,
std::__static_fancy_pointer_cast<__node_pointer>(__rt->__left_),
std::__static_fancy_pointer_cast<__end_node_pointer>(__rt)),
__upper_bound_multi(__k, std::__static_fancy_pointer_cast<__node_pointer>(__rt->__right_), __result));
2010-05-11 11:42:16 -08:00
}
return _Pp(iterator(__result), iterator(__result));
2010-05-11 11:42:16 -08:00
}
template <class _Tp, class _Compare, class _Allocator>
template <class _Key>
_LIBCPP_CONSTEXPR_SINCE_CXX26 pair<typename __tree<_Tp, _Compare, _Allocator>::const_iterator,
typename __tree<_Tp, _Compare, _Allocator>::const_iterator>
2010-05-11 11:42:16 -08:00
__tree<_Tp, _Compare, _Allocator>::__equal_range_multi(const _Key& __k) const {
using _Pp = pair<const_iterator, const_iterator>;
__end_node_pointer __result = __end_node();
[libc++] Optimize most of the __tree search algorithms (#155245) This patch introduces a new comparator, namely `__lazy_synth_three_way`, which tries to provide an efficient three way comparator for known types and falls back to using the provided comparator if it doesn't know how to do that. Currently, an efficient three way comparison is only provided when using one of the `less` comparions object from the standard library and `std::string`. This will be extended in future patches. ``` ------------------------------------------------------------------------------------------------------------------------------ Benchmark old new ------------------------------------------------------------------------------------------------------------------------------ std::map<std::string, int>::ctor(const&)/0 12.6 ns 12.6 ns std::map<std::string, int>::ctor(const&)/32 858 ns 837 ns std::map<std::string, int>::ctor(const&)/1024 46700 ns 46739 ns std::map<std::string, int>::ctor(const&)/8192 458100 ns 449806 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/0 12.8 ns 12.7 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/32 1286 ns 1266 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/1024 93812 ns 84686 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/8192 1480346 ns 1385924 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/0 12.9 ns 12.8 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/32 1044 ns 1055 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/1024 63071 ns 62861 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/8192 595046 ns 590223 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/0 13.6 ns 13.6 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/32 880 ns 911 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/1024 48627 ns 47808 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/8192 458552 ns 454497 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/0 13.8 ns 13.6 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/32 864 ns 851 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/1024 49483 ns 49555 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/8192 456977 ns 457894 ns std::map<std::string, int>::operator=(const&) (into populated Container)/0 1.31 ns 1.31 ns std::map<std::string, int>::operator=(const&) (into populated Container)/32 425 ns 415 ns std::map<std::string, int>::operator=(const&) (into populated Container)/1024 14248 ns 14225 ns std::map<std::string, int>::operator=(const&) (into populated Container)/8192 136684 ns 133696 ns std::map<std::string, int>::insert(value) (already present)/0 21.5 ns 16.2 ns std::map<std::string, int>::insert(value) (already present)/32 22.7 ns 25.1 ns std::map<std::string, int>::insert(value) (already present)/1024 54.5 ns 29.1 ns std::map<std::string, int>::insert(value) (already present)/8192 78.4 ns 30.4 ns std::map<std::string, int>::insert(value) (new value)/0 40.9 ns 39.0 ns std::map<std::string, int>::insert(value) (new value)/32 58.3 ns 47.2 ns std::map<std::string, int>::insert(value) (new value)/1024 120 ns 71.3 ns std::map<std::string, int>::insert(value) (new value)/8192 157 ns 129 ns std::map<std::string, int>::insert(hint, value) (good hint)/0 40.3 ns 40.7 ns std::map<std::string, int>::insert(hint, value) (good hint)/32 48.0 ns 30.0 ns std::map<std::string, int>::insert(hint, value) (good hint)/1024 107 ns 63.2 ns std::map<std::string, int>::insert(hint, value) (good hint)/8192 132 ns 107 ns std::map<std::string, int>::insert(hint, value) (bad hint)/0 27.0 ns 40.9 ns std::map<std::string, int>::insert(hint, value) (bad hint)/32 68.3 ns 58.4 ns std::map<std::string, int>::insert(hint, value) (bad hint)/1024 125 ns 82.0 ns std::map<std::string, int>::insert(hint, value) (bad hint)/8192 155 ns 150 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/0 404 ns 405 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/32 2004 ns 1805 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/1024 102820 ns 76102 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/8192 1144590 ns 949266 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/0 408 ns 404 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/32 1592 ns 1377 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/1024 74847 ns 53921 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/8192 828505 ns 698716 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/0 407 ns 407 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/32 1584 ns 1557 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/1024 47157 ns 47443 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/8192 623887 ns 628385 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/0 405 ns 403 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/32 1478 ns 1510 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/1024 47852 ns 47835 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/8192 605311 ns 606951 ns std::map<std::string, int>::erase(key) (existent)/0 129 ns 94.0 ns std::map<std::string, int>::erase(key) (existent)/32 110 ns 106 ns std::map<std::string, int>::erase(key) (existent)/1024 121 ns 128 ns std::map<std::string, int>::erase(key) (existent)/8192 165 ns 66.9 ns std::map<std::string, int>::erase(key) (non-existent)/0 0.269 ns 0.257 ns std::map<std::string, int>::erase(key) (non-existent)/32 21.9 ns 11.3 ns std::map<std::string, int>::erase(key) (non-existent)/1024 53.5 ns 25.4 ns std::map<std::string, int>::erase(key) (non-existent)/8192 67.3 ns 31.9 ns std::map<std::string, int>::erase(iterator)/0 46.3 ns 46.7 ns std::map<std::string, int>::erase(iterator)/32 44.4 ns 41.8 ns std::map<std::string, int>::erase(iterator)/1024 43.7 ns 46.4 ns std::map<std::string, int>::erase(iterator)/8192 45.2 ns 44.1 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/0 407 ns 407 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/32 876 ns 906 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/1024 20880 ns 20444 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/8192 252881 ns 241583 ns std::map<std::string, int>::clear()/0 407 ns 408 ns std::map<std::string, int>::clear()/32 1252 ns 1323 ns std::map<std::string, int>::clear()/1024 38488 ns 38017 ns std::map<std::string, int>::clear()/8192 416492 ns 428534 ns std::map<std::string, int>::find(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::find(key) (existent)/32 33.9 ns 15.3 ns std::map<std::string, int>::find(key) (existent)/1024 43.0 ns 25.5 ns std::map<std::string, int>::find(key) (existent)/8192 44.6 ns 29.3 ns std::map<std::string, int>::find(key) (non-existent)/0 0.259 ns 0.257 ns std::map<std::string, int>::find(key) (non-existent)/32 22.6 ns 11.4 ns std::map<std::string, int>::find(key) (non-existent)/1024 48.6 ns 25.1 ns std::map<std::string, int>::find(key) (non-existent)/8192 64.1 ns 31.1 ns std::map<std::string, int>::count(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::count(key) (existent)/32 32.2 ns 17.3 ns std::map<std::string, int>::count(key) (existent)/1024 42.4 ns 25.3 ns std::map<std::string, int>::count(key) (existent)/8192 44.4 ns 31.6 ns std::map<std::string, int>::count(key) (non-existent)/0 0.260 ns 0.259 ns std::map<std::string, int>::count(key) (non-existent)/32 22.9 ns 11.3 ns std::map<std::string, int>::count(key) (non-existent)/1024 49.8 ns 25.5 ns std::map<std::string, int>::count(key) (non-existent)/8192 66.3 ns 31.9 ns std::map<std::string, int>::contains(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::contains(key) (existent)/32 31.4 ns 18.0 ns std::map<std::string, int>::contains(key) (existent)/1024 44.3 ns 26.5 ns std::map<std::string, int>::contains(key) (existent)/8192 47.4 ns 30.2 ns std::map<std::string, int>::contains(key) (non-existent)/0 0.452 ns 0.441 ns std::map<std::string, int>::contains(key) (non-existent)/32 23.1 ns 11.5 ns std::map<std::string, int>::contains(key) (non-existent)/1024 46.2 ns 26.3 ns std::map<std::string, int>::contains(key) (non-existent)/8192 63.4 ns 31.4 ns std::map<std::string, int>::lower_bound(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::lower_bound(key) (existent)/32 17.2 ns 19.0 ns std::map<std::string, int>::lower_bound(key) (existent)/1024 27.1 ns 26.2 ns std::map<std::string, int>::lower_bound(key) (existent)/8192 34.0 ns 36.0 ns std::map<std::string, int>::lower_bound(key) (non-existent)/0 0.259 ns 0.257 ns std::map<std::string, int>::lower_bound(key) (non-existent)/32 11.6 ns 11.5 ns std::map<std::string, int>::lower_bound(key) (non-existent)/1024 24.8 ns 25.6 ns std::map<std::string, int>::lower_bound(key) (non-existent)/8192 31.7 ns 31.6 ns std::map<std::string, int>::upper_bound(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::upper_bound(key) (existent)/32 18.8 ns 19.7 ns std::map<std::string, int>::upper_bound(key) (existent)/1024 25.3 ns 27.7 ns std::map<std::string, int>::upper_bound(key) (existent)/8192 30.2 ns 29.9 ns std::map<std::string, int>::upper_bound(key) (non-existent)/0 0.260 ns 0.259 ns std::map<std::string, int>::upper_bound(key) (non-existent)/32 11.3 ns 12.0 ns std::map<std::string, int>::upper_bound(key) (non-existent)/1024 25.6 ns 25.9 ns std::map<std::string, int>::upper_bound(key) (non-existent)/8192 33.1 ns 34.2 ns std::map<std::string, int>::equal_range(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::equal_range(key) (existent)/32 33.5 ns 15.8 ns std::map<std::string, int>::equal_range(key) (existent)/1024 43.0 ns 25.1 ns std::map<std::string, int>::equal_range(key) (existent)/8192 54.1 ns 30.7 ns std::map<std::string, int>::equal_range(key) (non-existent)/0 0.265 ns 0.259 ns std::map<std::string, int>::equal_range(key) (non-existent)/32 22.1 ns 12.1 ns std::map<std::string, int>::equal_range(key) (non-existent)/1024 44.8 ns 24.4 ns std::map<std::string, int>::equal_range(key) (non-existent)/8192 62.2 ns 40.1 ns ``` Fixes #66577
2025-09-07 23:56:14 -08:00
__node_pointer __rt = __root();
auto __comp = __lazy_synth_three_way_comparator<value_compare, _Key, value_type>(value_comp());
2010-05-11 11:42:16 -08:00
while (__rt != nullptr) {
[libc++] Optimize most of the __tree search algorithms (#155245) This patch introduces a new comparator, namely `__lazy_synth_three_way`, which tries to provide an efficient three way comparator for known types and falls back to using the provided comparator if it doesn't know how to do that. Currently, an efficient three way comparison is only provided when using one of the `less` comparions object from the standard library and `std::string`. This will be extended in future patches. ``` ------------------------------------------------------------------------------------------------------------------------------ Benchmark old new ------------------------------------------------------------------------------------------------------------------------------ std::map<std::string, int>::ctor(const&)/0 12.6 ns 12.6 ns std::map<std::string, int>::ctor(const&)/32 858 ns 837 ns std::map<std::string, int>::ctor(const&)/1024 46700 ns 46739 ns std::map<std::string, int>::ctor(const&)/8192 458100 ns 449806 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/0 12.8 ns 12.7 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/32 1286 ns 1266 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/1024 93812 ns 84686 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/8192 1480346 ns 1385924 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/0 12.9 ns 12.8 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/32 1044 ns 1055 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/1024 63071 ns 62861 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/8192 595046 ns 590223 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/0 13.6 ns 13.6 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/32 880 ns 911 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/1024 48627 ns 47808 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/8192 458552 ns 454497 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/0 13.8 ns 13.6 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/32 864 ns 851 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/1024 49483 ns 49555 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/8192 456977 ns 457894 ns std::map<std::string, int>::operator=(const&) (into populated Container)/0 1.31 ns 1.31 ns std::map<std::string, int>::operator=(const&) (into populated Container)/32 425 ns 415 ns std::map<std::string, int>::operator=(const&) (into populated Container)/1024 14248 ns 14225 ns std::map<std::string, int>::operator=(const&) (into populated Container)/8192 136684 ns 133696 ns std::map<std::string, int>::insert(value) (already present)/0 21.5 ns 16.2 ns std::map<std::string, int>::insert(value) (already present)/32 22.7 ns 25.1 ns std::map<std::string, int>::insert(value) (already present)/1024 54.5 ns 29.1 ns std::map<std::string, int>::insert(value) (already present)/8192 78.4 ns 30.4 ns std::map<std::string, int>::insert(value) (new value)/0 40.9 ns 39.0 ns std::map<std::string, int>::insert(value) (new value)/32 58.3 ns 47.2 ns std::map<std::string, int>::insert(value) (new value)/1024 120 ns 71.3 ns std::map<std::string, int>::insert(value) (new value)/8192 157 ns 129 ns std::map<std::string, int>::insert(hint, value) (good hint)/0 40.3 ns 40.7 ns std::map<std::string, int>::insert(hint, value) (good hint)/32 48.0 ns 30.0 ns std::map<std::string, int>::insert(hint, value) (good hint)/1024 107 ns 63.2 ns std::map<std::string, int>::insert(hint, value) (good hint)/8192 132 ns 107 ns std::map<std::string, int>::insert(hint, value) (bad hint)/0 27.0 ns 40.9 ns std::map<std::string, int>::insert(hint, value) (bad hint)/32 68.3 ns 58.4 ns std::map<std::string, int>::insert(hint, value) (bad hint)/1024 125 ns 82.0 ns std::map<std::string, int>::insert(hint, value) (bad hint)/8192 155 ns 150 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/0 404 ns 405 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/32 2004 ns 1805 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/1024 102820 ns 76102 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/8192 1144590 ns 949266 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/0 408 ns 404 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/32 1592 ns 1377 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/1024 74847 ns 53921 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/8192 828505 ns 698716 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/0 407 ns 407 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/32 1584 ns 1557 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/1024 47157 ns 47443 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/8192 623887 ns 628385 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/0 405 ns 403 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/32 1478 ns 1510 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/1024 47852 ns 47835 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/8192 605311 ns 606951 ns std::map<std::string, int>::erase(key) (existent)/0 129 ns 94.0 ns std::map<std::string, int>::erase(key) (existent)/32 110 ns 106 ns std::map<std::string, int>::erase(key) (existent)/1024 121 ns 128 ns std::map<std::string, int>::erase(key) (existent)/8192 165 ns 66.9 ns std::map<std::string, int>::erase(key) (non-existent)/0 0.269 ns 0.257 ns std::map<std::string, int>::erase(key) (non-existent)/32 21.9 ns 11.3 ns std::map<std::string, int>::erase(key) (non-existent)/1024 53.5 ns 25.4 ns std::map<std::string, int>::erase(key) (non-existent)/8192 67.3 ns 31.9 ns std::map<std::string, int>::erase(iterator)/0 46.3 ns 46.7 ns std::map<std::string, int>::erase(iterator)/32 44.4 ns 41.8 ns std::map<std::string, int>::erase(iterator)/1024 43.7 ns 46.4 ns std::map<std::string, int>::erase(iterator)/8192 45.2 ns 44.1 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/0 407 ns 407 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/32 876 ns 906 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/1024 20880 ns 20444 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/8192 252881 ns 241583 ns std::map<std::string, int>::clear()/0 407 ns 408 ns std::map<std::string, int>::clear()/32 1252 ns 1323 ns std::map<std::string, int>::clear()/1024 38488 ns 38017 ns std::map<std::string, int>::clear()/8192 416492 ns 428534 ns std::map<std::string, int>::find(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::find(key) (existent)/32 33.9 ns 15.3 ns std::map<std::string, int>::find(key) (existent)/1024 43.0 ns 25.5 ns std::map<std::string, int>::find(key) (existent)/8192 44.6 ns 29.3 ns std::map<std::string, int>::find(key) (non-existent)/0 0.259 ns 0.257 ns std::map<std::string, int>::find(key) (non-existent)/32 22.6 ns 11.4 ns std::map<std::string, int>::find(key) (non-existent)/1024 48.6 ns 25.1 ns std::map<std::string, int>::find(key) (non-existent)/8192 64.1 ns 31.1 ns std::map<std::string, int>::count(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::count(key) (existent)/32 32.2 ns 17.3 ns std::map<std::string, int>::count(key) (existent)/1024 42.4 ns 25.3 ns std::map<std::string, int>::count(key) (existent)/8192 44.4 ns 31.6 ns std::map<std::string, int>::count(key) (non-existent)/0 0.260 ns 0.259 ns std::map<std::string, int>::count(key) (non-existent)/32 22.9 ns 11.3 ns std::map<std::string, int>::count(key) (non-existent)/1024 49.8 ns 25.5 ns std::map<std::string, int>::count(key) (non-existent)/8192 66.3 ns 31.9 ns std::map<std::string, int>::contains(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::contains(key) (existent)/32 31.4 ns 18.0 ns std::map<std::string, int>::contains(key) (existent)/1024 44.3 ns 26.5 ns std::map<std::string, int>::contains(key) (existent)/8192 47.4 ns 30.2 ns std::map<std::string, int>::contains(key) (non-existent)/0 0.452 ns 0.441 ns std::map<std::string, int>::contains(key) (non-existent)/32 23.1 ns 11.5 ns std::map<std::string, int>::contains(key) (non-existent)/1024 46.2 ns 26.3 ns std::map<std::string, int>::contains(key) (non-existent)/8192 63.4 ns 31.4 ns std::map<std::string, int>::lower_bound(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::lower_bound(key) (existent)/32 17.2 ns 19.0 ns std::map<std::string, int>::lower_bound(key) (existent)/1024 27.1 ns 26.2 ns std::map<std::string, int>::lower_bound(key) (existent)/8192 34.0 ns 36.0 ns std::map<std::string, int>::lower_bound(key) (non-existent)/0 0.259 ns 0.257 ns std::map<std::string, int>::lower_bound(key) (non-existent)/32 11.6 ns 11.5 ns std::map<std::string, int>::lower_bound(key) (non-existent)/1024 24.8 ns 25.6 ns std::map<std::string, int>::lower_bound(key) (non-existent)/8192 31.7 ns 31.6 ns std::map<std::string, int>::upper_bound(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::upper_bound(key) (existent)/32 18.8 ns 19.7 ns std::map<std::string, int>::upper_bound(key) (existent)/1024 25.3 ns 27.7 ns std::map<std::string, int>::upper_bound(key) (existent)/8192 30.2 ns 29.9 ns std::map<std::string, int>::upper_bound(key) (non-existent)/0 0.260 ns 0.259 ns std::map<std::string, int>::upper_bound(key) (non-existent)/32 11.3 ns 12.0 ns std::map<std::string, int>::upper_bound(key) (non-existent)/1024 25.6 ns 25.9 ns std::map<std::string, int>::upper_bound(key) (non-existent)/8192 33.1 ns 34.2 ns std::map<std::string, int>::equal_range(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::equal_range(key) (existent)/32 33.5 ns 15.8 ns std::map<std::string, int>::equal_range(key) (existent)/1024 43.0 ns 25.1 ns std::map<std::string, int>::equal_range(key) (existent)/8192 54.1 ns 30.7 ns std::map<std::string, int>::equal_range(key) (non-existent)/0 0.265 ns 0.259 ns std::map<std::string, int>::equal_range(key) (non-existent)/32 22.1 ns 12.1 ns std::map<std::string, int>::equal_range(key) (non-existent)/1024 44.8 ns 24.4 ns std::map<std::string, int>::equal_range(key) (non-existent)/8192 62.2 ns 40.1 ns ``` Fixes #66577
2025-09-07 23:56:14 -08:00
auto __comp_res = __comp(__k, __rt->__get_value());
if (__comp_res.__less()) {
__result = std::__static_fancy_pointer_cast<__end_node_pointer>(__rt);
__rt = std::__static_fancy_pointer_cast<__node_pointer>(__rt->__left_);
[libc++] Optimize most of the __tree search algorithms (#155245) This patch introduces a new comparator, namely `__lazy_synth_three_way`, which tries to provide an efficient three way comparator for known types and falls back to using the provided comparator if it doesn't know how to do that. Currently, an efficient three way comparison is only provided when using one of the `less` comparions object from the standard library and `std::string`. This will be extended in future patches. ``` ------------------------------------------------------------------------------------------------------------------------------ Benchmark old new ------------------------------------------------------------------------------------------------------------------------------ std::map<std::string, int>::ctor(const&)/0 12.6 ns 12.6 ns std::map<std::string, int>::ctor(const&)/32 858 ns 837 ns std::map<std::string, int>::ctor(const&)/1024 46700 ns 46739 ns std::map<std::string, int>::ctor(const&)/8192 458100 ns 449806 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/0 12.8 ns 12.7 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/32 1286 ns 1266 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/1024 93812 ns 84686 ns std::map<std::string, int>::ctor(iterator, iterator) (unsorted sequence)/8192 1480346 ns 1385924 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/0 12.9 ns 12.8 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/32 1044 ns 1055 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/1024 63071 ns 62861 ns std::map<std::string, int>::ctor(iterator, iterator) (sorted sequence)/8192 595046 ns 590223 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/0 13.6 ns 13.6 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/32 880 ns 911 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/1024 48627 ns 47808 ns std::map<std::string, int>::operator=(const&) (into cleared Container)/8192 458552 ns 454497 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/0 13.8 ns 13.6 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/32 864 ns 851 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/1024 49483 ns 49555 ns std::map<std::string, int>::operator=(const&) (into partially populated Container)/8192 456977 ns 457894 ns std::map<std::string, int>::operator=(const&) (into populated Container)/0 1.31 ns 1.31 ns std::map<std::string, int>::operator=(const&) (into populated Container)/32 425 ns 415 ns std::map<std::string, int>::operator=(const&) (into populated Container)/1024 14248 ns 14225 ns std::map<std::string, int>::operator=(const&) (into populated Container)/8192 136684 ns 133696 ns std::map<std::string, int>::insert(value) (already present)/0 21.5 ns 16.2 ns std::map<std::string, int>::insert(value) (already present)/32 22.7 ns 25.1 ns std::map<std::string, int>::insert(value) (already present)/1024 54.5 ns 29.1 ns std::map<std::string, int>::insert(value) (already present)/8192 78.4 ns 30.4 ns std::map<std::string, int>::insert(value) (new value)/0 40.9 ns 39.0 ns std::map<std::string, int>::insert(value) (new value)/32 58.3 ns 47.2 ns std::map<std::string, int>::insert(value) (new value)/1024 120 ns 71.3 ns std::map<std::string, int>::insert(value) (new value)/8192 157 ns 129 ns std::map<std::string, int>::insert(hint, value) (good hint)/0 40.3 ns 40.7 ns std::map<std::string, int>::insert(hint, value) (good hint)/32 48.0 ns 30.0 ns std::map<std::string, int>::insert(hint, value) (good hint)/1024 107 ns 63.2 ns std::map<std::string, int>::insert(hint, value) (good hint)/8192 132 ns 107 ns std::map<std::string, int>::insert(hint, value) (bad hint)/0 27.0 ns 40.9 ns std::map<std::string, int>::insert(hint, value) (bad hint)/32 68.3 ns 58.4 ns std::map<std::string, int>::insert(hint, value) (bad hint)/1024 125 ns 82.0 ns std::map<std::string, int>::insert(hint, value) (bad hint)/8192 155 ns 150 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/0 404 ns 405 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/32 2004 ns 1805 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/1024 102820 ns 76102 ns std::map<std::string, int>::insert(iterator, iterator) (all new keys)/8192 1144590 ns 949266 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/0 408 ns 404 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/32 1592 ns 1377 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/1024 74847 ns 53921 ns std::map<std::string, int>::insert(iterator, iterator) (half new keys)/8192 828505 ns 698716 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/0 407 ns 407 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/32 1584 ns 1557 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/1024 47157 ns 47443 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from same type)/8192 623887 ns 628385 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/0 405 ns 403 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/32 1478 ns 1510 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/1024 47852 ns 47835 ns std::map<std::string, int>::insert(iterator, iterator) (product_iterator from zip_view)/8192 605311 ns 606951 ns std::map<std::string, int>::erase(key) (existent)/0 129 ns 94.0 ns std::map<std::string, int>::erase(key) (existent)/32 110 ns 106 ns std::map<std::string, int>::erase(key) (existent)/1024 121 ns 128 ns std::map<std::string, int>::erase(key) (existent)/8192 165 ns 66.9 ns std::map<std::string, int>::erase(key) (non-existent)/0 0.269 ns 0.257 ns std::map<std::string, int>::erase(key) (non-existent)/32 21.9 ns 11.3 ns std::map<std::string, int>::erase(key) (non-existent)/1024 53.5 ns 25.4 ns std::map<std::string, int>::erase(key) (non-existent)/8192 67.3 ns 31.9 ns std::map<std::string, int>::erase(iterator)/0 46.3 ns 46.7 ns std::map<std::string, int>::erase(iterator)/32 44.4 ns 41.8 ns std::map<std::string, int>::erase(iterator)/1024 43.7 ns 46.4 ns std::map<std::string, int>::erase(iterator)/8192 45.2 ns 44.1 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/0 407 ns 407 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/32 876 ns 906 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/1024 20880 ns 20444 ns std::map<std::string, int>::erase(iterator, iterator) (erase half the container)/8192 252881 ns 241583 ns std::map<std::string, int>::clear()/0 407 ns 408 ns std::map<std::string, int>::clear()/32 1252 ns 1323 ns std::map<std::string, int>::clear()/1024 38488 ns 38017 ns std::map<std::string, int>::clear()/8192 416492 ns 428534 ns std::map<std::string, int>::find(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::find(key) (existent)/32 33.9 ns 15.3 ns std::map<std::string, int>::find(key) (existent)/1024 43.0 ns 25.5 ns std::map<std::string, int>::find(key) (existent)/8192 44.6 ns 29.3 ns std::map<std::string, int>::find(key) (non-existent)/0 0.259 ns 0.257 ns std::map<std::string, int>::find(key) (non-existent)/32 22.6 ns 11.4 ns std::map<std::string, int>::find(key) (non-existent)/1024 48.6 ns 25.1 ns std::map<std::string, int>::find(key) (non-existent)/8192 64.1 ns 31.1 ns std::map<std::string, int>::count(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::count(key) (existent)/32 32.2 ns 17.3 ns std::map<std::string, int>::count(key) (existent)/1024 42.4 ns 25.3 ns std::map<std::string, int>::count(key) (existent)/8192 44.4 ns 31.6 ns std::map<std::string, int>::count(key) (non-existent)/0 0.260 ns 0.259 ns std::map<std::string, int>::count(key) (non-existent)/32 22.9 ns 11.3 ns std::map<std::string, int>::count(key) (non-existent)/1024 49.8 ns 25.5 ns std::map<std::string, int>::count(key) (non-existent)/8192 66.3 ns 31.9 ns std::map<std::string, int>::contains(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::contains(key) (existent)/32 31.4 ns 18.0 ns std::map<std::string, int>::contains(key) (existent)/1024 44.3 ns 26.5 ns std::map<std::string, int>::contains(key) (existent)/8192 47.4 ns 30.2 ns std::map<std::string, int>::contains(key) (non-existent)/0 0.452 ns 0.441 ns std::map<std::string, int>::contains(key) (non-existent)/32 23.1 ns 11.5 ns std::map<std::string, int>::contains(key) (non-existent)/1024 46.2 ns 26.3 ns std::map<std::string, int>::contains(key) (non-existent)/8192 63.4 ns 31.4 ns std::map<std::string, int>::lower_bound(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::lower_bound(key) (existent)/32 17.2 ns 19.0 ns std::map<std::string, int>::lower_bound(key) (existent)/1024 27.1 ns 26.2 ns std::map<std::string, int>::lower_bound(key) (existent)/8192 34.0 ns 36.0 ns std::map<std::string, int>::lower_bound(key) (non-existent)/0 0.259 ns 0.257 ns std::map<std::string, int>::lower_bound(key) (non-existent)/32 11.6 ns 11.5 ns std::map<std::string, int>::lower_bound(key) (non-existent)/1024 24.8 ns 25.6 ns std::map<std::string, int>::lower_bound(key) (non-existent)/8192 31.7 ns 31.6 ns std::map<std::string, int>::upper_bound(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::upper_bound(key) (existent)/32 18.8 ns 19.7 ns std::map<std::string, int>::upper_bound(key) (existent)/1024 25.3 ns 27.7 ns std::map<std::string, int>::upper_bound(key) (existent)/8192 30.2 ns 29.9 ns std::map<std::string, int>::upper_bound(key) (non-existent)/0 0.260 ns 0.259 ns std::map<std::string, int>::upper_bound(key) (non-existent)/32 11.3 ns 12.0 ns std::map<std::string, int>::upper_bound(key) (non-existent)/1024 25.6 ns 25.9 ns std::map<std::string, int>::upper_bound(key) (non-existent)/8192 33.1 ns 34.2 ns std::map<std::string, int>::equal_range(key) (existent)/0 0.008 ns 0.008 ns std::map<std::string, int>::equal_range(key) (existent)/32 33.5 ns 15.8 ns std::map<std::string, int>::equal_range(key) (existent)/1024 43.0 ns 25.1 ns std::map<std::string, int>::equal_range(key) (existent)/8192 54.1 ns 30.7 ns std::map<std::string, int>::equal_range(key) (non-existent)/0 0.265 ns 0.259 ns std::map<std::string, int>::equal_range(key) (non-existent)/32 22.1 ns 12.1 ns std::map<std::string, int>::equal_range(key) (non-existent)/1024 44.8 ns 24.4 ns std::map<std::string, int>::equal_range(key) (non-existent)/8192 62.2 ns 40.1 ns ``` Fixes #66577
2025-09-07 23:56:14 -08:00
} else if (__comp_res.__greater())
__rt = std::__static_fancy_pointer_cast<__node_pointer>(__rt->__right_);
2010-05-11 11:42:16 -08:00
else
return _Pp(__lower_bound_multi(__k,
std::__static_fancy_pointer_cast<__node_pointer>(__rt->__left_),
std::__static_fancy_pointer_cast<__end_node_pointer>(__rt)),
__upper_bound_multi(__k, std::__static_fancy_pointer_cast<__node_pointer>(__rt->__right_), __result));
2010-05-11 11:42:16 -08:00
}
return _Pp(const_iterator(__result), const_iterator(__result));
2010-05-11 11:42:16 -08:00
}
template <class _Tp, class _Compare, class _Allocator>
_LIBCPP_CONSTEXPR_SINCE_CXX26 typename __tree<_Tp, _Compare, _Allocator>::__node_holder
__tree<_Tp, _Compare, _Allocator>::remove(const_iterator __p) _NOEXCEPT {
Fix undefined behavior in __tree Summary: This patch attempts to fix the undefined behavior in __tree by changing the node pointer types used throughout. The pointer types are changed for raw pointers in the current ABI and for fancy pointers in ABI V2 (since the fancy pointer types may not be ABI compatible). The UB in `__tree` arises because tree downcasts the embedded end node and then deferences that pointer. Currently there are 3 node types in __tree. * `__tree_end_node` which contains the `__left_` pointer. This node is embedded within the container. * `__tree_node_base` which contains `__right_`, `__parent_` and `__is_black`. This node is used throughout the tree rebalancing algorithms. * `__tree_node` which contains `__value_`. Currently `__tree` stores the start of the tree, `__begin_node_`, as a pointer to a `__tree_node`. Additionally the iterators store their position as a pointer to a `__tree_node`. In both of these cases the pointee can be the end node. This is fixed by changing them to store `__tree_end_node` pointers instead. To make this change I introduced an `__iter_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node` in the current one. Both `__tree::__begin_node_` and iterator pointers are now stored as `__iter_pointers`. The other situation where `__tree_end_node` is stored as the wrong type is in `__tree_node_base::__parent_`. Currently `__left_`, `__right_`, and `__parent_` are all `__tree_node_base` pointers. Since the end node will only be stored in `__parent_` the fix is to change `__parent_` to be a pointer to `__tree_end_node`. To make this change I introduced a `__parent_pointer` typedef which is defined to be a pointer to either `__tree_end_node` in the new ABI or `__tree_node_base` in the current one. Note that in the new ABI `__iter_pointer` and `__parent_pointer` are the same type (but not in the old one). The confusion between these two types is unfortunate but it was the best solution I could come up with that maintains the ABI. The typedef changes force a ton of explicit type casts to correct pointer types and to make current code compatible with both the old and new pointer typedefs. This is the bulk of the change and it's really messy. Unfortunately I don't know how to avoid it. Please let me know what you think. Reviewers: howard.hinnant, mclow.lists Subscribers: howard.hinnant, bbannier, cfe-commits Differential Revision: https://reviews.llvm.org/D20786 llvm-svn: 276003
2016-07-19 09:56:20 -08:00
__node_pointer __np = __p.__get_np();
if (__begin_node_ == __p.__ptr_) {
2010-05-11 11:42:16 -08:00
if (__np->__right_ != nullptr)
__begin_node_ = std::__static_fancy_pointer_cast<__end_node_pointer>(__np->__right_);
2010-05-11 11:42:16 -08:00
else
__begin_node_ = std::__static_fancy_pointer_cast<__end_node_pointer>(__np->__parent_);
2010-05-11 11:42:16 -08:00
}
--__size_;
std::__tree_remove(__end_node()->__left_, std::__static_fancy_pointer_cast<__node_base_pointer>(__np));
return __node_holder(__np, _Dp(__node_alloc(), true));
2010-05-11 11:42:16 -08:00
}
2011-06-04 06:31:57 -08:00
template <class _Tp, class _Compare, class _Allocator>
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
swap(__tree<_Tp, _Compare, _Allocator>& __x, __tree<_Tp, _Compare, _Allocator>& __y)
2011-06-04 06:31:57 -08:00
_NOEXCEPT_(_NOEXCEPT_(__x.swap(__y))) {
__x.swap(__y);
}
2010-05-11 11:42:16 -08:00
_LIBCPP_END_NAMESPACE_STD
_LIBCPP_POP_MACROS
#endif // _LIBCPP___TREE