]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
libstdc++: Optimize basic_string move assignment
authorFrançois Dumont <fdumont@gcc.gnu.org>
Wed, 4 Jan 2023 18:34:21 +0000 (19:34 +0100)
committerFrançois Dumont <fdumont@gcc.gnu.org>
Sat, 4 Feb 2023 13:03:54 +0000 (14:03 +0100)
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.

libstdc++-v3/include/bits/basic_string.h
libstdc++-v3/testsuite/21_strings/basic_string/allocator/char/move_assign.cc

index aa018262c98b6633bc347bfa75492fce38f6c631..c81dc0d425a0ae648c46f520b603971978413281 100644 (file)
@@ -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();
index cc58348e1162aa317f7f32b88240b5cdd97c7472..21e0b1cb4f44f21fa1ad7625feae8aedb055a1bf 100644 (file)
@@ -28,6 +28,8 @@ const C c = 'a';
 using traits = std::char_traits<C>;
 
 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<C, true, tracker_allocator<C>> alloc_type;
+  typedef std::basic_string<C, traits, alloc_type> 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;
 }