std::vector move assign patch
Marc Glisse
marc.glisse@inria.fr
Sun May 14 11:26:00 GMT 2017
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
More information about the Libstdc++
mailing list