[libcxx-commits] [libcxx] [libc++] __key_equiv is sometimes unused, sometimes 2x expensive (PR #175087)
via libcxx-commits
libcxx-commits at lists.llvm.org
Fri Jan 9 09:08:25 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] [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_;
};
More information about the libcxx-commits
mailing list