[libcxx-commits] [libcxx] [libc++] __key_equiv is sometimes 2x expensive (PR #175087)

via libcxx-commits libcxx-commits at lists.llvm.org
Fri Jan 9 09:44:00 PST 2026


https://github.com/halbi2 updated https://github.com/llvm/llvm-project/pull/175087

>From 69c510bfabd00fd38e83b8384972f3968129dbe3 Mon Sep 17 00:00:00 2001
From: halbi2 <hehiralbi at gmail.com>
Date: Thu, 8 Jan 2026 17:57:22 -0500
Subject: [PATCH 1/3] [libc++] __key_equiv is sometimes 2x expensive

Rename to __key_not_less. Rename and comment on __unique
to __unchecked_unique: ranges::unique has UB but internal
function helper __unchecked_unique does not have UB.

Fixes #175086
---
 libcxx/include/__algorithm/ranges_unique.h  |  4 ++--
 libcxx/include/__algorithm/unique.h         |  5 +++--
 libcxx/include/__cxx03/__algorithm/unique.h |  5 +++--
 libcxx/include/__flat_map/flat_map.h        | 16 ++++++++--------
 libcxx/include/__flat_set/flat_set.h        | 16 ++++++++--------
 5 files changed, 24 insertions(+), 22 deletions(-)

diff --git a/libcxx/include/__algorithm/ranges_unique.h b/libcxx/include/__algorithm/ranges_unique.h
index a817359abd889..b5be51100eed4 100644
--- a/libcxx/include/__algorithm/ranges_unique.h
+++ b/libcxx/include/__algorithm/ranges_unique.h
@@ -48,7 +48,7 @@ struct __unique {
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr subrange<_Iter>
   operator()(_Iter __first, _Sent __last, _Comp __comp = {}, _Proj __proj = {}) const {
     auto __ret =
-        std::__unique<_RangeAlgPolicy>(std::move(__first), std::move(__last), std::__make_projected(__comp, __proj));
+        std::__unchecked_unique<_RangeAlgPolicy>(std::move(__first), std::move(__last), std::__make_projected(__comp, __proj));
     return {std::move(__ret.first), std::move(__ret.second)};
   }
 
@@ -58,7 +58,7 @@ struct __unique {
     requires permutable<iterator_t<_Range>>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr borrowed_subrange_t<_Range>
   operator()(_Range&& __range, _Comp __comp = {}, _Proj __proj = {}) const {
-    auto __ret = std::__unique<_RangeAlgPolicy>(
+    auto __ret = std::__unchecked_unique<_RangeAlgPolicy>(
         ranges::begin(__range), ranges::end(__range), std::__make_projected(__comp, __proj));
     return {std::move(__ret.first), std::move(__ret.second)};
   }
diff --git a/libcxx/include/__algorithm/unique.h b/libcxx/include/__algorithm/unique.h
index 307c424a7c2fb..155a4ed20b18e 100644
--- a/libcxx/include/__algorithm/unique.h
+++ b/libcxx/include/__algorithm/unique.h
@@ -29,9 +29,10 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 // unique
 
+// For this unchecked algorithm, __pred does not need to be an equivalence relation.
 template <class _AlgPolicy, class _Iter, class _Sent, class _BinaryPredicate>
 [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 std::pair<_Iter, _Iter>
-__unique(_Iter __first, _Sent __last, _BinaryPredicate&& __pred) {
+__unchecked_unique(_Iter __first, _Sent __last, _BinaryPredicate&& __pred) {
   __identity __proj;
   __first = std::__adjacent_find(__first, __last, __pred, __proj);
   if (__first != __last) {
@@ -50,7 +51,7 @@ __unique(_Iter __first, _Sent __last, _BinaryPredicate&& __pred) {
 template <class _ForwardIterator, class _BinaryPredicate>
 [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _ForwardIterator
 unique(_ForwardIterator __first, _ForwardIterator __last, _BinaryPredicate __pred) {
-  return std::__unique<_ClassicAlgPolicy>(std::move(__first), std::move(__last), __pred).first;
+  return std::__unchecked_unique<_ClassicAlgPolicy>(std::move(__first), std::move(__last), __pred).first;
 }
 
 template <class _ForwardIterator>
diff --git a/libcxx/include/__cxx03/__algorithm/unique.h b/libcxx/include/__cxx03/__algorithm/unique.h
index 15980b93b4be3..309c4b3a954a1 100644
--- a/libcxx/include/__cxx03/__algorithm/unique.h
+++ b/libcxx/include/__cxx03/__algorithm/unique.h
@@ -28,9 +28,10 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 // unique
 
+// For this unchecked algorithm, __pred does not need to be an equivalence relation.
 template <class _AlgPolicy, class _Iter, class _Sent, class _BinaryPredicate>
 _LIBCPP_NODISCARD _LIBCPP_HIDE_FROM_ABI std::pair<_Iter, _Iter>
-__unique(_Iter __first, _Sent __last, _BinaryPredicate&& __pred) {
+__unchecked_unique(_Iter __first, _Sent __last, _BinaryPredicate&& __pred) {
   __first = std::__adjacent_find(__first, __last, __pred);
   if (__first != __last) {
     // ...  a  a  ?  ...
@@ -48,7 +49,7 @@ __unique(_Iter __first, _Sent __last, _BinaryPredicate&& __pred) {
 template <class _ForwardIterator, class _BinaryPredicate>
 _LIBCPP_NODISCARD _LIBCPP_HIDE_FROM_ABI _ForwardIterator
 unique(_ForwardIterator __first, _ForwardIterator __last, _BinaryPredicate __pred) {
-  return std::__unique<_ClassicAlgPolicy>(std::move(__first), std::move(__last), __pred).first;
+  return std::__unchecked_unique<_ClassicAlgPolicy>(std::move(__first), std::move(__last), __pred).first;
 }
 
 template <class _ForwardIterator>
diff --git a/libcxx/include/__flat_map/flat_map.h b/libcxx/include/__flat_map/flat_map.h
index 50487cada24c0..10cebc47919ef 100644
--- a/libcxx/include/__flat_map/flat_map.h
+++ b/libcxx/include/__flat_map/flat_map.h
@@ -17,8 +17,8 @@
 #include <__algorithm/ranges_equal.h>
 #include <__algorithm/ranges_inplace_merge.h>
 #include <__algorithm/ranges_sort.h>
-#include <__algorithm/ranges_unique.h>
 #include <__algorithm/remove_if.h>
+#include <__algorithm/unique.h>
 #include <__algorithm/upper_bound.h>
 #include <__assert>
 #include <__compare/synth_three_way.h>
@@ -944,7 +944,7 @@ class flat_map {
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __sort_and_unique() {
     auto __zv = ranges::views::zip(__containers_.keys, __containers_.values);
     ranges::sort(__zv, __compare_, [](const auto& __p) -> decltype(auto) { return std::get<0>(__p); });
-    auto __dup_start = ranges::unique(__zv, __key_equiv(__compare_)).begin();
+    auto __dup_start = std::__unchecked_unique<_RangeAlgPolicy>(__zv, __key_not_less(__compare_)).begin();
     auto __dist      = ranges::distance(__zv.begin(), __dup_start);
     __containers_.keys.erase(__containers_.keys.begin() + __dist, __containers_.keys.end());
     __containers_.values.erase(__containers_.values.begin() + __dist, __containers_.values.end());
@@ -979,7 +979,7 @@ class flat_map {
       }
       ranges::inplace_merge(__zv.begin(), __zv.begin() + __append_start_offset, __end, __compare_key);
 
-      auto __dup_start = ranges::unique(__zv, __key_equiv(__compare_)).begin();
+      auto __dup_start = std::__unchecked_unique<_RangeAlgPolicy>(__zv, __key_not_less(__compare_)).begin();
       auto __dist      = ranges::distance(__zv.begin(), __dup_start);
       __containers_.keys.erase(__containers_.keys.begin() + __dist, __containers_.keys.end());
       __containers_.values.erase(__containers_.values.begin() + __dist, __containers_.values.end());
@@ -1135,11 +1135,11 @@ class flat_map {
   containers __containers_;
   _LIBCPP_NO_UNIQUE_ADDRESS key_compare __compare_;
 
-  struct __key_equiv {
-    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __key_equiv(key_compare __c) : __comp_(__c) {}
-    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool
-    operator()(const_reference __x, const_reference __y) const {
-      return !__comp_(std::get<0>(__x), std::get<0>(__y)) && !__comp_(std::get<0>(__y), std::get<0>(__x));
+  struct __key_not_less {
+    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __key_not_less(key_compare __c) : __comp_(__c) {}
+    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool operator()(const auto& __x, const auto& __y) const {
+      // __x is less or equal to __y because the range is already sorted
+      return !__comp_(std::get<0>(__x), std::get<0>(__y));
     }
     key_compare __comp_;
   };
diff --git a/libcxx/include/__flat_set/flat_set.h b/libcxx/include/__flat_set/flat_set.h
index 57c3926e33b68..a9079616a2b83 100644
--- a/libcxx/include/__flat_set/flat_set.h
+++ b/libcxx/include/__flat_set/flat_set.h
@@ -16,8 +16,8 @@
 #include <__algorithm/ranges_equal.h>
 #include <__algorithm/ranges_inplace_merge.h>
 #include <__algorithm/ranges_sort.h>
-#include <__algorithm/ranges_unique.h>
 #include <__algorithm/remove_if.h>
+#include <__algorithm/unique.h>
 #include <__algorithm/upper_bound.h>
 #include <__assert>
 #include <__compare/synth_three_way.h>
@@ -688,7 +688,7 @@ class flat_set {
   // is no invariant state to preserve
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __sort_and_unique() {
     ranges::sort(__keys_, __compare_);
-    auto __dup_start = ranges::unique(__keys_, __key_equiv(__compare_)).begin();
+    auto __dup_start = std::__unchecked_unique<_RangeAlgPolicy>(__keys_, __key_not_less(__compare_)).begin();
     __keys_.erase(__dup_start, __keys_.end());
   }
 
@@ -706,7 +706,7 @@ class flat_set {
       }
       ranges::inplace_merge(__keys_.begin(), __keys_.begin() + __old_size, __keys_.end(), __compare_);
 
-      auto __dup_start = ranges::unique(__keys_, __key_equiv(__compare_)).begin();
+      auto __dup_start = std::__unchecked_unique<_RangeAlgPolicy>(__keys_, __key_not_less(__compare_)).begin();
       __keys_.erase(__dup_start, __keys_.end());
     }
     __on_failure.__complete();
@@ -781,11 +781,11 @@ class flat_set {
   _KeyContainer __keys_;
   _LIBCPP_NO_UNIQUE_ADDRESS key_compare __compare_;
 
-  struct __key_equiv {
-    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __key_equiv(key_compare __c) : __comp_(__c) {}
-    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool
-    operator()(const_reference __x, const_reference __y) const {
-      return !__comp_(__x, __y) && !__comp_(__y, __x);
+  struct __key_not_less {
+    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __key_not_less(key_compare __c) : __comp_(__c) {}
+    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool operator()(const auto& __x, const auto& __y) const {
+      // __x is less or equal to __y because the range is already sorted
+      return !__comp_(__x, __y);
     }
     key_compare __comp_;
   };

>From 3a4c30b0abe0507cac7a251023ea01a9c92632c6 Mon Sep 17 00:00:00 2001
From: halbi2 <hehiralbi at gmail.com>
Date: Fri, 9 Jan 2026 12:11:00 -0500
Subject: [PATCH 2/3] fix formatting

---
 libcxx/include/__algorithm/ranges_unique.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__algorithm/ranges_unique.h b/libcxx/include/__algorithm/ranges_unique.h
index b5be51100eed4..6d0dae9534472 100644
--- a/libcxx/include/__algorithm/ranges_unique.h
+++ b/libcxx/include/__algorithm/ranges_unique.h
@@ -47,8 +47,8 @@ struct __unique {
             indirect_equivalence_relation<projected<_Iter, _Proj>> _Comp = ranges::equal_to>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr subrange<_Iter>
   operator()(_Iter __first, _Sent __last, _Comp __comp = {}, _Proj __proj = {}) const {
-    auto __ret =
-        std::__unchecked_unique<_RangeAlgPolicy>(std::move(__first), std::move(__last), std::__make_projected(__comp, __proj));
+    auto __ret = std::__unchecked_unique<_RangeAlgPolicy>(
+        std::move(__first), std::move(__last), std::__make_projected(__comp, __proj));
     return {std::move(__ret.first), std::move(__ret.second)};
   }
 

>From b6930631997080e0974369675e5b21e46a677df8 Mon Sep 17 00:00:00 2001
From: halbi2 <hehiralbi at gmail.com>
Date: Fri, 9 Jan 2026 12:43:36 -0500
Subject: [PATCH 3/3] fix mistake

---
 libcxx/include/__flat_map/flat_map.h | 6 ++++--
 libcxx/include/__flat_set/flat_set.h | 6 ++++--
 2 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/__flat_map/flat_map.h b/libcxx/include/__flat_map/flat_map.h
index 10cebc47919ef..5d55bd2d4298a 100644
--- a/libcxx/include/__flat_map/flat_map.h
+++ b/libcxx/include/__flat_map/flat_map.h
@@ -944,7 +944,8 @@ class flat_map {
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __sort_and_unique() {
     auto __zv = ranges::views::zip(__containers_.keys, __containers_.values);
     ranges::sort(__zv, __compare_, [](const auto& __p) -> decltype(auto) { return std::get<0>(__p); });
-    auto __dup_start = std::__unchecked_unique<_RangeAlgPolicy>(__zv, __key_not_less(__compare_)).begin();
+    auto __dup_start = std::__unchecked_unique<_RangeAlgPolicy>(
+        __zv.begin(), __zv.end(), __key_not_less(__compare_)).begin();
     auto __dist      = ranges::distance(__zv.begin(), __dup_start);
     __containers_.keys.erase(__containers_.keys.begin() + __dist, __containers_.keys.end());
     __containers_.values.erase(__containers_.values.begin() + __dist, __containers_.values.end());
@@ -979,7 +980,8 @@ class flat_map {
       }
       ranges::inplace_merge(__zv.begin(), __zv.begin() + __append_start_offset, __end, __compare_key);
 
-      auto __dup_start = std::__unchecked_unique<_RangeAlgPolicy>(__zv, __key_not_less(__compare_)).begin();
+      auto __dup_start = std::__unchecked_unique<_RangeAlgPolicy>(
+          __zv.begin(), __zv.end(), __key_not_less(__compare_)).begin();
       auto __dist      = ranges::distance(__zv.begin(), __dup_start);
       __containers_.keys.erase(__containers_.keys.begin() + __dist, __containers_.keys.end());
       __containers_.values.erase(__containers_.values.begin() + __dist, __containers_.values.end());
diff --git a/libcxx/include/__flat_set/flat_set.h b/libcxx/include/__flat_set/flat_set.h
index a9079616a2b83..2790fed4f48e5 100644
--- a/libcxx/include/__flat_set/flat_set.h
+++ b/libcxx/include/__flat_set/flat_set.h
@@ -688,7 +688,8 @@ class flat_set {
   // is no invariant state to preserve
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __sort_and_unique() {
     ranges::sort(__keys_, __compare_);
-    auto __dup_start = std::__unchecked_unique<_RangeAlgPolicy>(__keys_, __key_not_less(__compare_)).begin();
+    auto __dup_start = std::__unchecked_unique<_RangeAlgPolicy>(
+        __keys_.begin(), __keys_.end(), __key_not_less(__compare_)).begin();
     __keys_.erase(__dup_start, __keys_.end());
   }
 
@@ -706,7 +707,8 @@ class flat_set {
       }
       ranges::inplace_merge(__keys_.begin(), __keys_.begin() + __old_size, __keys_.end(), __compare_);
 
-      auto __dup_start = std::__unchecked_unique<_RangeAlgPolicy>(__keys_, __key_not_less(__compare_)).begin();
+      auto __dup_start = std::__unchecked_unique<_RangeAlgPolicy>(
+          __keys_.begin(), __keys_.end(), __key_not_less(__compare_)).begin();
       __keys_.erase(__dup_start, __keys_.end());
     }
     __on_failure.__complete();



More information about the libcxx-commits mailing list