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

core: check for pointer equality when comparing Eq slices #61665

Merged
merged 2 commits into from
Jul 11, 2019

Conversation

aschampion
Copy link
Contributor

Because Eq types must be reflexively equal, an equal-length slice to the same memory location must be equal.

This is related to #33892 (and #32699) answering this comment from that PR:

Great! One more easy question: why does this optimization not apply in the non-BytewiseEquality implementation directly above?

Because slices of non-reflexively equal types (like f64) are not equal even if it's the same slice. But if the types are Eq, we can use this same-address optimization, which this PR implements. Obviously this changes behavior if types violate the reflexivity condition of Eq, because their impls of PartialEq will no longer be called per-item, but 🤷‍♂ .

It's not clear how often this optimization comes up in the real world outside of the same-&str case covered by #33892, so I'm requesting a perf run (on MacOS today, so can't run rustc_perf myself). I'm going ahead and making the PR on the basis of being surprised things didn't already work this way.

This is my first time hacking rust itself, so as a perf sanity check I ran ./x.py bench --stage 0 src/lib{std,alloc}, but the differences were noisy.

To make the existing specialization for BytewiseEquality explicit, it's now a supertrait of Eq + Copy. Eq should be sufficient, but Copy was included for clarity.

Because Eq types must be reflexively equal, an equal-length slice to the
same memory location must be equal.
@rust-highfive
Copy link
Collaborator

Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @sfackler (or someone else) soon.

If any changes to this PR are deemed necessary, please add them as extra commits. This ensures that the reviewer can see what has changed since they last reviewed the code. Due to the way GitHub handles out-of-date commits, this should also make it reasonably obvious what issues have or haven't been addressed. Large or tricky changes may require several passes of review and changes.

Please see the contribution instructions for more information.

@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Jun 8, 2019
return true;
}

for i in 0..self.len() {
Copy link
Contributor

Choose a reason for hiding this comment

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

This could be written as:

        self.iter().zip(other.iter()).all(|(x, y)| x == y)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point. I mindlessly copied the other impl, but as written it's not clear the bounds check for other[i] would always be elided. I've updated both impls to user iterators now.

@oli-obk
Copy link
Contributor

oli-obk commented Jun 8, 2019

@bors try

@bors
Copy link
Contributor

bors commented Jun 8, 2019

⌛ Trying commit 30b27f3 with merge 7966b55e0c690a4bce499ac9179ac54610f3cb66...

@bors
Copy link
Contributor

bors commented Jun 8, 2019

⌛ Trying commit 30b27f3 with merge 034cf602489c17a0d3a6b9991f6a3997bf23d56d...

@aschampion
Copy link
Contributor Author

i128 and u128 should also be BytewiseEquality so they can take advantage of the memcmp impl. Should I push that here rather than opening another PR for a 1 line diff?

@Centril
Copy link
Contributor

Centril commented Jun 10, 2019

@bors try

@bors
Copy link
Contributor

bors commented Jun 10, 2019

⌛ Trying commit d482589 with merge 261715133edddbeeccb64c5a890ce76658fe589f...

@bors
Copy link
Contributor

bors commented Jun 10, 2019

☀️ Try build successful - checks-travis
Build commit: 261715133edddbeeccb64c5a890ce76658fe589f

@Centril
Copy link
Contributor

Centril commented Jun 10, 2019

@rust-timer build 261715133edddbeeccb64c5a890ce76658fe589f

@rust-timer
Copy link
Collaborator

Success: Queued 261715133edddbeeccb64c5a890ce76658fe589f with parent 400b409, comparison URL.

@rust-timer
Copy link
Collaborator

Finished benchmarking try commit 261715133edddbeeccb64c5a890ce76658fe589f, comparison URL.

@sfackler
Copy link
Member

i128 and u128 should also be BytewiseEquality so they can take advantage of the memcmp impl. Should I push that here rather than opening another PR for a 1 line diff?

Either way - a separate PR may be best to avoid accidentally dropping the change if the perf run reports that the pointer equality check isn't worth it though.

Centril added a commit to Centril/rust that referenced this pull request Jun 11, 2019
core: use memcmp optimization for 128 bit integer slices

All other sized integer slices do this. From rust-lang#61665.
Centril added a commit to Centril/rust that referenced this pull request Jun 11, 2019
core: use memcmp optimization for 128 bit integer slices

All other sized integer slices do this. From rust-lang#61665.
Centril added a commit to Centril/rust that referenced this pull request Jun 11, 2019
core: use memcmp optimization for 128 bit integer slices

All other sized integer slices do this. From rust-lang#61665.
@Dylan-DPC-zz
Copy link

ping from triage @sfackler @aschampion any updates on this?

@sfackler
Copy link
Member

sfackler commented Jul 1, 2019

The benchmark results seem like a pretty mixed bag - not sure if this change really had much of an impact.

@aschampion
Copy link
Contributor Author

For context, I created this because I had code (change queue consistency check) where the most common path was same slice comparison, and having exploited the primitive slice equal pointer optimization before was surprised when the performance was linear and not constant. Of course, if you want to rely on this optimization it's easy to manually check for this case, which is what I've done in the meantime. However, it doesn't look like this comes up frequently in the benchmarking suite. I've looked through a few popular crates trying to find cases where this would apply, but haven't found any yet.

So it's down to whether having behavior consistent with primitive slices is seen as more consistent/less surprising, or if having another impl is noise for a rare niche optimization, in which case this can be closed. I can sympathize with both views.

@sfackler sfackler added I-nominated T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. labels Jul 2, 2019
@sfackler
Copy link
Member

sfackler commented Jul 2, 2019

Yeah, that all makes sense - seems like it's worth discussing at the next libs triage meeting.

@sfackler
Copy link
Member

We talked about this at the libs meeting today, and tentatively felt like we should land this. It doesn't appear to regress normal code in a significant way, and we can always back it out later!

@bors r+

@bors
Copy link
Contributor

bors commented Jul 10, 2019

📌 Commit d482589 has been approved by sfackler

@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 Jul 10, 2019
Centril added a commit to Centril/rust that referenced this pull request Jul 11, 2019
core: check for pointer equality when comparing Eq slices

Because `Eq` types must be reflexively equal, an equal-length slice to the same memory location must be equal.

This is related to rust-lang#33892 (and rust-lang#32699) answering this comment from that PR:

> Great! One more easy question: why does this optimization not apply in the non-BytewiseEquality implementation directly above?

Because slices of non-reflexively equal types (like `f64`) are not equal even if it's the same slice. But if the types are `Eq`, we can use this same-address optimization, which this PR implements. Obviously this changes behavior if types violate the reflexivity condition of `Eq`, because their impls of `PartialEq` will no longer be called per-item, but 🤷‍♂ .

It's not clear how often this optimization comes up in the real world outside of the same-`&str` case covered by rust-lang#33892, so **I'm requesting a perf run** (on MacOS today, so can't run `rustc_perf` myself). I'm going ahead and making the PR on the basis of being surprised things didn't already work this way.

This is my first time hacking rust itself, so as a perf sanity check I ran `./x.py bench --stage 0 src/lib{std,alloc}`, but the differences were noisy.

To make the existing specialization for `BytewiseEquality` explicit, it's now a supertrait of `Eq + Copy`. `Eq` should be sufficient, but `Copy` was included for clarity.
bors added a commit that referenced this pull request Jul 11, 2019
Rollup of 7 pull requests

Successful merges:

 - #61665 (core: check for pointer equality when comparing Eq slices)
 - #61923 (Prerequisites from dep graph refactoring #2)
 - #62270 (Move async-await tests from run-pass to ui)
 - #62425 (filedesc: don't use ioctl(FIOCLEX) on Linux)
 - #62476 (Continue refactoring macro expansion and resolution)
 - #62519 (Regression test for HRTB bug (issue 30786).)
 - #62557 (Fix typo in libcore/intrinsics.rs)

Failed merges:

r? @ghost
@bors
Copy link
Contributor

bors commented Jul 11, 2019

⌛ Testing commit d482589 with merge 6907005...

@bors
Copy link
Contributor

bors commented Jul 11, 2019

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

@bors bors added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. labels Jul 11, 2019
@bors bors merged commit d482589 into rust-lang:master Jul 11, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. 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