std: Avoid ptr::copy if unnecessary in vec::Drain by alexcrichton · Pull Request #50575 · rust-lang/rust (original) (raw)

This commit is spawned out of a performance regression investigation in #50496.
In tracking down this regression it turned out that the expand_statements
function in the compiler was taking quite a long time. Further investigation
showed two key properties:

Combined together it looked like glibc gained an optimization to the memmove
function in 2.24. Ideally we don't want to rely on this optimization, so I
wanted to dig further to see what was happening.

The hottest part of expand_statements was Drop for Drain in the call to
splice where we insert new statements into the original vector. This should
be a cheap operation because we're draining and replacing iterators of the exact
same length, but under the hood memmove was being called a lot, causing a
slowdown on glibc 2.23.

It turns out that at least one of the optimizations in glibc 2.24 was that
memmove where the src/dst are equal becomes much faster. This program
executes in ~2.5s against glibc 2.23 and ~0.3s against glibc 2.24, exhibiting
how glibc 2.24 is optimizing memmove if the src/dst are equal.

And all that brings us to what this commit itself is doing. The change here is
purely to Drop for Drain to avoid the call to ptr::copy if the region being
copied doesn't actually need to be copied. For normal usage of just Drain
itself this check isn't really necessary, but because Splice internally
contains Drain this provides a nice speed boost on glibc 2.23. Overall this
should fix the regression seen in #50496 on glibc 2.23 and also fix the
regression on Windows where memmove looks to not have this optimization.

Note that the way splice was called in expand_statements would cause a
quadratic number of elements to be copied via memmove which is likely why the
tuple-stress benchmark showed such a severe regression.

Closes #50496