On Tue, 25 Apr 2017, Jonathan Wakely wrote:
That's only 5 more memory operations. If I tweak vector swapping to avoid
calling swap on each member (which drops type-based aliasing information,
that was the topic of PR64601)
I didn't really understand the discussion in the PR. I find that's
true of most TBAA discussions.
std::swap(T& x, T& y) is hard to optimise because we don't know that
the dynamic type of the thing at &x is the same type as T?
std::swap<int*> only knows that it is dealing with pointers to integers, so
_M_start from one vector might be in the same location as _M_finish from
some other vector...
When I access __x._M_start directly, I am accessing some part of a
_Vector_impl, and 2 _Vector_impl can only be the same or disjoint, they
cannot partially overlap.
I see. I'm surprised that after the std::swap calls get inlined, so
that the references can be "seen through", the pessimisation is still
present.
(this is my understanding of how compilers handle TBAA)
The only things that carry information are the declaration of an object,
and accesses (read/write). Pointers and references are meaningless until
the object is accessed, they are just a fancy way to do pointer
arithmetic, and the way the object is accessed carries the whole
information. If I have a reference p to std::pair<int,int>, p.first=42 or
x=p.first access the first element of a pair, while *&p.first=42 or
x=*&p.first are only accesses to an int. p.first and q.second cannot
alias, while *&p.first and *&q.second can.
In other words
illegal: ((std::pair<int,int>*)&p.second)->first=2
allowed: *(int*)&((std::pair<int,int>*)&p.second)->first=2
because there is no actual access through the "wrong" pointers (I added a
redundant cast to int* to try and clarify the difference).
(actually, gcc wrongly optimizes *&x to x so you need to use an
intermediate variable to notice this: auto y=&x; ... *y ... while clang is
stricter)
I think this is horrible but that's not a fight I feel capable of leading.
If what I said is too hard to understand, it is fine to ignore. And if it
is wrong, I'll be happy to learn that :-)
By the way, do we have a policy on writing if(p)delete p; vs directly
delete p; which does nothing for p==0? There are cases where the extra
shortcut is a nice optimization, others where it is redundant work. At -Os,
the middle-end could optimize if(p)free(p) to free(p) in the future.
No official policy. I always consider the if (p) to be redundant
noise, but that's based on readability and correctness, not
performance.
I was going to say we have that in std::vector's destructor, but
allocators do not have the same property as free/delete, deallocate can
only be called with something allocate returned, not 0. So we don't have a
choice.
--
Marc Glisse