I'm just curious why there isn't an overload of __uninitialized_move_a(_Iter __first, _Iter __last, _GLIBCXX_STD_C::_Deque_iterator<_OTp, _OTp&, _OTp*> __result, ...)?
Jonathan Wakely <[email protected]> 於 2026年3月17日週二 上午7:24寫道: > I reimplemented uninitialized_copy and uninitialized_move in > r15-4473-g3abe751ea86e34 so that they no longer delegate to std::copy, > but that meant that they were no longer optimized for std::deque > iterators, leading to performance regressions for operations on a > std::deque with trivial element types. This adds new overloads of > __uninitialized_copy_a and __uninitialized_move_a to handle std::deque > iterators, restoring the lost performance. > > There are also overloads of std::fill for deque iterators which are no > longer used for std::uninitialized_fill. This does not add replacements > for those, so there will still be lost performance for std::deque > operations that depend on std::uninitialized_fill. Similarly, inserting > or assigning from istreambuf_iterator into a std::deque no longer uses > the std::copy overloads for those types, and that isn't fixed by this > patch either. > > libstdc++-v3/ChangeLog: > > PR libstdc++/124463 > * include/bits/deque.tcc (__uninitialized_copy_a): Define > overloads for input and output iterators being std::deque > iterators, and for only the output iterator being a std::deque > iterator. > (__uninitialized_move_a): Overload for input and output > iterators being std::deque iterators. > * include/bits/stl_uninitialized.h (__uninitialized_copy_a) > (__uninitialized_move_a): Declare overloads for std::deque > iterators. > --- > > I don't love this solution, but it's a quick fix for the reported > regression. For GCC 17 I'd like to add a more general segmented iterator > optimization, which all algos could opt in to generically, which > wouldn't require distinct overloads for each type of segmented iterator > (that's related to PR 123211). > > Tested x86_64-linux. > > libstdc++-v3/include/bits/deque.tcc | 94 +++++++++++++++++++ > libstdc++-v3/include/bits/stl_uninitialized.h | 27 ++++++ > 2 files changed, 121 insertions(+) > > diff --git a/libstdc++-v3/include/bits/deque.tcc > b/libstdc++-v3/include/bits/deque.tcc > index 7a690466cd58..b35f268b0db1 100644 > --- a/libstdc++-v3/include/bits/deque.tcc > +++ b/libstdc++-v3/include/bits/deque.tcc > @@ -1549,6 +1549,100 @@ _GLIBCXX_END_NAMESPACE_CONTAINER > return __last2 != __first2; > } > > +#if __cplusplus >= 201103L > +#pragma GCC diagnostic push > +#pragma GCC diagnostic ignored "-Wc++17-extensions" // if constexpr > + template<typename _ITp, typename _IRef, typename _IPtr, typename _OTp, > + typename _Tp> > + _GLIBCXX_STD_C::_Deque_iterator<_OTp, _OTp&, _OTp*> > + __uninitialized_copy_a( > + _GLIBCXX_STD_C::_Deque_iterator<_ITp, _IRef, _IPtr> __first, > + _GLIBCXX_STD_C::_Deque_iterator<_ITp, _IRef, _IPtr> __last, > + _GLIBCXX_STD_C::_Deque_iterator<_OTp, _OTp&, _OTp*> __result, > + allocator<_Tp>&) > + { > + // In order to unwind all initialized elements, we just use the > default > + // implementation if construction can throw. > + if constexpr (!__is_nothrow_constructible(_OTp, _IRef)) > + return std::__do_uninit_copy(__first, __last, __result); > + else > + while (__first != __last) > + { > + auto __from = __first._M_cur; > + ptrdiff_t __n; > + if (__first._M_node == __last._M_node) > + __n = __last._M_cur - __from; > + else > + __n = __first._M_last - __from; > + _OTp* __sres = __result._M_cur; > + __n = std::min<ptrdiff_t>(__n, __result._M_last - > __result._M_cur); > + std::uninitialized_copy(__from, __from + __n, __result._M_cur); > + __first += __n; > + __result += __n; > + } > + return __result; > + } > + > + template<typename _Iter, typename _OTp, typename _Tp> > + __enable_if_t<__is_random_access_iter<_Iter>::value, > + _GLIBCXX_STD_C::_Deque_iterator<_OTp, _OTp&, _OTp*>> > + __uninitialized_copy_a(_Iter __first, _Iter __last, > + _GLIBCXX_STD_C::_Deque_iterator<_OTp, _OTp&, _OTp*> __result, > + allocator<_Tp>&) > + { > + // In order to unwind all initialized elements, we just use the > default > + // implementation if construction can throw. > + if constexpr (!__is_nothrow_constructible(_OTp, decltype(*__first))) > + return std::__do_uninit_copy(__first, __last, __result); > + else > + while (__first != __last) > + { > + auto __n = std::min<ptrdiff_t>(__last - __first, > + __result._M_last - > __result._M_cur); > + std::uninitialized_copy(__first, __first + __n, > __result._M_cur); > + __first += __n; > + __result += __n; > + } > + return __result; > + } > + > + template<typename _ITp, typename _IRef, typename _IPtr, typename _OTp, > + typename _Tp> > + _GLIBCXX_STD_C::_Deque_iterator<_OTp, _OTp&, _OTp*> > + __uninitialized_move_a( > + _GLIBCXX_STD_C::_Deque_iterator<_ITp, _IRef, _IPtr> __first, > + _GLIBCXX_STD_C::_Deque_iterator<_ITp, _IRef, _IPtr> __last, > + _GLIBCXX_STD_C::_Deque_iterator<_OTp, _OTp&, _OTp*> __result, > + allocator<_Tp>&) > + { > + // In order to unwind all initialized elements, we just use the > default > + // implementation if construction can throw. > + if constexpr (!__is_nothrow_constructible(_OTp, > + > decltype(std::move(*__first)))) > + return std::uninitialized_copy(std::make_move_iterator(__first), > + std::make_move_iterator(__last), > + __result); > + else > + while (__first != __last) > + { > + auto __from = __first._M_cur; > + ptrdiff_t __n; > + if (__first._M_node == __last._M_node) > + __n = __last._M_cur - __from; > + else > + __n = __first._M_last - __from; > + __n = std::min<ptrdiff_t>(__n, __result._M_last - > __result._M_cur); > + std::uninitialized_copy(std::make_move_iterator(__from), > + std::make_move_iterator(__from + __n), > + __result._M_cur); > + __first += __n; > + __result += __n; > + } > + return __result; > + } > +#pragma GCC diagnostic pop > +#endif // C++11 > + > _GLIBCXX_END_NAMESPACE_VERSION > } // namespace std > > diff --git a/libstdc++-v3/include/bits/stl_uninitialized.h > b/libstdc++-v3/include/bits/stl_uninitialized.h > index 82e4ba2ff7b7..ecc24023e98f 100644 > --- a/libstdc++-v3/include/bits/stl_uninitialized.h > +++ b/libstdc++-v3/include/bits/stl_uninitialized.h > @@ -662,6 +662,33 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > } > #endif > > +#if __cplusplus >= 201103L > + template<typename _ITp, typename _IRef, typename _IPtr, typename _OTp, > + typename _Tp> > + _GLIBCXX_STD_C::_Deque_iterator<_OTp, _OTp&, _OTp*> > + __uninitialized_copy_a( > + _GLIBCXX_STD_C::_Deque_iterator<_ITp, _IRef, _IPtr> __first, > + _GLIBCXX_STD_C::_Deque_iterator<_ITp, _IRef, _IPtr> __last, > + _GLIBCXX_STD_C::_Deque_iterator<_OTp, _OTp&, _OTp*> __result, > + allocator<_Tp>&); > + > + template<typename _Iter, typename _OTp, typename _Tp> > + __enable_if_t<__is_random_access_iter<_Iter>::value, > + _GLIBCXX_STD_C::_Deque_iterator<_OTp, _OTp&, _OTp*>> > + __uninitialized_copy_a(_Iter __first, _Iter __last, > + _GLIBCXX_STD_C::_Deque_iterator<_OTp, _OTp&, _OTp*> __result, > + allocator<_Tp>&); > + > + template<typename _ITp, typename _IRef, typename _IPtr, typename _OTp, > + typename _Tp> > + _GLIBCXX_STD_C::_Deque_iterator<_OTp, _OTp&, _OTp*> > + __uninitialized_move_a( > + _GLIBCXX_STD_C::_Deque_iterator<_ITp, _IRef, _IPtr> __first, > + _GLIBCXX_STD_C::_Deque_iterator<_ITp, _IRef, _IPtr> __last, > + _GLIBCXX_STD_C::_Deque_iterator<_OTp, _OTp&, _OTp*> __result, > + allocator<_Tp>&); > +#endif > + > template<typename _InputIterator, typename _ForwardIterator, > typename _Allocator> > _GLIBCXX20_CONSTEXPR > -- > 2.53.0 > >
