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

Implement clone_from for BTreeMap and BTreeSet #66648

Merged
merged 7 commits into from
Jan 30, 2020

Conversation

crgl
Copy link
Contributor

@crgl crgl commented Nov 22, 2019

See #28481. This results in up to 90% speedups on simple data types when self and other are the same size, and is generally comparable or faster. Some concerns:

  1. This implementation requires an Ord bound on the Clone implementation for BTreeMap and BTreeSet. Since these structs can only be created externally for keys with Ord implemented, this should be fine? If not, there's certainly a less safe way to do this.
  2. Changing next_unchecked on RangeMut to return mutable key references allows for replacing the entire overlapping portion of both maps without changing the external interface in any way. However, if clone_from fails it can leave the BTreeMap in an invalid state, which might be unacceptable.

This probably needs an FCP since it changes a trait bound, but (as far as I know?) that change cannot break any external code.

@rust-highfive
Copy link
Collaborator

r? @Mark-Simulacrum

(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 Nov 22, 2019
src/liballoc/collections/btree/map.rs Show resolved Hide resolved
src/liballoc/collections/btree/map.rs Show resolved Hide resolved
src/liballoc/collections/btree/map.rs Outdated Show resolved Hide resolved
src/liballoc/collections/btree/map.rs Outdated Show resolved Hide resolved
@Centril
Copy link
Contributor

Centril commented Nov 22, 2019

r? @scottmcm cc @bluss

@Centril
Copy link
Contributor

Centril commented Nov 22, 2019

@bors try @rust-timer queue

@rust-timer
Copy link
Collaborator

Awaiting bors try build completion

@bors
Copy link
Contributor

bors commented Nov 22, 2019

⌛ Trying commit 8f05f02 with merge 633e948...

bors added a commit that referenced this pull request Nov 22, 2019
Implement clone_from for BTreeMap and BTreeSet

See #28481. This results in up to 90% speedups on simple data types when `self` and `other` are the same size, and is generally comparable or faster. Some concerns:

1. This implementation requires an `Ord` bound on the `Clone` implementation for `BTreeMap` and `BTreeSet`. Since these structs can only be created externally for keys with `Ord` implemented, this should be fine? If not, there's certainly a less safe way to do this.
2. Changing `next_unchecked` on `RangeMut` to return mutable key references allows for replacing the entire overlapping portion of both maps without changing the external interface in any way. However, if `clone_from` fails it can leave the `BTreeMap` in an invalid state, which might be unacceptable.

This probably needs an FCP since it changes a trait bound, but (as far as I know?) that change cannot break any external code.
@bors
Copy link
Contributor

bors commented Nov 23, 2019

☀️ Try build successful - checks-azure
Build commit: 633e948 (633e9482caf6121c3e6152b058328f575d580124)

@rust-timer
Copy link
Collaborator

Queued 633e948 with parent 5fa0af2, future comparison URL.

@JohnCSimon
Copy link
Member

Ping from triage:
@scottmcm , checks are green and it looks like the change requests have been resolved, can you please review this PR? Thanks.

@JohnCSimon
Copy link
Member

Pinging again from triage:
@scottmcm - can you please review this PR?

@bors
Copy link
Contributor

bors commented Dec 23, 2019

☔ The latest upstream changes (presumably #67540) made this pull request unmergeable. Please resolve the merge conflicts.

@crgl
Copy link
Contributor Author

crgl commented Dec 23, 2019

This has been rebased and cleaned up to account for the formatting. Is there any interest in merging it? I believe BTree collections are the last collections in the standard library without a specialized clone_from (since HashMap and HashSet are implemented externally and probably don't need it anyway). I think it would be a reasonable thing to have and I'm happy to change any part of this that needs changing, but if deriving Clone is preferred then it probably makes sense to close this PR.

@Dylan-DPC-zz
Copy link

r? @KodrAus

@rust-highfive rust-highfive assigned KodrAus and unassigned scottmcm Dec 27, 2019
@Dylan-DPC-zz
Copy link

@KodrAus any updates?

@Dylan-DPC-zz
Copy link

r? @Amanieu

@rust-highfive rust-highfive assigned Amanieu and unassigned KodrAus Jan 26, 2020
Copy link
Contributor

@KodrAus KodrAus left a comment

Choose a reason for hiding this comment

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

Thanks for your patience @crgl!

I've just left a few comments to start, but have one question about the safety of mutating keys within the map.

src/liballoc/collections/btree/map.rs Outdated Show resolved Hide resolved
src/liballoc/collections/btree/map.rs Outdated Show resolved Hide resolved
src/liballoc/collections/btree/map.rs Outdated Show resolved Hide resolved
src/liballoc/collections/btree/map.rs Show resolved Hide resolved
src/liballoc/tests/btree/map.rs Show resolved Hide resolved
None
}
} {
self.split_off(&key);
Copy link
Member

Choose a reason for hiding this comment

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

I feel that a lot of duplicate work being done here. We first find the nth key, the pass it to split_off so that it can find the position of that key (again!), after which it actually splits the tree. Would it be possible to avoid doing the key lookup twice?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not that I can see, unfortunately! However, split_off doesn't exactly find the key and then split the tree. Because split_off has a target key, it can split the tree as it's searching for the key position on the edges it descends on. In theory it would be better to have a split_after function that splits off the first n keys, but in practice the BTree doesn't have any subtree size information available (and traverses the whole tree every time it needs to recalculate length as a result). Similarly, it would definitely be more efficient if nth was specialized for BTree iterators, but I don't see an easy way to do that. Please let me know if you have any ideas about how to avoid iterating through (up to) half the tree!

Copy link
Member

Choose a reason for hiding this comment

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

My point is that you are traversing the tree twice: once in the nth call in the iterator, and once in split_off. However fixing this is likely to be very complicated, so it's fine to leave it as it is for now.

@Amanieu
Copy link
Member

Amanieu commented Jan 28, 2020

r=me if @KodrAus has no other comments to make

@KodrAus
Copy link
Contributor

KodrAus commented Jan 29, 2020

@bors r=Amanieu rollup

@bors
Copy link
Contributor

bors commented Jan 29, 2020

📌 Commit 6c3e477 has been approved by Amanieu

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jan 29, 2020
Dylan-DPC-zz pushed a commit to Dylan-DPC-zz/rust that referenced this pull request Jan 30, 2020
Implement clone_from for BTreeMap and BTreeSet

See rust-lang#28481. This results in up to 90% speedups on simple data types when `self` and `other` are the same size, and is generally comparable or faster. Some concerns:

1. This implementation requires an `Ord` bound on the `Clone` implementation for `BTreeMap` and `BTreeSet`. Since these structs can only be created externally for keys with `Ord` implemented, this should be fine? If not, there's certainly a less safe way to do this.
2. Changing `next_unchecked` on `RangeMut` to return mutable key references allows for replacing the entire overlapping portion of both maps without changing the external interface in any way. However, if `clone_from` fails it can leave the `BTreeMap` in an invalid state, which might be unacceptable.

~This probably needs an FCP since it changes a trait bound, but (as far as I know?) that change cannot break any external code.~
bors added a commit that referenced this pull request Jan 30, 2020
Rollup of 6 pull requests

Successful merges:

 - #66648 (Implement clone_from for BTreeMap and BTreeSet)
 - #68468 (BTreeMap: tag and explain unsafe internal functions or assert preconditions)
 - #68626 (Use termize instead of term_size)
 - #68640 (Document remaining undocumented `From` implementations for IPs)
 - #68651 (Document `From` implementation for NonZero nums)
 - #68655 (Fix revision annotations in borrowck-feature-nll-overrides-migrate)

Failed merges:

r? @ghost
@bors
Copy link
Contributor

bors commented Jan 30, 2020

⌛ Testing commit 6c3e477 with merge 3024c4e...

@bors bors merged commit 6c3e477 into rust-lang:master Jan 30, 2020
@crgl crgl deleted the btree-clone-from branch January 30, 2020 14:24
ssomers added a commit to ssomers/rust that referenced this pull request Feb 16, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion.
Projects
None yet
Development

Successfully merging this pull request may close these issues.