[libcxx-commits] [libcxx] 4cc8860 - [libc++] Refactor std::equal to forward to the 3-leg overload if the size of the ranges is known (#171585)

via libcxx-commits libcxx-commits at lists.llvm.org
Tue Dec 23 00:04:16 PST 2025


Author: Nikolas Klauser
Date: 2025-12-23T09:04:12+01:00
New Revision: 4cc8860f8f6161a12463df5dfd6454bd18ae0520

URL: https://github.com/llvm/llvm-project/commit/4cc8860f8f6161a12463df5dfd6454bd18ae0520
DIFF: https://github.com/llvm/llvm-project/commit/4cc8860f8f6161a12463df5dfd6454bd18ae0520.diff

LOG: [libc++] Refactor std::equal to forward to the 3-leg overload if the size of the ranges is known (#171585)

This allows us to merge some optimizations common between the 3-leg
overload and the two ranges overload.

In some cases this could also improve performance, since we avoid
checking one of the iterators if the size if the ranges is known. I
haven't been able to show any improvements though.

This is also a prerequisite for optimizing `std::search`.

Added: 
    

Modified: 
    libcxx/include/__algorithm/equal.h
    libcxx/include/__algorithm/ranges_equal.h
    libcxx/include/__bit_reference
    libcxx/test/std/utilities/memory/specialized.algorithms/buffer.h

Removed: 
    


################################################################################
diff  --git a/libcxx/include/__algorithm/equal.h b/libcxx/include/__algorithm/equal.h
index 22f5fe6282b1d..957cc29759424 100644
--- a/libcxx/include/__algorithm/equal.h
+++ b/libcxx/include/__algorithm/equal.h
@@ -160,22 +160,28 @@ template <class _Cp,
           bool _IsConst1,
           bool _IsConst2,
           class _BinaryPredicate,
-          __enable_if_t<__desugars_to_v<__equal_tag, _BinaryPredicate, bool, bool>, int> = 0>
+          class _Proj1,
+          class _Proj2,
+          __enable_if_t<__is_identity<_Proj1>::value && __is_identity<_Proj2>::value &&
+                            __desugars_to_v<__equal_tag, _BinaryPredicate, bool, bool>,
+                        int> = 0>
 [[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool __equal_iter_impl(
     __bit_iterator<_Cp, _IsConst1> __first1,
     __bit_iterator<_Cp, _IsConst1> __last1,
     __bit_iterator<_Cp, _IsConst2> __first2,
-    _BinaryPredicate) {
+    _BinaryPredicate,
+    _Proj1&,
+    _Proj2&) {
   if (__first1.__ctz_ == __first2.__ctz_)
     return std::__equal_aligned(__first1, __last1, __first2);
   return std::__equal_unaligned(__first1, __last1, __first2);
 }
 
-template <class _InputIterator1, class _InputIterator2, class _BinaryPredicate>
+template <class _InIter1, class _Sent1, class _InIter2, class _Pred, class _Proj1, class _Proj2>
 [[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool __equal_iter_impl(
-    _InputIterator1 __first1, _InputIterator1 __last1, _InputIterator2 __first2, _BinaryPredicate& __pred) {
+    _InIter1 __first1, _Sent1 __last1, _InIter2 __first2, _Pred& __pred, _Proj1& __proj1, _Proj2& __proj2) {
   for (; __first1 != __last1; ++__first1, (void)++__first2)
-    if (!__pred(*__first1, *__first2))
+    if (!std::__invoke(__pred, std::__invoke(__proj1, *__first1), std::__invoke(__proj2, *__first2)))
       return false;
   return true;
 }
@@ -183,19 +189,23 @@ template <class _InputIterator1, class _InputIterator2, class _BinaryPredicate>
 template <class _Tp,
           class _Up,
           class _BinaryPredicate,
-          __enable_if_t<__desugars_to_v<__equal_tag, _BinaryPredicate, _Tp, _Up> && !is_volatile<_Tp>::value &&
+          class _Proj1,
+          class _Proj2,
+          __enable_if_t<__is_identity<_Proj1>::value && __is_identity<_Proj2>::value &&
+                            __desugars_to_v<__equal_tag, _BinaryPredicate, _Tp, _Up> && !is_volatile<_Tp>::value &&
                             !is_volatile<_Up>::value && __is_trivially_equality_comparable_v<_Tp, _Up>,
                         int> = 0>
 [[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool
-__equal_iter_impl(_Tp* __first1, _Tp* __last1, _Up* __first2, _BinaryPredicate&) {
+__equal_iter_impl(_Tp* __first1, _Tp* __last1, _Up* __first2, _BinaryPredicate&, _Proj1&, _Proj2&) {
   return std::__constexpr_memcmp_equal(__first1, __first2, __element_count(__last1 - __first1));
 }
 
 template <class _InputIterator1, class _InputIterator2, class _BinaryPredicate>
 [[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool
 equal(_InputIterator1 __first1, _InputIterator1 __last1, _InputIterator2 __first2, _BinaryPredicate __pred) {
+  __identity __proj;
   return std::__equal_iter_impl(
-      std::__unwrap_iter(__first1), std::__unwrap_iter(__last1), std::__unwrap_iter(__first2), __pred);
+      std::__unwrap_iter(__first1), std::__unwrap_iter(__last1), std::__unwrap_iter(__first2), __pred, __proj, __proj);
 }
 
 template <class _InputIterator1, class _InputIterator2>
@@ -206,52 +216,28 @@ equal(_InputIterator1 __first1, _InputIterator1 __last1, _InputIterator2 __first
 
 #if _LIBCPP_STD_VER >= 14
 
-template <class _Iter1, class _Sent1, class _Iter2, class _Sent2, class _Pred, class _Proj1, class _Proj2>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool __equal_impl(
-    _Iter1 __first1, _Sent1 __last1, _Iter2 __first2, _Sent2 __last2, _Pred& __comp, _Proj1& __proj1, _Proj2& __proj2) {
-  while (__first1 != __last1 && __first2 != __last2) {
-    if (!std::__invoke(__comp, std::__invoke(__proj1, *__first1), std::__invoke(__proj2, *__first2)))
-      return false;
-    ++__first1;
-    ++__first2;
-  }
-  return __first1 == __last1 && __first2 == __last2;
-}
-
-template <class _Tp,
-          class _Up,
-          class _Pred,
-          class _Proj1,
-          class _Proj2,
-          __enable_if_t<__desugars_to_v<__equal_tag, _Pred, _Tp, _Up> && __is_identity<_Proj1>::value &&
-                            __is_identity<_Proj2>::value && !is_volatile<_Tp>::value && !is_volatile<_Up>::value &&
-                            __is_trivially_equality_comparable_v<_Tp, _Up>,
-                        int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool
-__equal_impl(_Tp* __first1, _Tp* __last1, _Up* __first2, _Up*, _Pred&, _Proj1&, _Proj2&) {
-  return std::__constexpr_memcmp_equal(__first1, __first2, __element_count(__last1 - __first1));
-}
-
-template <class _Cp,
-          bool _IsConst1,
-          bool _IsConst2,
+template <bool __known_equal_length,
+          class _Iter1,
+          class _Sent1,
+          class _Iter2,
+          class _Sent2,
           class _Pred,
           class _Proj1,
-          class _Proj2,
-          __enable_if_t<__desugars_to_v<__equal_tag, _Pred, bool, bool> && __is_identity<_Proj1>::value &&
-                            __is_identity<_Proj2>::value,
-                        int> = 0>
+          class _Proj2>
 [[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool __equal_impl(
-    __bit_iterator<_Cp, _IsConst1> __first1,
-    __bit_iterator<_Cp, _IsConst1> __last1,
-    __bit_iterator<_Cp, _IsConst2> __first2,
-    __bit_iterator<_Cp, _IsConst2>,
-    _Pred&,
-    _Proj1&,
-    _Proj2&) {
-  if (__first1.__ctz_ == __first2.__ctz_)
-    return std::__equal_aligned(__first1, __last1, __first2);
-  return std::__equal_unaligned(__first1, __last1, __first2);
+    _Iter1 __first1, _Sent1 __last1, _Iter2 __first2, _Sent2 __last2, _Pred& __comp, _Proj1& __proj1, _Proj2& __proj2) {
+  if constexpr (__known_equal_length) {
+    return std::__equal_iter_impl(
+        std::move(__first1), std::move(__last1), std::move(__first2), __comp, __proj1, __proj2);
+  } else {
+    while (__first1 != __last1 && __first2 != __last2) {
+      if (!std::__invoke(__comp, std::__invoke(__proj1, *__first1), std::__invoke(__proj2, *__first2)))
+        return false;
+      ++__first1;
+      ++__first2;
+    }
+    return __first1 == __last1 && __first2 == __last2;
+  }
 }
 
 template <class _InputIterator1, class _InputIterator2, class _BinaryPredicate>
@@ -261,13 +247,15 @@ equal(_InputIterator1 __first1,
       _InputIterator2 __first2,
       _InputIterator2 __last2,
       _BinaryPredicate __pred) {
-  if constexpr (__has_random_access_iterator_category<_InputIterator1>::value &&
-                __has_random_access_iterator_category<_InputIterator2>::value) {
+  static constexpr bool __both_random_access =
+      __has_random_access_iterator_category<_InputIterator1>::value &&
+      __has_random_access_iterator_category<_InputIterator2>::value;
+  if constexpr (__both_random_access) {
     if (std::distance(__first1, __last1) != std::distance(__first2, __last2))
       return false;
   }
   __identity __proj;
-  return std::__equal_impl(
+  return std::__equal_impl<__both_random_access>(
       std::__unwrap_iter(__first1),
       std::__unwrap_iter(__last1),
       std::__unwrap_iter(__first2),

diff  --git a/libcxx/include/__algorithm/ranges_equal.h b/libcxx/include/__algorithm/ranges_equal.h
index c26d13f002201..8eb2fc1017f28 100644
--- a/libcxx/include/__algorithm/ranges_equal.h
+++ b/libcxx/include/__algorithm/ranges_equal.h
@@ -13,13 +13,12 @@
 #include <__algorithm/unwrap_range.h>
 #include <__config>
 #include <__functional/identity.h>
-#include <__functional/invoke.h>
 #include <__functional/ranges_operations.h>
 #include <__iterator/concepts.h>
-#include <__iterator/distance.h>
 #include <__iterator/indirectly_comparable.h>
 #include <__ranges/access.h>
 #include <__ranges/concepts.h>
+#include <__ranges/size.h>
 #include <__utility/move.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -51,20 +50,17 @@ struct __equal {
       _Pred __pred   = {},
       _Proj1 __proj1 = {},
       _Proj2 __proj2 = {}) const {
-    if constexpr (sized_sentinel_for<_Sent1, _Iter1> && sized_sentinel_for<_Sent2, _Iter2>) {
+    static constexpr bool __both_sized = sized_sentinel_for<_Sent1, _Iter1> && sized_sentinel_for<_Sent2, _Iter2>;
+    if constexpr (__both_sized) {
       if (__last1 - __first1 != __last2 - __first2)
         return false;
     }
-    auto __unwrapped1 = std::__unwrap_range(std::move(__first1), std::move(__last1));
-    auto __unwrapped2 = std::__unwrap_range(std::move(__first2), std::move(__last2));
-    return std::__equal_impl(
-        std::move(__unwrapped1.first),
-        std::move(__unwrapped1.second),
-        std::move(__unwrapped2.first),
-        std::move(__unwrapped2.second),
-        __pred,
-        __proj1,
-        __proj2);
+
+    auto [__ufirst1, __ulast1] = std::__unwrap_range(std::move(__first1), std::move(__last1));
+    auto [__ufirst2, __ulast2] = std::__unwrap_range(std::move(__first2), std::move(__last2));
+
+    return std::__equal_impl<__both_sized>(
+        std::move(__ufirst1), std::move(__ulast1), std::move(__ufirst2), std::move(__ulast2), __pred, __proj1, __proj2);
   }
 
   template <input_range _Range1,
@@ -75,21 +71,16 @@ struct __equal {
     requires indirectly_comparable<iterator_t<_Range1>, iterator_t<_Range2>, _Pred, _Proj1, _Proj2>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr bool operator()(
       _Range1&& __range1, _Range2&& __range2, _Pred __pred = {}, _Proj1 __proj1 = {}, _Proj2 __proj2 = {}) const {
-    if constexpr (sized_range<_Range1> && sized_range<_Range2>) {
-      if (ranges::distance(__range1) != ranges::distance(__range2))
+    static constexpr bool __both_sized = sized_range<_Range1> && sized_range<_Range2>;
+    if constexpr (__both_sized) {
+      if (ranges::size(__range1) != ranges::size(__range2))
         return false;
     }
-    auto __unwrapped1 = std::__unwrap_range(ranges::begin(__range1), ranges::end(__range1));
-    auto __unwrapped2 = std::__unwrap_range(ranges::begin(__range2), ranges::end(__range2));
-    return std::__equal_impl(
-        std::move(__unwrapped1.first),
-        std::move(__unwrapped1.second),
-        std::move(__unwrapped2.first),
-        std::move(__unwrapped2.second),
-        __pred,
-        __proj1,
-        __proj2);
-    return false;
+
+    auto [__ufirst1, __ulast1] = std::__unwrap_range(ranges::begin(__range1), ranges::end(__range1));
+    auto [__ufirst2, __ulast2] = std::__unwrap_range(ranges::begin(__range2), ranges::end(__range2));
+    return std::__equal_impl<__both_sized>(
+        std::move(__ufirst1), std::move(__ulast1), std::move(__ufirst2), std::move(__ulast2), __pred, __proj1, __proj2);
   }
 };
 

diff  --git a/libcxx/include/__bit_reference b/libcxx/include/__bit_reference
index 7008252cab2b7..8daf3a2baafcd 100644
--- a/libcxx/include/__bit_reference
+++ b/libcxx/include/__bit_reference
@@ -509,10 +509,20 @@ private:
             bool _IsConst1,
             bool _IsConst2,
             class _BinaryPredicate,
-            __enable_if_t<__desugars_to_v<__equal_tag, _BinaryPredicate, bool, bool>, int> >
+            class _Proj1,
+            class _Proj2,
+            __enable_if_t<__is_identity<_Proj1>::value && __is_identity<_Proj2>::value &&
+                              __desugars_to_v<__equal_tag, _BinaryPredicate, bool, bool>,
+                          int> >
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 friend bool __equal_iter_impl(
-      __bit_iterator<_Dp, _IsConst1>, __bit_iterator<_Dp, _IsConst1>, __bit_iterator<_Dp, _IsConst2>, _BinaryPredicate);
-  template <class _Dp,
+      __bit_iterator<_Dp, _IsConst1>,
+      __bit_iterator<_Dp, _IsConst1>,
+      __bit_iterator<_Dp, _IsConst2>,
+      _BinaryPredicate,
+      _Proj1&,
+      _Proj2&);
+  template <bool,
+            class _Dp,
             bool _IsConst1,
             bool _IsConst2,
             class _Pred,

diff  --git a/libcxx/test/std/utilities/memory/specialized.algorithms/buffer.h b/libcxx/test/std/utilities/memory/specialized.algorithms/buffer.h
index 1114fdc68e724..072c0546cd7be 100644
--- a/libcxx/test/std/utilities/memory/specialized.algorithms/buffer.h
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/buffer.h
@@ -19,7 +19,7 @@ struct Buffer {
   const T* cbegin() const { return reinterpret_cast<const T*>(buffer); }
   const T* cend() const { return cbegin() + N; }
 
-  constexpr int size() const { return N; }
+  constexpr unsigned size() const { return N; }
 };
 
 #endif // LIBCPP_TEST_STD_UTILITIES_MEMORY_SPECIALIZED_ALGORITHMS_BUFFER_H


        


More information about the libcxx-commits mailing list