-
Notifications
You must be signed in to change notification settings - Fork 13k
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
Add Iterator specialisation for Repeat::nth #47533
Conversation
r? @Kimundi (rust_highfive has picked a reviewer for you, use r? to override) |
r? @scottmcm (Simpler for someone involved in the previous discussion to review.) |
@varkor I'm not an approved reviewer (and am not on the libs team), so cannot be assigned. Since this is as much a policy decision as a library change, maybe it would make sense to include as part of this change a documentation comment on Brainstorming for things that might not want this behaviour:
Side note: Copy elision is the only optimization in C++ (until C++14 added a second one, at least) that can change observable behaviour, which I suppose is both a note about how important and how scary it can be 🙂 |
src/libcore/tests/iter.rs
Outdated
@@ -1403,6 +1403,7 @@ fn test_repeat() { | |||
assert_eq!(it.next(), Some(42)); | |||
assert_eq!(it.next(), Some(42)); | |||
assert_eq!(it.next(), Some(42)); | |||
assert_eq!(it.nth(usize::MAX), Some(42)); |
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.
Consider doing this with something that's need_drop, since LLVM already can remove the loop for trivial things like i32: https://play.rust-lang.org/?gist=546bdfadcb744e3aadabf698267b204f&version=nightly
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.
Are you sure the loop is being removed here? https://play.rust-lang.org/?gist=c93f5f86a61ea6af7db1b3c2ab22b66f&version=nightly is terminated for taking too long to execute (which was the intention for this test case as well), which isn't the case after the specialisation.
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.
Try https://play.rust-lang.org/?gist=c93f5f86a61ea6af7db1b3c2ab22b66f&version=stable&mode=release, and it doesn't time out. (Now, I'm sure at least one of the builds runs the test in debug, so it'd still catch it, but it's also a Copy type, so copy elision is definitely allowed -- a dead memcpy is certainly removable -- compared to a more complex Clone impl that's the subject of discussion in the issue.)
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.
Ah, got it! I'll adjust the test, thanks!
@scottmcm: Regarding documentation, do you think it makes more sense to mention this on On |
Just so we're clear: Such a broad change to Clone would mark a major semantic change to Rust that certainly requires a whole RFC. The same decision for Clone on types that are also Copy (much less controversial) did go through an RFC. Arguably an RFC is already required for any form of Clone elision, no matter how restricted, but it's especially clear-cut when changing Clone in general. |
Yes, I wanted to avoid going down this route for exactly that reason. I realise such a change would be far too large to be contained in this issue. So far it seems that there's no general rule for I think it still isn't exactly clear whether even this sort of change too requires a RFC — might be helpful for the libs team to weigh in here. |
I personally think that whoever wants the side effects should use a |
I think I agree with @rkruppe here, on reflection. The docs for the @llogiq Unfortunately there are now cases where the iterator way is faster than the for loop way (particularly around |
Note that it's not just side effects in Clone that can make Clone elision user-visible, side effects in Drop also matter. For example, I've seen probably dozens of "noisy drop" types (which print something on drop) used to track the order in which temporaries are dropped. This is still unlikely to matter for program correctness, but it's somewhat more likely than Clone side effects to be user-visible and hence confusing. I don't believe it's terribly likely that Clone elision for Repeat::nth would do material damage to anyone, but it might very well confuse people, and I cannot rule out that it actually affects the behavior of a real, valid (though arguably badly-written) program. The potential benefits are also very small IMO, so I'm opposed to trying to be clever here. (A specialization for |
I had overlooked |
Alternatively, use specialization to only elide clones of types that are |
Yeah, probably it's best to do that for now, and then get around to discussing the issue more visibly in an RFC at a later point. I'll fix that soon. |
This doesn't add any performance, though, because llvm is already able to optimize all of these cases. |
Yeah. It probably will improve compile times (slightly) and performance in debug mode (significantly for large n) so it's still worthwhile IMO. |
Implementing this just for |
Can you point out what you tried and which type inference was broken? I have experienced something similar in #47082 when I tried to specialize |
It might be possible to achieve that way, but considering how small this change is (when non- |
A final (implementational) follow-up for #47370, this (solely) adds an
Iterator::nth
optimisation specialisation forRepeat
.This changes the number of times
clone()
is called on the repeated value, which is an observable change. It's not entirely clear whether this is acceptable, and might benefit from a FCP.cc @sfackler, @rkruppe, @bluss