Skip to content

[libc++] Optimize {set,map}::{lower,upper}_bound#161366

Merged
philnik777 merged 1 commit into
llvm:mainfrom
philnik777:optimize_tree_lower_upper_bound
Oct 9, 2025
Merged

[libc++] Optimize {set,map}::{lower,upper}_bound#161366
philnik777 merged 1 commit into
llvm:mainfrom
philnik777:optimize_tree_lower_upper_bound

Conversation

@philnik777

@philnik777 philnik777 commented Sep 30, 2025

Copy link
Copy Markdown
Contributor

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

@ldionne ldionne marked this pull request as ready for review October 1, 2025 15:24
@ldionne ldionne requested a review from a team as a code owner October 1, 2025 15:24
@llvmbot llvmbot added the libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. label Oct 1, 2025
@llvmbot

llvmbot commented Oct 1, 2025

Copy link
Copy Markdown
Member

@llvm/pr-subscribers-libcxx

Author: Nikolas Klauser (philnik777)

Changes

Apple M4:

Benchmark                                                                                        Baseline    Candidate    Difference    % Difference
---------------------------------------------------------------------------------------------  ----------  -----------  ------------  --------------
std::map&lt;int,_int&gt;::lower_bound(key)_(existent)/0                                                    0.01         0.01         -0.00          -25.78
std::map&lt;int,_int&gt;::lower_bound(key)_(existent)/1024                                                 7.94         4.28         -3.66          -46.09
std::map&lt;int,_int&gt;::lower_bound(key)_(existent)/32                                                   2.73         1.69         -1.03          -37.89
std::map&lt;int,_int&gt;::lower_bound(key)_(existent)/8192                                                11.63         5.52         -6.11          -52.55
std::map&lt;int,_int&gt;::lower_bound(key)_(non-existent)/0                                                0.28         0.28         -0.00           -1.35
std::map&lt;int,_int&gt;::lower_bound(key)_(non-existent)/1024                                            17.21         7.63         -9.58          -55.67
std::map&lt;int,_int&gt;::lower_bound(key)_(non-existent)/32                                               4.71         3.26         -1.45          -30.71
std::map&lt;int,_int&gt;::lower_bound(key)_(non-existent)/8192                                            26.82        10.58        -16.24          -60.55
std::map&lt;int,_int&gt;::upper_bound(key)_(existent)/0                                                    0.01         0.01          0.00           20.62
std::map&lt;int,_int&gt;::upper_bound(key)_(existent)/1024                                                 7.93         3.61         -4.32          -54.49
std::map&lt;int,_int&gt;::upper_bound(key)_(existent)/32                                                   2.83         1.98         -0.85          -30.01
std::map&lt;int,_int&gt;::upper_bound(key)_(existent)/8192                                                11.69         5.72         -5.97          -51.06
std::map&lt;int,_int&gt;::upper_bound(key)_(non-existent)/0                                                0.28         0.28         -0.00           -1.36
std::map&lt;int,_int&gt;::upper_bound(key)_(non-existent)/1024                                            17.21         8.00         -9.21          -53.53
std::map&lt;int,_int&gt;::upper_bound(key)_(non-existent)/32                                               4.70         2.93         -1.78          -37.76
std::map&lt;int,_int&gt;::upper_bound(key)_(non-existent)/8192                                            26.54        11.18        -15.36          -57.89
std::map&lt;std::string,_int&gt;::lower_bound(key)_(existent)/0                                            0.04         0.04         -0.00           -3.26
std::map&lt;std::string,_int&gt;::lower_bound(key)_(existent)/1024                                        27.46        26.25         -1.22           -4.43
std::map&lt;std::string,_int&gt;::lower_bound(key)_(existent)/32                                          19.17        15.71         -3.46          -18.07
std::map&lt;std::string,_int&gt;::lower_bound(key)_(existent)/8192                                        35.33        35.03         -0.30           -0.84
std::map&lt;std::string,_int&gt;::lower_bound(key)_(non-existent)/0                                        0.27         0.27         -0.00           -1.45
std::map&lt;std::string,_int&gt;::lower_bound(key)_(non-existent)/1024                                    24.88        24.17         -0.70           -2.83
std::map&lt;std::string,_int&gt;::lower_bound(key)_(non-existent)/32                                      11.67        11.63         -0.04           -0.32
std::map&lt;std::string,_int&gt;::lower_bound(key)_(non-existent)/8192                                    31.81        32.33          0.52            1.64
std::map&lt;std::string,_int&gt;::upper_bound(key)_(existent)/0                                            0.04         0.04         -0.00           -2.22
std::map&lt;std::string,_int&gt;::upper_bound(key)_(existent)/1024                                        29.91        26.51         -3.40          -11.38
std::map&lt;std::string,_int&gt;::upper_bound(key)_(existent)/32                                          19.69        17.74         -1.95           -9.92
std::map&lt;std::string,_int&gt;::upper_bound(key)_(existent)/8192                                        32.55        35.24          2.69            8.25
std::map&lt;std::string,_int&gt;::upper_bound(key)_(non-existent)/0                                        0.27         0.27         -0.00           -1.74
std::map&lt;std::string,_int&gt;::upper_bound(key)_(non-existent)/1024                                    23.87        26.77          2.91           12.18
std::map&lt;std::string,_int&gt;::upper_bound(key)_(non-existent)/32                                      11.44        11.81          0.37            3.24
std::map&lt;std::string,_int&gt;::upper_bound(key)_(non-existent)/8192                                    33.02        32.59         -0.43           -1.29
std::set&lt;int&gt;::lower_bound(key)_(existent)/0                                                         0.01         0.01          0.00            0.48
std::set&lt;int&gt;::lower_bound(key)_(existent)/1024                                                      7.83         4.21         -3.62          -46.23
std::set&lt;int&gt;::lower_bound(key)_(existent)/32                                                        2.74         1.68         -1.06          -38.81
std::set&lt;int&gt;::lower_bound(key)_(existent)/8192                                                     22.75        11.12        -11.63          -51.12
std::set&lt;int&gt;::lower_bound(key)_(non-existent)/0                                                     0.28         0.27         -0.01           -3.52
std::set&lt;int&gt;::lower_bound(key)_(non-existent)/1024                                                 17.15         8.40         -8.75          -51.03
std::set&lt;int&gt;::lower_bound(key)_(non-existent)/32                                                    4.63         2.50         -2.13          -46.03
std::set&lt;int&gt;::lower_bound(key)_(non-existent)/8192                                                 28.88        11.05        -17.82          -61.72
std::set&lt;int&gt;::upper_bound(key)_(existent)/0                                                         0.01         0.01         -0.00           -7.79
std::set&lt;int&gt;::upper_bound(key)_(existent)/1024                                                      7.80         3.63         -4.16          -53.42
std::set&lt;int&gt;::upper_bound(key)_(existent)/32                                                        2.81         1.90         -0.91          -32.44
std::set&lt;int&gt;::upper_bound(key)_(existent)/8192                                                     21.93        11.35        -10.58          -48.26
std::set&lt;int&gt;::upper_bound(key)_(non-existent)/0                                                     0.28         0.27         -0.01           -3.81
std::set&lt;int&gt;::upper_bound(key)_(non-existent)/1024                                                 16.76         7.38         -9.38          -55.98
std::set&lt;int&gt;::upper_bound(key)_(non-existent)/32                                                    4.58         3.10         -1.48          -32.31
std::set&lt;int&gt;::upper_bound(key)_(non-existent)/8192                                                 26.95        10.70        -16.25          -60.29
std::set&lt;std::string&gt;::lower_bound(key)_(existent)/0                                                 0.04         0.04          0.00            0.02
std::set&lt;std::string&gt;::lower_bound(key)_(existent)/1024                                             28.08        27.04         -1.04           -3.71
std::set&lt;std::string&gt;::lower_bound(key)_(existent)/32                                               17.53        16.94         -0.58           -3.34
std::set&lt;std::string&gt;::lower_bound(key)_(existent)/8192                                             32.79        33.28          0.49            1.49
std::set&lt;std::string&gt;::lower_bound(key)_(non-existent)/0                                             0.28         0.28         -0.00           -0.06
std::set&lt;std::string&gt;::lower_bound(key)_(non-existent)/1024                                         25.23        24.38         -0.85           -3.38
std::set&lt;std::string&gt;::lower_bound(key)_(non-existent)/32                                           11.45        11.68          0.24            2.07
std::set&lt;std::string&gt;::lower_bound(key)_(non-existent)/8192                                         32.30        36.80          4.50           13.95
std::set&lt;std::string&gt;::upper_bound(key)_(existent)/0                                                 0.04         0.04         -0.00           -0.14
std::set&lt;std::string&gt;::upper_bound(key)_(existent)/1024                                             26.71        26.37         -0.34           -1.27
std::set&lt;std::string&gt;::upper_bound(key)_(existent)/32                                               20.07        19.06         -1.02           -5.06
std::set&lt;std::string&gt;::upper_bound(key)_(existent)/8192                                             36.69        35.50         -1.19           -3.25
std::set&lt;std::string&gt;::upper_bound(key)_(non-existent)/0                                             0.28         0.28         -0.00           -0.16
std::set&lt;std::string&gt;::upper_bound(key)_(non-existent)/1024                                         24.48        24.90          0.42            1.73
std::set&lt;std::string&gt;::upper_bound(key)_(non-existent)/32                                           11.68        11.77          0.09            0.77
std::set&lt;std::string&gt;::upper_bound(key)_(non-existent)/8192                                         33.16        34.12          0.96            2.89

Full diff: https://github.com/llvm/llvm-project/pull/161366.diff

3 Files Affected:

  • (modified) libcxx/include/__tree (+42)
  • (modified) libcxx/include/map (+7-3)
  • (modified) libcxx/include/set (+8-4)
diff --git a/libcxx/include/__tree b/libcxx/include/__tree
index 61c910c52c536..ef53bf1fa8364 100644
--- a/libcxx/include/__tree
+++ b/libcxx/include/__tree
@@ -1165,6 +1165,48 @@ public:
   template <class _Key>
   _LIBCPP_HIDE_FROM_ABI size_type __count_multi(const _Key& __k) const;
 
+  template <bool _LowerBound, class _Key>
+  _LIBCPP_HIDE_FROM_ABI __end_node_pointer __lower_upper_bound_unique_impl(const _Key& __v) const {
+    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 = static_cast<__end_node_pointer>(__rt);
+        __rt     = static_cast<__node_pointer>(__rt->__left_);
+      } else if (__comp_res.__greater()) {
+        __rt = static_cast<__node_pointer>(__rt->__right_);
+      } else if _LIBCPP_CONSTEXPR (_LowerBound) {
+        return static_cast<__end_node_pointer>(__rt);
+      } else {
+        return __rt->__right_ ? static_cast<__end_node_pointer>(std::__tree_min(__rt->__right_)) : __result;
+      }
+    }
+    return __result;
+  }
+
+  template <class _Key>
+  _LIBCPP_HIDE_FROM_ABI iterator __lower_bound_unique(const _Key& __v) {
+    return iterator(__lower_upper_bound_unique_impl<true>(__v));
+  }
+
+  template <class _Key>
+  _LIBCPP_HIDE_FROM_ABI const_iterator __lower_bound_unique(const _Key& __v) const {
+    return const_iterator(__lower_upper_bound_unique_impl<true>(__v));
+  }
+
+  template <class _Key>
+  _LIBCPP_HIDE_FROM_ABI iterator __upper_bound_unique(const _Key& __v) {
+    return iterator(__lower_upper_bound_unique_impl<false>(__v));
+  }
+
+  template <class _Key>
+  _LIBCPP_HIDE_FROM_ABI const_iterator __upper_bound_unique(const _Key& __v) const {
+    return iterator(__lower_upper_bound_unique_impl<false>(__v));
+  }
+
   template <class _Key>
   _LIBCPP_HIDE_FROM_ABI iterator lower_bound(const _Key& __v) {
     return __lower_bound(__v, __root(), __end_node());
diff --git a/libcxx/include/map b/libcxx/include/map
index a63dfec910aae..58840556d42f5 100644
--- a/libcxx/include/map
+++ b/libcxx/include/map
@@ -1269,7 +1269,9 @@ public:
 #  endif // _LIBCPP_STD_VER >= 20
 
   _LIBCPP_HIDE_FROM_ABI iterator lower_bound(const key_type& __k) { return __tree_.lower_bound(__k); }
-  _LIBCPP_HIDE_FROM_ABI const_iterator lower_bound(const key_type& __k) const { return __tree_.lower_bound(__k); }
+  _LIBCPP_HIDE_FROM_ABI const_iterator lower_bound(const key_type& __k) const {
+    return __tree_.__lower_bound_unique(__k);
+  }
 #  if _LIBCPP_STD_VER >= 14
   template <typename _K2, enable_if_t<__is_transparent_v<_Compare, _K2>, int> = 0>
   _LIBCPP_HIDE_FROM_ABI iterator lower_bound(const _K2& __k) {
@@ -1282,8 +1284,10 @@ public:
   }
 #  endif
 
-  _LIBCPP_HIDE_FROM_ABI iterator upper_bound(const key_type& __k) { return __tree_.upper_bound(__k); }
-  _LIBCPP_HIDE_FROM_ABI const_iterator upper_bound(const key_type& __k) const { return __tree_.upper_bound(__k); }
+  _LIBCPP_HIDE_FROM_ABI iterator upper_bound(const key_type& __k) { return __tree_.__upper_bound_unique(__k); }
+  _LIBCPP_HIDE_FROM_ABI const_iterator upper_bound(const key_type& __k) const {
+    return __tree_.__upper_bound_unique(__k);
+  }
 #  if _LIBCPP_STD_VER >= 14
   template <typename _K2, enable_if_t<__is_transparent_v<_Compare, _K2>, int> = 0>
   _LIBCPP_HIDE_FROM_ABI iterator upper_bound(const _K2& __k) {
diff --git a/libcxx/include/set b/libcxx/include/set
index 75529e7bac6ff..f77dc4821e997 100644
--- a/libcxx/include/set
+++ b/libcxx/include/set
@@ -849,8 +849,10 @@ public:
   }
 #  endif // _LIBCPP_STD_VER >= 20
 
-  _LIBCPP_HIDE_FROM_ABI iterator lower_bound(const key_type& __k) { return __tree_.lower_bound(__k); }
-  _LIBCPP_HIDE_FROM_ABI const_iterator lower_bound(const key_type& __k) const { return __tree_.lower_bound(__k); }
+  _LIBCPP_HIDE_FROM_ABI iterator lower_bound(const key_type& __k) { return __tree_.__lower_bound_unique(__k); }
+  _LIBCPP_HIDE_FROM_ABI const_iterator lower_bound(const key_type& __k) const {
+    return __tree_.__lower_bound_unique(__k);
+  }
 #  if _LIBCPP_STD_VER >= 14
   template <typename _K2, enable_if_t<__is_transparent_v<_Compare, _K2>, int> = 0>
   _LIBCPP_HIDE_FROM_ABI iterator lower_bound(const _K2& __k) {
@@ -863,8 +865,10 @@ public:
   }
 #  endif
 
-  _LIBCPP_HIDE_FROM_ABI iterator upper_bound(const key_type& __k) { return __tree_.upper_bound(__k); }
-  _LIBCPP_HIDE_FROM_ABI const_iterator upper_bound(const key_type& __k) const { return __tree_.upper_bound(__k); }
+  _LIBCPP_HIDE_FROM_ABI iterator upper_bound(const key_type& __k) { return __tree_.__upper_bound_unique(__k); }
+  _LIBCPP_HIDE_FROM_ABI const_iterator upper_bound(const key_type& __k) const {
+    return __tree_.__upper_bound_unique(__k);
+  }
 #  if _LIBCPP_STD_VER >= 14
   template <typename _K2, enable_if_t<__is_transparent_v<_Compare, _K2>, int> = 0>
   _LIBCPP_HIDE_FROM_ABI iterator upper_bound(const _K2& __k) {

Comment thread libcxx/include/__tree Outdated
Comment thread libcxx/include/set
@philnik777 philnik777 force-pushed the optimize_tree_lower_upper_bound branch from 98737ea to bf8685a Compare October 2, 2025 08:04
Comment thread libcxx/include/__tree Outdated
@philnik777 philnik777 force-pushed the optimize_tree_lower_upper_bound branch from bf8685a to 4e6971e Compare October 7, 2025 08:34
@github-actions

github-actions Bot commented Oct 7, 2025

Copy link
Copy Markdown

✅ With the latest revision this PR passed the C/C++ code formatter.

@philnik777 philnik777 force-pushed the optimize_tree_lower_upper_bound branch from 4e6971e to 7ee59ec Compare October 8, 2025 11:14
@philnik777 philnik777 merged commit 45f224d into llvm:main Oct 9, 2025
76 of 79 checks passed
akadutta pushed a commit to akadutta/llvm-project that referenced this pull request Oct 14, 2025
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
```
@ldionne

ldionne commented Jun 16, 2026

Copy link
Copy Markdown
Member

(re-posting since I previously posted before the end of the meeting by mistake, which we're not supposed to do)

For posterity, while the validity of this optimization was challenged due to the Boost.ICL issue linked, LEWG reinforced libc++'s interpretation of the Standard when it discussed LWG 4572 at the Brno meeting, confirming that this optimization is valid.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. performance

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants