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

Louis Dionne via libcxx-commits libcxx-commits at lists.llvm.org
Thu Dec 18 08:34:30 PST 2025


================
@@ -68,44 +64,45 @@ _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 _RAIter, class _Pred, class _Proj, class _ValueT>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 _RAIter
+__find_last_not(_RAIter __first, _RAIter __last, const _ValueT& __value, _Pred& __pred, _Proj& __proj) {
+  auto __orig_last = __last;
+  while (true) {
+    if (__first == __last)
+      return __orig_last;
+
+    if (!std::__invoke(__pred, std::__invoke(__proj, *--__last), __value)) {
+      return __last;
+    }
+  }
+}
+
+template <class _AlgPolicy, class _Pred, class _Iter, class _SizeT, class _Type, class _Proj, class _DiffT>
----------------
ldionne wrote:

Notes from our discussion:

```c++
struct _Range {
  auto first;
  auto last;
};

// __known_to_match is the longest subrange where all elements are known to match at any given point in time.
// We basically try to expand it successively into a range of sufficient length. When we fail
// to do that because we find a mismatching element, we move it forward to the beginning of
// the next consecutive sequence that is not known not to match.
Range __known_to_match{__first, __first};
while (true) {
  // There's no chance of expanding the subrange into a sequence of sufficient length
  if (__known_to_match.first > __try_match_until)
   return std::make_pair(__last, __last);

  // If the last element that didn't match is `__known_to_match.first + __count`, we know that
  // `[__known_to_match.first, __known_to_match.first + __count)` is a sequence of all matching
  // elements, so we're done.
  auto __mismatch = std::__find_last_not(__known_to_match.last, __known_to_match.first + __count, __value, __pred, __proj);
  if (__mismatch == __known_to_match.first + __count)
    return std::make_pair(__known_to_match.first, __known_to_match.first + __count);

  // Otherwise, we have to move the __known_to_match window forward past the point where we know
  // for sure a match is impossible.
  __known_to_match = {__mismatch+1, __known_to_match.first + __count};
}
```

Another possibility here would be to get rid of the semantic-infused names and to instead do something like

```
//
// |----------1111111111----------|
//     ^a     ^b       ^c
//
```

With a comment and a diagram explaining what these subranges are.


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


More information about the libcxx-commits mailing list