This is the mail archive of the libstdc++@gcc.gnu.org mailing list for the libstdc++ project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: std::vector move assign patch


On Wed, 26 Apr 2017, François Dumont wrote:

I plan to propose this patch when back to stage 1. As it defaults default and move constructor I wonder if it can make any change to the generated code.

It helps quite a bit for move construction in some cases, particularly

  typedef std::vector<int> V;
  V f(V&&b){ return std::move(b); }

where gcc expects that RVO might let the return variable alias b. I don't know how common those cases are though.

It doesn't seem to change the generated code for move assignment. I thought _Vector_impl_data might make it a couple lines shorter to write optimal code, but that would require having an operator= (it is currently implicitly deleted) so it compensates.

For reference, code for slightly more efficient move assignment (not really tested):

      void
      _M_move_assign(vector&& __x, std::true_type) noexcept
      {
        _M_deallocate(this->_M_impl._M_start, this->_M_impl._M_end_of_storage
                      - this->_M_impl._M_start);
        this->_M_impl._M_start = __x._M_impl._M_start;
        this->_M_impl._M_finish = __x._M_impl._M_finish;
        this->_M_impl._M_end_of_storage = __x._M_impl._M_end_of_storage;
        __x._M_impl._M_start = pointer();
        __x._M_impl._M_finish = pointer();
        __x._M_impl._M_end_of_storage = pointer();
        std::__alloc_on_move(_M_get_Tp_allocator(), __x._M_get_Tp_allocator());
      }

Looking only at the code generated for

void f(V&a,V&&b){ a=std::move(b); }

it is tempting to save the arguments to _M_deallocate in temporaries and
only call it at the end: jmp instead of call on x86, less stack
manipulation.

      void
      _M_move_assign(vector&& __x, std::true_type) noexcept
      {
        pointer __ptr = this->_M_impl._M_start;
        size_t __sz = this->_M_impl._M_end_of_storage - this->_M_impl._M_start;
        this->_M_impl._M_start = __x._M_impl._M_start;
        this->_M_impl._M_finish = __x._M_impl._M_finish;
        this->_M_impl._M_end_of_storage = __x._M_impl._M_end_of_storage;
        __x._M_impl._M_start = pointer();
        __x._M_impl._M_finish = pointer();
        __x._M_impl._M_end_of_storage = pointer();
        std::__alloc_on_move(_M_get_Tp_allocator(), __x._M_get_Tp_allocator());
        _M_deallocate(__ptr, __sz);
      }

But as soon as it gets inlined in some larger function (I expect this is the usual case for vector's move assignment, and we probably don't care about performance in other cases), this advantage disappears, and it becomes unclear which is better.

--
Marc Glisse


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]