[libcxx-commits] [libcxx] [libc++] Optimize std::min_element (PR #100616)
Nikolas Klauser via libcxx-commits
libcxx-commits at lists.llvm.org
Thu Mar 27 09:09:26 PDT 2025
================
@@ -27,20 +28,115 @@ _LIBCPP_PUSH_MACROS
_LIBCPP_BEGIN_NAMESPACE_STD
+template <class _Iter, class _Sent, class = void>
+struct __ConstTimeDistance : false_type {};
+
+#if _LIBCPP_STD_VER >= 20
+
+template <class _Iter, class _Sent>
+struct __ConstTimeDistance< _Iter, _Sent, __enable_if_t< sized_sentinel_for<_Sent, _Iter> >> : true_type {};
+
+#else
+
+template <class _Iter>
+struct __ConstTimeDistance<
+ _Iter,
+ _Iter,
+ __enable_if_t< is_same<typename iterator_traits<_Iter>::iterator_category, random_access_iterator_tag>::value> >
+ : true_type {};
+
+#endif // _LIBCPP_STD_VER >= 20
+
template <class _Comp, class _Iter, class _Sent, class _Proj>
-inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 _Iter
-__min_element(_Iter __first, _Sent __last, _Comp& __comp, _Proj& __proj) {
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 _Iter __min_element(
+ _Iter __first,
+ _Sent __last,
+ _Comp __comp,
+ _Proj& __proj,
+ /*_ConstTimeDistance*/ false_type) {
if (__first == __last)
return __first;
_Iter __i = __first;
while (++__i != __last)
if (std::__invoke(__comp, std::__invoke(__proj, *__i), std::__invoke(__proj, *__first)))
__first = __i;
-
return __first;
}
+template <class _Comp, class _Iter, class _Sent, class _Proj>
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 _Iter __min_element(
+ _Iter __first,
+ _Sent __last,
+ _Comp __comp,
+ _Proj& __proj,
+ /*ConstTimeDistance*/ true_type) {
+ if (__first == __last)
+ return __first;
+
+ typedef typename std::iterator_traits<_Iter>::difference_type diff_type;
+ diff_type __n = std::distance(__first, __last);
+
+ if (__n <= 64) {
+ _Iter __i = __first;
+ while (++__i != __last)
+ if (std::__invoke(__comp, std::__invoke(__proj, *__i), std::__invoke(__proj, *__first)))
+ __first = __i;
+ return __first;
+ }
+
+ diff_type __block_size = 256;
+
+ diff_type __n_blocked = __n - (__n % __block_size);
+ _Iter __block_start = __first, __block_end = __first;
+
+ typedef typename std::iterator_traits<_Iter>::value_type value_type;
+ value_type __min_val = std::__invoke(__proj, *__first);
+
+ _Iter __curr = __first;
+ for (diff_type __i = 0; __i < __n_blocked; __i += __block_size) {
+ _Iter __start = __curr;
+ value_type __block_min = __min_val;
+ for (diff_type __j = 0; __j < __block_size; __j++) {
+ if (std::__invoke(__comp, std::__invoke(__proj, *__curr), __block_min)) {
+ __block_min = *__curr;
+ }
+ __curr++;
+ }
+ if (std::__invoke(__comp, __block_min, __min_val)) {
+ __min_val = __block_min;
+ __block_start = __start;
+ __block_end = __curr;
+ }
+ }
----------------
philnik777 wrote:
This looks like something that results in incredible amounts of code bloat when the comparator isn't known or can't be inlined. e.g. how does the performance and code size compare with `string`?
https://github.com/llvm/llvm-project/pull/100616
More information about the libcxx-commits
mailing list