[libcxx-commits] [libcxx] [libc++] Optimize search_n (PR #171389)

Louis Dionne via libcxx-commits libcxx-commits at lists.llvm.org
Wed Dec 10 07:39:34 PST 2025


================
@@ -68,43 +70,35 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 pair<_Iter, _Iter> __search_
   }
 }
 
-template <class _AlgPolicy, class _Pred, class _Iter, class _Sent, class _SizeT, class _Type, class _Proj, class _DiffT>
+template <class _AlgPolicy, class _Pred, class _Iter, class _SizeT, class _Type, class _Proj, class _DiffT>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 std::pair<_Iter, _Iter> __search_n_random_access_impl(
-    _Iter __first, _Sent __last, _SizeT __count, const _Type& __value, _Pred& __pred, _Proj& __proj, _DiffT __size1) {
-  using difference_type = typename iterator_traits<_Iter>::difference_type;
+    _Iter __first, _SizeT __count_in, const _Type& __value, _Pred& __pred, _Proj& __proj, _DiffT __size) {
+  auto __last  = __first + __size;
+  auto __count = static_cast<_DiffT>(__count_in);
+
   if (__count == 0)
     return std::make_pair(__first, __first);
-  if (__size1 < static_cast<_DiffT>(__count)) {
-    _IterOps<_AlgPolicy>::__advance_to(__first, __last);
-    return std::make_pair(__first, __first);
-  }
+  if (__size < __count)
+    return std::make_pair(__last, __last);
 
-  const auto __s = __first + __size1 - difference_type(__count - 1); // Start of pattern match can't go beyond here
+  _Iter __try_match_until = __last - __count;
+  _Iter __match_start     = __first;
+  _Iter __matched_until   = __first;
   while (true) {
-    // Find first element in sequence that matchs __value, with a mininum of loop checks
-    while (true) {
-      if (__first >= __s) { // return __last if no element matches __value
-        _IterOps<_AlgPolicy>::__advance_to(__first, __last);
-        return std::make_pair(__first, __first);
-      }
-      if (std::__invoke(__pred, std::__invoke(__proj, *__first), __value))
-        break;
-      ++__first;
-    }
-    // *__first matches __value_, now match elements after here
-    auto __m = __first;
-    _SizeT __c(0);
+    if (__match_start > __try_match_until)
+      return std::make_pair(__last, __last);
+
+    _Iter __check = __match_start + __count;
+
     while (true) {
-      if (++__c == __count) // If pattern exhausted, __first is the answer (works for 1 element pattern)
-        return std::make_pair(__first, __first + _DiffT(__count));
-      ++__m; // no need to check range on __m because __s guarantees we have enough source
+      if (__check == __matched_until)
+        return std::make_pair(__match_start, __match_start + __count);
 
-      // if there is a mismatch, restart with a new __first
-      if (!std::__invoke(__pred, std::__invoke(__proj, *__m), __value)) {
-        __first = __m;
-        ++__first;
+      if (!std::__invoke(__pred, std::__invoke(__proj, *--__check), __value)) {
----------------
ldionne wrote:

I would suggest introducing `__find_last` to clean up these two nested `while (true)` loops:

```c++
auto __not_value = [&](auto const& __x) { return !std::__invoke(__pred, __x, __value); };
auto __res = std::__find_last(__match_start, __match_start + __count, __not_value, __proj);
if (__res == __match_start + __count) {
  // we have a match
} else {
  // no match :-(
}
```

We don't necessarily have to go all the way and refactor `ranges::find_last` to use it, but at least having a named algorithm for this is an improvement.

https://github.com/llvm/llvm-project/pull/171389


More information about the libcxx-commits mailing list