From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 1720) id E0A6F3858C52; Sat, 4 Feb 2023 13:05:22 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org E0A6F3858C52 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1675515922; bh=/zwD07iPaDQJPm5VZGBDVkBB8+RValPBVVgpbrHZ12U=; h=From:To:Subject:Date:From; b=xi/4/u9U4Z6AHEk4Fg3VTmiyhts2KYlYC1J1Mcla07kWxnCVwM5VSGWWYygJ0oBkv +yYTFH++fwM99cEJ7lsr+RJ5DYR0JQLJCf0FYqiHZbr2WGp6Nyp1OswHgxaBvdfoTt nSgD5u9TiGZvcovu4Uz65/Xf1U3KedXfnLdLxTlo= MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Content-Type: text/plain; charset="utf-8" From: Francois Dumont To: gcc-cvs@gcc.gnu.org, libstdc++-cvs@gcc.gnu.org Subject: [gcc r13-5700] libstdc++: Optimize basic_string move assignment X-Act-Checkin: gcc X-Git-Author: =?utf-8?q?Fran=C3=A7ois_Dumont?= X-Git-Refname: refs/heads/master X-Git-Oldrev: 49e52115b09b477382fef6f04fd7b4d1641f902c X-Git-Newrev: 540a22d243966d1b882db26b17fe674467e2a169 Message-Id: <20230204130522.E0A6F3858C52@sourceware.org> Date: Sat, 4 Feb 2023 13:05:22 +0000 (GMT) List-Id: https://gcc.gnu.org/g:540a22d243966d1b882db26b17fe674467e2a169 commit r13-5700-g540a22d243966d1b882db26b17fe674467e2a169 Author: François Dumont Date: Wed Jan 4 19:34:21 2023 +0100 libstdc++: Optimize basic_string move assignment Since resolution of Issue 2593 [1] we can consider that equal allocators before the propagate-on-move-assignment operations will still be equal afterward. So we can extend the optimization of transfering the storage of the move-to instance to the move-from one that is currently limited to always equal allocators. [1] https://cplusplus.github.io/LWG/issue2593 libstdc++-v3/ChangeLog: * include/bits/basic_string.h (operator=(basic_string&&)): Transfer move-to storage to the move-from instance when allocators are equal. * testsuite/21_strings/basic_string/allocator/char/move_assign.cc (test04): New test case. Diff: --- libstdc++-v3/include/bits/basic_string.h | 11 +++-- .../basic_string/allocator/char/move_assign.cc | 52 ++++++++++++++++++++++ 2 files changed, 57 insertions(+), 6 deletions(-) diff --git a/libstdc++-v3/include/bits/basic_string.h b/libstdc++-v3/include/bits/basic_string.h index aa018262c98..c81dc0d425a 100644 --- a/libstdc++-v3/include/bits/basic_string.h +++ b/libstdc++-v3/include/bits/basic_string.h @@ -844,9 +844,10 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 operator=(basic_string&& __str) noexcept(_Alloc_traits::_S_nothrow_move()) { + const bool __equal_allocs = _Alloc_traits::_S_always_equal() + || _M_get_allocator() == __str._M_get_allocator(); if (!_M_is_local() && _Alloc_traits::_S_propagate_on_move_assign() - && !_Alloc_traits::_S_always_equal() - && _M_get_allocator() != __str._M_get_allocator()) + && !__equal_allocs) { // Destroy existing storage before replacing allocator. _M_destroy(_M_allocated_capacity); @@ -868,16 +869,14 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 _M_set_length(__str.size()); } } - else if (_Alloc_traits::_S_propagate_on_move_assign() - || _Alloc_traits::_S_always_equal() - || _M_get_allocator() == __str._M_get_allocator()) + else if (_Alloc_traits::_S_propagate_on_move_assign() || __equal_allocs) { // Just move the allocated pointer, our allocator can free it. pointer __data = nullptr; size_type __capacity; if (!_M_is_local()) { - if (_Alloc_traits::_S_always_equal()) + if (__equal_allocs) { // __str can reuse our existing storage. __data = _M_data(); diff --git a/libstdc++-v3/testsuite/21_strings/basic_string/allocator/char/move_assign.cc b/libstdc++-v3/testsuite/21_strings/basic_string/allocator/char/move_assign.cc index cc58348e116..21e0b1cb4f4 100644 --- a/libstdc++-v3/testsuite/21_strings/basic_string/allocator/char/move_assign.cc +++ b/libstdc++-v3/testsuite/21_strings/basic_string/allocator/char/move_assign.cc @@ -28,6 +28,8 @@ const C c = 'a'; using traits = std::char_traits; using __gnu_test::propagating_allocator; +using __gnu_test::tracker_allocator_counter; +using __gnu_test::tracker_allocator; void test01() { @@ -146,10 +148,60 @@ void test03() VERIFY(7 == v8.get_allocator().get_personality()); } +void test04() +{ + typedef propagating_allocator> alloc_type; + typedef std::basic_string test_type; + + { + tracker_allocator_counter::reset(); + test_type v1(alloc_type(1)); + v1 = "abcdefghijklmnopqr10"; + auto ref_alloc_count = tracker_allocator_counter::get_allocation_count(); + + test_type v2(alloc_type(2)); + v2 = "abcdefghijklmnopqr20"; + v2 = std::move(v1); + VERIFY(1 == v1.get_allocator().get_personality()); + VERIFY(1 == v2.get_allocator().get_personality()); + + VERIFY( tracker_allocator_counter::get_allocation_count() == 2 * ref_alloc_count ); + VERIFY( tracker_allocator_counter::get_deallocation_count() == ref_alloc_count ); + + v1 = "abcdefghijklmnopqr11"; + + VERIFY( tracker_allocator_counter::get_allocation_count() == 3 * ref_alloc_count ); + } + + { + tracker_allocator_counter::reset(); + test_type v1(alloc_type(1)); + v1 = "abcdefghijklmnopqr10"; + auto ref_alloc_count = tracker_allocator_counter::get_allocation_count(); + + test_type v2(alloc_type(1)); + v2 = "abcdefghijklmnopqr20"; + v2 = std::move(v1); + VERIFY(1 == v1.get_allocator().get_personality()); + VERIFY(1 == v2.get_allocator().get_personality()); + + VERIFY( tracker_allocator_counter::get_allocation_count() == 2 * ref_alloc_count ); + VERIFY( tracker_allocator_counter::get_deallocation_count() == 0 ); + + v1 = "abcdefghijklmnopqr11"; + + VERIFY( tracker_allocator_counter::get_allocation_count() == 2 * ref_alloc_count ); + } + + VERIFY( tracker_allocator_counter::get_allocation_count() == + tracker_allocator_counter::get_deallocation_count() ); +} + int main() { test01(); test02(); test03(); + test04(); return 0; }