ZOn Tue, 15 Oct 2024, Jonathan Wakely wrote:
> Tested x86_64-linux.
>
> -- >8 --
>
> There doesn't seem to be a lot of benefit in reusing __find_if with
> __gnu_cxx::__ops predicates, since they aren't going to actually
> instantiate any less code if we use different predicates every time
> (e.g. __ops::__negate, or __ops::__iter_equals_val, or
> __ops::__pred_iter).
>
> And now that std::find no longer calls __find_if (because it just does a
> loop directly), we can make the _Iter_equals_val case of __find_if call
> std::find, to take advantage of its memchr optimization. This benefits
> other algos like search_n which use __find_if with _Iter_equals_val.
Makes sense, LGTM
>
> libstdc++-v3/ChangeLog:
>
> * include/bits/stl_algo.h (__find_if_not): Do loop here instead
> of using __find_if with __gnu_cxx::__ops predicate.
> (find_if): Likewise.
> (find): Move to ...
> * include/bits/stl_algobase.h (find): ... here.
> (__find_if): Overload for _Iter_equals_val predicate.
> ---
> libstdc++-v3/include/bits/stl_algo.h | 63 +++---------------------
> libstdc++-v3/include/bits/stl_algobase.h | 61 +++++++++++++++++++++++
> 2 files changed, 68 insertions(+), 56 deletions(-)
>
> diff --git a/libstdc++-v3/include/bits/stl_algo.h
> b/libstdc++-v3/include/bits/stl_algo.h
> index 489ce7e14d2..05c1dbd07b6 100644
> --- a/libstdc++-v3/include/bits/stl_algo.h
> +++ b/libstdc++-v3/include/bits/stl_algo.h
> @@ -105,15 +105,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> std::iter_swap(__result, __b);
> }
>
> - /// Provided for stable_partition to use.
> + // Used by std::find_if_not and __stable_partition.
> template<typename _InputIterator, typename _Predicate>
> _GLIBCXX20_CONSTEXPR
> inline _InputIterator
> __find_if_not(_InputIterator __first, _InputIterator __last,
> _Predicate __pred)
> {
> - return std::__find_if(__first, __last,
> - __gnu_cxx::__ops::__negate(__pred));
> + while (__first != __last && __pred(__first))
> + ++__first;
> + return __first;
> }
>
> /// Like find_if_not(), but uses and updates a count of the
> @@ -3810,57 +3811,6 @@ _GLIBCXX_BEGIN_NAMESPACE_ALGO
> }
> #endif // C++17
>
> - /**
> - * @brief Find the first occurrence of a value in a sequence.
> - * @ingroup non_mutating_algorithms
> - * @param __first An input iterator.
> - * @param __last An input iterator.
> - * @param __val The value to find.
> - * @return The first iterator @c i in the range @p [__first,__last)
> - * such that @c *i == @p __val, or @p __last if no such iterator exists.
> - */
> - template<typename _InputIterator, typename _Tp>
> - _GLIBCXX20_CONSTEXPR
> - inline _InputIterator
> - find(_InputIterator __first, _InputIterator __last, const _Tp& __val)
> - {
> - // concept requirements
> - __glibcxx_function_requires(_InputIteratorConcept<_InputIterator>)
> - __glibcxx_function_requires(_EqualOpConcept<
> - typename iterator_traits<_InputIterator>::value_type, _Tp>)
> - __glibcxx_requires_valid_range(__first, __last);
> -
> -#if __cpp_if_constexpr && __glibcxx_type_trait_variable_templates
> - using _ValT = typename iterator_traits<_InputIterator>::value_type;
> - if constexpr (__can_use_memchr_for_find<_ValT, _Tp>)
> - if constexpr (is_pointer_v<decltype(std::__niter_base(__first))>
> -#if __cpp_lib_concepts
> - || contiguous_iterator<_InputIterator>
> -#endif
> - )
> - {
> - // If conversion to the 1-byte value_type alters the value,
> - // it would not be found by std::find using equality comparison.
> - // We need to check this here, because otherwise something like
> - // memchr("a", 'a'+256, 1) would give a false positive match.
> - if (!(static_cast<_ValT>(__val) == __val))
> - return __last;
> - else if (!__is_constant_evaluated())
> - {
> - const void* __p0 = std::__to_address(__first);
> - const int __ival = static_cast<int>(__val);
> - if (auto __n = std::distance(__first, __last); __n > 0)
> - if (auto __p1 = __builtin_memchr(__p0, __ival, __n))
> - return __first + ((const char*)__p1 - (const char*)__p0);
> - return __last;
> - }
> - }
> -#endif
> -
> - return std::__find_if(__first, __last,
> - __gnu_cxx::__ops::__iter_equals_val(__val));
> - }
> -
> /**
> * @brief Find the first element in a sequence for which a
> * predicate is true.
> @@ -3883,8 +3833,9 @@ _GLIBCXX_BEGIN_NAMESPACE_ALGO
> typename iterator_traits<_InputIterator>::value_type>)
> __glibcxx_requires_valid_range(__first, __last);
>
> - return std::__find_if(__first, __last,
> - __gnu_cxx::__ops::__pred_iter(__pred));
> + while (__first != __last && !__pred(*__first))
> + ++__first;
> + return __first;
> }
>
> /**
> diff --git a/libstdc++-v3/include/bits/stl_algobase.h
> b/libstdc++-v3/include/bits/stl_algobase.h
> index 5f77b00be9b..34e1cf7322f 100644
> --- a/libstdc++-v3/include/bits/stl_algobase.h
> +++ b/libstdc++-v3/include/bits/stl_algobase.h
> @@ -2077,6 +2077,58 @@ _GLIBCXX_BEGIN_NAMESPACE_ALGO
> }
> #endif
>
> + /**
> + * @brief Find the first occurrence of a value in a sequence.
> + * @ingroup non_mutating_algorithms
> + * @param __first An input iterator.
> + * @param __last An input iterator.
> + * @param __val The value to find.
> + * @return The first iterator @c i in the range @p [__first,__last)
> + * such that @c *i == @p __val, or @p __last if no such iterator exists.
> + */
> + template<typename _InputIterator, typename _Tp>
> + _GLIBCXX20_CONSTEXPR
> + inline _InputIterator
> + find(_InputIterator __first, _InputIterator __last, const _Tp& __val)
> + {
> + // concept requirements
> + __glibcxx_function_requires(_InputIteratorConcept<_InputIterator>)
> + __glibcxx_function_requires(_EqualOpConcept<
> + typename iterator_traits<_InputIterator>::value_type, _Tp>)
> + __glibcxx_requires_valid_range(__first, __last);
> +
> +#if __cpp_if_constexpr && __glibcxx_type_trait_variable_templates
> + using _ValT = typename iterator_traits<_InputIterator>::value_type;
> + if constexpr (__can_use_memchr_for_find<_ValT, _Tp>)
> + if constexpr (is_pointer_v<decltype(std::__niter_base(__first))>
> +#if __cpp_lib_concepts
> + || contiguous_iterator<_InputIterator>
> +#endif
> + )
> + {
> + // If conversion to the 1-byte value_type alters the value,
> + // it would not be found by std::find using equality comparison.
> + // We need to check this here, because otherwise something like
> + // memchr("a", 'a'+256, 1) would give a false positive match.
> + if (!(static_cast<_ValT>(__val) == __val))
> + return __last;
> + else if (!__is_constant_evaluated())
> + {
> + const void* __p0 = std::__to_address(__first);
> + const int __ival = static_cast<int>(__val);
> + if (auto __n = std::distance(__first, __last); __n > 0)
> + if (auto __p1 = __builtin_memchr(__p0, __ival, __n))
> + return __first + ((const char*)__p1 - (const char*)__p0);
> + return __last;
> + }
> + }
> +#endif
> +
> + while (__first != __last && !(*__first == __val))
> + ++__first;
> + return __first;
> + }
> +
> _GLIBCXX_END_NAMESPACE_ALGO
>
> // Implementation of std::find_if, also used in std::remove_if and others.
> @@ -2091,6 +2143,15 @@ _GLIBCXX_END_NAMESPACE_ALGO
> return __first;
> }
>
> + // When the predicate is just comparing to a value we can use std::find,
> + // which is optimized to memchr for some types.
> + template<typename _Iterator, typename _Value>
> + _GLIBCXX20_CONSTEXPR
> + inline _Iterator
> + __find_if(_Iterator __first, _Iterator __last,
> + __gnu_cxx::__ops::_Iter_equals_val<_Value> __pred)
> + { return _GLIBCXX_STD_A::find(__first, __last, __pred._M_value); }
> +
> template<typename _InputIterator, typename _Predicate>
> _GLIBCXX20_CONSTEXPR
> typename iterator_traits<_InputIterator>::difference_type
> --
> 2.46.2
>
>