This is the mail archive of the
libstdc++@gcc.gnu.org
mailing list for the libstdc++ project.
Re: std::vector move assign patch
- From: Marc Glisse <marc dot glisse at inria dot fr>
- To: François Dumont <frs dot dumont at gmail dot com>
- Cc: libstdc++ at gcc dot gnu dot org
- Date: Sun, 14 May 2017 13:26:26 +0200 (CEST)
- Subject: Re: std::vector move assign patch
- Authentication-results: sourceware.org; auth=none
- References: <52BDC6AD.5030207@gmail.com> <CAMe9rOok7WqKip7MAB885B_ZJonx5H3UpvUyZopiHur6W-tk2w@mail.gmail.com> <CAH6eHdSuwbuXzs0fmy4iJ6z+AUiFaKXNe1qhzzmNACnsdNKXhQ@mail.gmail.com> <alpine.DEB.2.20.1704220003540.2493@stedding.saclay.inria.fr> <20170425125253.GE5109@redhat.com> <alpine.DEB.2.20.1704251536320.2317@stedding.saclay.inria.fr> <20170425153530.GG5109@redhat.com> <alpine.DEB.2.20.1704251749400.2317@stedding.saclay.inria.fr> <20170425165403.GH5109@redhat.com> <alpine.DEB.2.20.1704252142350.1826@stedding.saclay.inria.fr> <9f1fd07a-bacb-01d4-d1ec-9ce274378e44@gmail.com>
- Reply-to: libstdc++ at gcc dot gnu dot org
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