-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
Conversation
Because Eq types must be reflexively equal, an equal-length slice to the same memory location must be equal.
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. |
src/libcore/slice/mod.rs
Outdated
return true; | ||
} | ||
|
||
for i in 0..self.len() { |
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
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.
@bors try |
⌛ Trying commit 30b27f3 with merge 7966b55e0c690a4bce499ac9179ac54610f3cb66... |
⌛ Trying commit 30b27f3 with merge 034cf602489c17a0d3a6b9991f6a3997bf23d56d... |
|
@bors try |
⌛ Trying commit d482589 with merge 261715133edddbeeccb64c5a890ce76658fe589f... |
☀️ Try build successful - checks-travis |
@rust-timer build 261715133edddbeeccb64c5a890ce76658fe589f |
Success: Queued 261715133edddbeeccb64c5a890ce76658fe589f with parent 400b409, comparison URL. |
Finished benchmarking try commit 261715133edddbeeccb64c5a890ce76658fe589f, comparison URL. |
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. |
core: use memcmp optimization for 128 bit integer slices All other sized integer slices do this. From rust-lang#61665.
core: use memcmp optimization for 128 bit integer slices All other sized integer slices do this. From rust-lang#61665.
core: use memcmp optimization for 128 bit integer slices All other sized integer slices do this. From rust-lang#61665.
ping from triage @sfackler @aschampion any updates on this? |
The benchmark results seem like a pretty mixed bag - not sure if this change really had much of an impact. |
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 |
Yeah, that all makes sense - seems like it's worth discussing at the next libs triage meeting. |
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+ |
📌 Commit d482589 has been approved by |
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.
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
☔ The latest upstream changes (presumably #62580) made this pull request unmergeable. Please resolve the merge conflicts. |
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:
Because slices of non-reflexively equal types (like
f64
) are not equal even if it's the same slice. But if the types areEq
, we can use this same-address optimization, which this PR implements. Obviously this changes behavior if types violate the reflexivity condition ofEq
, because their impls ofPartialEq
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 runrustc_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 ofEq + Copy
.Eq
should be sufficient, butCopy
was included for clarity.