From 1eb88fc46f0ac1f933c2aba15c3041aae557f9d9 Mon Sep 17 00:00:00 2001 From: Marshall Clow Date: Mon, 7 Oct 2024 12:36:28 -0700 Subject: [PATCH 1/3] Optimize swap_ranges for contigious, replaceable ranges --- libcxx/include/__algorithm/swap_ranges.h | 42 ++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/libcxx/include/__algorithm/swap_ranges.h b/libcxx/include/__algorithm/swap_ranges.h index 54b453b72360e0..e3f62e371da9ea 100644 --- a/libcxx/include/__algorithm/swap_ranges.h +++ b/libcxx/include/__algorithm/swap_ranges.h @@ -40,6 +40,48 @@ __swap_ranges(_ForwardIterator1 __first1, _Sentinel1 __last1, _ForwardIterator2 template _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 pair<_ForwardIterator1, _ForwardIterator2> __swap_ranges(_ForwardIterator1 __first1, _Sentinel1 __last1, _ForwardIterator2 __first2) { +#if _LIBCPP_STD_VER >= 26 + if ! consteval { + using _V1Type = iterator_traits<_ForwardIterator1>::value_type; + using _V2Type = iterator_traits<_ForwardIterator2>::value_type; + if constexpr(__libcpp_is_contiguous_iterator<_ForwardIterator1>::value && + __libcpp_is_contiguous_iterator<_ForwardIterator2>::value && + is_same_v<_V1Type, _V2Type> && is_replaceable_v<_V1Type>) { + size_t __distance = distance(__first1, __last1); + if (__distance == 1) { + using std::swap; + swap(*__first1, *__first2); + } + else if (__distance > 0) { + size_t __numBytes = __distance * sizeof(_V1Type); + byte *__aptr = reinterpret_cast (addressof(*__first1)); + byte *__bptr = reinterpret_cast (addressof(*__first2)); + + size_t __size = __numBytes < 512 ? __numBytes : 512; + size_t __chunk = __numBytes / __size; + size_t __rem = __numBytes % __size; + + char __buffer[__size]; + if (__chunk > 0) { + for (std::size_t __n = 0; __n < __chunk; __n++, __aptr += __size, __bptr += __size) { + __builtin_memcpy(__buffer, __aptr, __size); + __builtin_memcpy(__aptr, __bptr, __size); + __builtin_memcpy(__bptr, __buffer, __size); + } + } + + if (__rem > 0) { + __builtin_memcpy(__buffer, __aptr, __rem); + __builtin_memcpy(__aptr, __bptr, __rem); + __builtin_memcpy(__bptr, __buffer, __rem); + } + } + return pair<_ForwardIterator1, _ForwardIterator2>(__first1 + __distance, __first2 + __distance); + } + } +#endif + +// else not replaceable, not contiguous, or constexpr while (__first1 != __last1) { _IterOps<_AlgPolicy>::iter_swap(__first1, __first2); ++__first1; From f9c8b3d29114a886062d10bb45b854b47052824b Mon Sep 17 00:00:00 2001 From: Marshall Clow Date: Mon, 7 Oct 2024 15:36:57 -0700 Subject: [PATCH 2/3] Add a check for trivial_relocatability --- libcxx/include/__algorithm/swap_ranges.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libcxx/include/__algorithm/swap_ranges.h b/libcxx/include/__algorithm/swap_ranges.h index e3f62e371da9ea..e94f02b45c725c 100644 --- a/libcxx/include/__algorithm/swap_ranges.h +++ b/libcxx/include/__algorithm/swap_ranges.h @@ -46,7 +46,8 @@ __swap_ranges(_ForwardIterator1 __first1, _Sentinel1 __last1, _ForwardIterator2 using _V2Type = iterator_traits<_ForwardIterator2>::value_type; if constexpr(__libcpp_is_contiguous_iterator<_ForwardIterator1>::value && __libcpp_is_contiguous_iterator<_ForwardIterator2>::value && - is_same_v<_V1Type, _V2Type> && is_replaceable_v<_V1Type>) { + is_same_v<_V1Type, _V2Type> && + is_trivially_relocatable_v<_V1Type> && is_replaceable_v<_V1Type>) { size_t __distance = distance(__first1, __last1); if (__distance == 1) { using std::swap; From 32638859460984f0e3494fcad9627c4830472cf3 Mon Sep 17 00:00:00 2001 From: Marshall Clow Date: Mon, 7 Oct 2024 16:09:17 -0700 Subject: [PATCH 3/3] Use iter_swap for one element ranges --- libcxx/include/__algorithm/swap_ranges.h | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/libcxx/include/__algorithm/swap_ranges.h b/libcxx/include/__algorithm/swap_ranges.h index e94f02b45c725c..8ddaae94b5af56 100644 --- a/libcxx/include/__algorithm/swap_ranges.h +++ b/libcxx/include/__algorithm/swap_ranges.h @@ -49,10 +49,8 @@ __swap_ranges(_ForwardIterator1 __first1, _Sentinel1 __last1, _ForwardIterator2 is_same_v<_V1Type, _V2Type> && is_trivially_relocatable_v<_V1Type> && is_replaceable_v<_V1Type>) { size_t __distance = distance(__first1, __last1); - if (__distance == 1) { - using std::swap; - swap(*__first1, *__first2); - } + if (__distance == 1) + iter_swap(__first1, __first2); else if (__distance > 0) { size_t __numBytes = __distance * sizeof(_V1Type); byte *__aptr = reinterpret_cast (addressof(*__first1));