From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 2181) id 0C7EE386546F; Thu, 17 Aug 2023 20:31:17 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 0C7EE386546F DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1692304277; bh=ndwc1B1IxN9gUBZM4ma6n4IMAlXIUWAnyf1n2Cn0zFg=; h=From:To:Subject:Date:From; b=BtDk+vngRPIAhtMLR2yHGiWsJKlIO+EfrsTsYoG6LER6jt05FoEn/b++sqkTiYCdO 8tbBRBBQoDaqSm0iH232cnDnT/VdvTn+1ctZ/fdMNKEHj9CLYjWMA8k6lSVLQUY+V1 ZqPVyIHyLLql3SHqnVDHqm8XHTfKBraLXhsDBXV8= MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Type: text/plain; charset="utf-8" From: Jonathan Wakely To: gcc-cvs@gcc.gnu.org, libstdc++-cvs@gcc.gnu.org Subject: [gcc r14-3306] libstdc++: Optimize std::string::assign(Iter, Iter) [PR110945] X-Act-Checkin: gcc X-Git-Author: Jonathan Wakely X-Git-Refname: refs/heads/master X-Git-Oldrev: 6cf214b4fc97f5320fff8f4d5d262ef858f34a8d X-Git-Newrev: cc3d7baf2741777e99567d4301802c99f5775619 Message-Id: <20230817203117.0C7EE386546F@sourceware.org> Date: Thu, 17 Aug 2023 20:31:17 +0000 (GMT) List-Id: https://gcc.gnu.org/g:cc3d7baf2741777e99567d4301802c99f5775619 commit r14-3306-gcc3d7baf2741777e99567d4301802c99f5775619 Author: Jonathan Wakely Date: Tue Aug 8 16:31:42 2023 +0100 libstdc++: Optimize std::string::assign(Iter, Iter) [PR110945] Calling string::assign(Iter, Iter) with "foreign" iterators (not the string's own iterator or pointer types) currently constructs a temporary string and then calls replace to copy the characters from it. That means we copy from the iterators twice, and if the replace operation has to grow the string then we also allocate twice. By using *this = basic_string(first, last, get_allocator()) we only perform a single allocation+copy and then do a cheap move assignment instead of a second copy (and possible allocation). But that alternative has to be done conditionally, so that we don't pessimize the native iterator case (the string's own iterator and pointer types) which currently select efficient overloads of replace which will not allocate at all if the string already has sufficient capacity. For C++20 we can extend that efficient case to work for any contiguous iterator with the right value type, not just for the string's native iterators. So the change is to inline the code that decides whether to work in place or to allocate+copy (instead of deciding that via overload resolution for replace), and for the allocate+copy case do a move assignment instead of another call to replace. For C++98 there is no change, as we can't do an efficient move assignment anyway, so keep the current code. We can also simplify assign(initializer_list) because the backing array for an initializer_list is always disjunct with *this, so most of the code in _M_replace is not needed. libstdc++-v3/ChangeLog: PR libstdc++/110945 * include/bits/basic_string.h (basic_string::assign(Iter, Iter)): Dispatch to _M_replace or move assignment from a temporary, based on the iterator type. Diff: --- libstdc++-v3/include/bits/basic_string.h | 42 +++++++++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/libstdc++-v3/include/bits/basic_string.h b/libstdc++-v3/include/bits/basic_string.h index f4bbf521bba7..09fd62afa665 100644 --- a/libstdc++-v3/include/bits/basic_string.h +++ b/libstdc++-v3/include/bits/basic_string.h @@ -1711,15 +1711,36 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 * Sets value of string to characters in the range [__first,__last). */ #if __cplusplus >= 201103L +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wc++17-extensions" template> _GLIBCXX20_CONSTEXPR + basic_string& + assign(_InputIterator __first, _InputIterator __last) + { +#if __cplusplus >= 202002L + if constexpr (contiguous_iterator<_InputIterator> + && is_same_v, _CharT>) +#else + if constexpr (__is_one_of<_InputIterator, const_iterator, iterator, + const _CharT*, _CharT*>::value) +#endif + { + __glibcxx_requires_valid_range(__first, __last); + return _M_replace(size_type(0), size(), + std::__to_address(__first), __last - __first); + } + else + return *this = basic_string(__first, __last, get_allocator()); + } +#pragma GCC diagnostic pop #else template + basic_string& + assign(_InputIterator __first, _InputIterator __last) + { return this->replace(begin(), end(), __first, __last); } #endif - basic_string& - assign(_InputIterator __first, _InputIterator __last) - { return this->replace(begin(), end(), __first, __last); } #if __cplusplus >= 201103L /** @@ -1730,7 +1751,20 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 _GLIBCXX20_CONSTEXPR basic_string& assign(initializer_list<_CharT> __l) - { return this->assign(__l.begin(), __l.size()); } + { + // The initializer_list array cannot alias the characters in *this + // so we don't need to use replace to that case. + const size_type __n = __l.size(); + if (__n > capacity()) + *this = basic_string(__l.begin(), __l.end(), get_allocator()); + else + { + if (__n) + _S_copy(_M_data(), __l.begin(), __n); + _M_set_length(__n); + } + return *this; + } #endif // C++11 #if __cplusplus >= 201703L