Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Swap self and other in vec::append when it avoids a reallocation #77538

Closed
wants to merge 2 commits into from

Conversation

the8472
Copy link
Member

@the8472 the8472 commented Oct 4, 2020

Spun out from #77496

Implements #56763

This needs discussion since it slightly weakens the "no shrinking" guarantee, even though it keeps with the stated intent of the rule that

This ensures no unnecessary allocations or deallocations occur.

@rust-highfive
Copy link
Collaborator

r? @kennytm

(rust_highfive has picked a reviewer for you, use r? to override)

@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Oct 4, 2020
@the8472 the8472 changed the title Swap append Swap self and other in vec::append when it avoids a reallocation Oct 4, 2020
@kennytm kennytm added the T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. label Oct 4, 2020
@kennytm
Copy link
Member

kennytm commented Oct 4, 2020

r? @KodrAus

@rust-highfive rust-highfive assigned KodrAus and unassigned kennytm Oct 4, 2020
@@ -1257,6 +1257,14 @@ impl<T> Vec<T> {
#[inline]
#[stable(feature = "append", since = "1.4.0")]
pub fn append(&mut self, other: &mut Self) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or we could just put a big notice that possible allocation stealing in the docs of append to give users a note.

Comment on lines +246 to +247
/// `Vec` will not automatically shrink itself, even if completely empty, when doing so
/// would cause unnecessary allocations or deallocations to occur. Emptying a `Vec`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe playing with words would confuse most users, this does not feel much different to me. Maybe

Suggested change
/// `Vec` will not automatically shrink itself, even if completely empty, when doing so
/// would cause unnecessary allocations or deallocations to occur. Emptying a `Vec`
/// `Vec` will not automatically shrink itself, even if completely empty. **But** it
/// will try to reduce unnecessary allocations or deallocations by reusing existing allocation. Emptying a `Vec`

Maybe a big but helps? Probably needs rewrapping.

// The capacity limit ensures that we are not stealing a large preallocation from `other`
// that is not commensurate with the avoided reallocation in self.
if self.len == 0 && self.capacity() < other.len && other.capacity() / 2 <= other.len {
mem::swap(self, other);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even though this helps the general case but it may regress on some cases where users want to reuse the capacity of the other Vec.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

without this change: it must allocate now to grow self
with this change: it may have to allocate later to grow other

@the8472
Copy link
Member Author

the8472 commented Oct 5, 2020

A more conservative alternative would be to swap only if self.cap == 0 and after the swap do other.reserve(self.cap). This would result in the same number of allocations as appending but avoid the memcpy.

@KodrAus
Copy link
Contributor

KodrAus commented Oct 18, 2020

Thanks for putting so much effort into optimizing Vec @the8472!

Did we have a motivating usecase for this? It would be good to understand some cases where we reduce allocations, and cases where we don't.

@the8472
Copy link
Member Author

the8472 commented Oct 18, 2020

@KodrAus I'm pretty much just implementing #56763 with this PR, so we'll have to ask @Gankra about motivating use-cases.

@the8472
Copy link
Member Author

the8472 commented Oct 18, 2020

And this could use a perf run to see if it even has any positive impact.

@KodrAus
Copy link
Contributor

KodrAus commented Oct 23, 2020

I'm ok with that re-interpretation of no-shrinking to avoid re-allocating when it's not necessary. The reason I was interested in motivating cases is because it makes the performance characteristics a bit less local, so it's tricky to tell from just the input vectors whether it's an improvement or not.

Let's do a timer run!

@rust-timer queue

@rust-timer
Copy link
Collaborator

Awaiting bors try build completion

@m-ou-se
Copy link
Member

m-ou-se commented Oct 23, 2020

Awaiting bors try

@bors try

@bors
Copy link
Contributor

bors commented Oct 23, 2020

⌛ Trying commit 4cdbb8c with merge 43f40c41fe26c6cc769b75bf60734fb20e68a622...

@bors
Copy link
Contributor

bors commented Oct 23, 2020

☀️ Try build successful - checks-actions
Build commit: 43f40c41fe26c6cc769b75bf60734fb20e68a622 (43f40c41fe26c6cc769b75bf60734fb20e68a622)

@rust-timer
Copy link
Collaborator

Queued 43f40c41fe26c6cc769b75bf60734fb20e68a622 with parent 07a63e6, future comparison URL.

@rust-timer
Copy link
Collaborator

Finished benchmarking try commit (43f40c41fe26c6cc769b75bf60734fb20e68a622): 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 rollup- to bors.

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

@the8472
Copy link
Member Author

the8472 commented Oct 23, 2020

Meh, doesn't seem worthwhile.

@KodrAus
Copy link
Contributor

KodrAus commented Oct 24, 2020

Thanks for investigating @the8472!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants