Uh oh!
There was an error while loading. Please reload this page.
Change slice::to_vec to not use extend_from_slice - #79186
Conversation
rust-highfive
commented
Nov 18, 2020
(rust_highfive has picked a reviewer for you, use r? to override) |
3297db7 to
e09b5a0Compare
Mark-Simulacrum
left a comment
There was a problem hiding this comment.
The extend_from_slice was previously (presumably) optimized to e.g. perform a single memcpy via specialization on T: Copy, is that property preserved here? Can we add a codegen test (src/test/codegen, see https://www.llvm.org/docs/CommandGuide/FileCheck.html for docs on syntax or examples in that dir) to show that we get expected behavior here perhaps (for the T: Copy case at least).
Uh oh!
There was an error while loading. Please reload this page.
While there's no codegen test yet for that property, from the godbolt link which uses a It might take a bit to add a test since compiling on my machine is slow |
FWIW the &str -> String case I was able to better optimize (edit: at least at a "asm looks better" level) than what this PR produces with https://rust.godbolt.org/z/WP7WdK, but I'm not sure if we can e.g. put that directly in a From impl. This PR may still make sense even without that. @m-ou-se -- did you perhaps have context here beyond the Zulip conversation? Trying to figure out if there's other cases besides &str -> String that we care about here. |
Ah I think that what you have is certainly better for the case of |
Mark-Simulacrum
commented
Nov 19, 2020
@bors try @rust-timer queue |
rust-timer
commented
Nov 19, 2020
Awaiting bors try build completion |
bors
commented
Nov 19, 2020
⌛ Trying commit 97ca104b566e8b60191bd40c1103694f1e8cba4f with merge c163a6ecb293f84742f6802fe80a1b745c3e1b4c... |
bors
commented
Nov 19, 2020
☀️ Try build successful - checks-actions |
rust-timer
commented
Nov 19, 2020
Queued c163a6ecb293f84742f6802fe80a1b745c3e1b4c with parent 8256379, future comparison URL. |
rust-timer
commented
Nov 19, 2020
Finished benchmarking try commit (c163a6ecb293f84742f6802fe80a1b745c3e1b4c): comparison url. Benchmarking this pull request likely means that it is perf-sensitive, so we're automatically marking it as not fit for rolling up. Please note that if the perf results are neutral, you should likely undo the rollup=never given below by specifying Importantly, though, if the results of this run are non-neutral do not roll this PR up -- it will mask other regressions or improvements in the roll up. @bors rollup=never |
Interesting, mixed with mostly good results, I've also added an additional specialization since it appears that that on top the new version should hopefully be better, as from prior comments the reason it slowed down was because of missing |
bd79187 to
7e9f777CompareJulianKnodt
commented
Nov 19, 2020
It might be a good idea to have another perf run to see if the additional changes helped or regressed anything. |
- let mut vec = Vec::with_capacity(s.len());+ let mut vec = Vec::new();
vec.extend_from_slice(s);
vec |
Hm, I agree that that is much better than the implementation provided with the drop guard, but I think Simulacrum's impl has fewer instructions when it comes to string types. I also looked at a comparison to the implementation provided with larger types, and it appears just removing https://rust.godbolt.org/z/xP9Wrr |
More assembly instructions doesn't have to be a bad thing. It's optimizing for speed mostly. It looks like it just unrolled the copy loop better in the |
Mm that makes sense. I wonder if I could get the drop guard version to unroll some, because it seems to do less work outside of the loop, so taking the best of both worlds |
the8472
commented
Nov 19, 2020
Vec's The specialization: Lines 2287 to 2299 in fe98231 which ultimately should (through some unnecessary indirections) delegate to Lines 1266 to 1274 in fe98231 |
JulianKnodt
commented
Nov 19, 2020
I would think |
rust-timer
commented
Nov 22, 2020
Queued da403f94eb6c20c344e2e0a7fb61f0a2c940a930 with parent 20328b5, future comparison URL. |
rust-timer
commented
Nov 22, 2020
Finished benchmarking try commit (da403f94eb6c20c344e2e0a7fb61f0a2c940a930): comparison url. Benchmarking this pull request likely means that it is perf-sensitive, so we're automatically marking it as not fit for rolling up. Please note that if the perf results are neutral, you should likely undo the rollup=never given below by specifying Importantly, though, if the results of this run are non-neutral do not roll this PR up -- it will mask other regressions or improvements in the roll up. @bors rollup=never |
JulianKnodt
commented
Nov 22, 2020
Thank you! I should've rebased more carefully to keep it clear which was which. |
Uh oh!
There was an error while loading. Please reload this page.
Mark-Simulacrum
commented
Nov 22, 2020
Please also update the description of this PR, and feel free to keep commits squashed. I'm not sure about the numerous perf runs here either, if there's some comparison being done or whatever I didn't catch that :) The last perf run looks pretty excellent though! |
This also required adding a loop guard in case clone panics Add specialization for copy There is a better version for copy, so I've added specialization for that function and hopefully that should speed it up even more. Switch FromIter<slice::Iter> to use `to_vec` Test different unrolling version for to_vec Revert to impl From benchmarking, it appears this version is faster
Mark-Simulacrum
commented
Nov 23, 2020
@bors try @rust-timer queue Let's double check there are no regressions from the Copy/Clone switch. |
rust-timer
commented
Nov 23, 2020
Awaiting bors try build completion |
bors
commented
Nov 23, 2020
⌛ Trying commit a991558 with merge 52c91095e4d4144807832f6fb658bea83f707c50... |
bors
commented
Nov 23, 2020
☀️ Try build successful - checks-actions |
rust-timer
commented
Nov 23, 2020
Queued 52c91095e4d4144807832f6fb658bea83f707c50 with parent a0d664b, future comparison URL. |
rust-timer
commented
Nov 23, 2020
Finished benchmarking try commit (52c91095e4d4144807832f6fb658bea83f707c50): comparison url. Benchmarking this pull request likely means that it is perf-sensitive, so we're automatically marking it as not fit for rolling up. Please note that if the perf results are neutral, you should likely undo the rollup=never given below by specifying Importantly, though, if the results of this run are non-neutral do not roll this PR up -- it will mask other regressions or improvements in the roll up. @bors rollup=never |
Mark-Simulacrum
commented
Nov 23, 2020
@bors r+ Ok, latest perf run looks good to me. |
bors
commented
Nov 23, 2020
📌 Commit a991558 has been approved by |
bors
commented
Nov 23, 2020
bors
commented
Nov 23, 2020
☀️ Test successful - checks-actions |
I saw this Zulip thread, and didn't see any update from it, so I thought I'd try to fix it. This converts
to_vecto no longer useextend_from_slice, but relies on knowing that the allocated capacity is the same size as the input.Godbolt new v1
Godbolt new v2 w/ drop guard
Godbolt old version
After some amount of iteration, there are now two specializations for
to_vec, one forCopytypes that use memcpy, and one for clone types which is the original from this PR.This is then used inside of
impl<T: Clone> FromIterator<Iter::Slice<T>> for Vec<T>which is essentially equivalent to&[T] -> Vec<T>, instead of previous specialization of theextendfunction. This is because extend has to reason more about existing capacity by callingreserveon an existing vec, and thus produces worse asm.Downsides: This allocates the exact capacity, so I think if many items are added to this
Vecafter, it might need to allocate whereas extending may not. I also noticed the number of faults went up in the benchmarks, but not sure where from exactly.