-
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
Allow Pre-Stepping in Step-By? #52065
Comments
Nominated for discussion by @rust-lang/libs because of upcoming stabilization being a blocker for perf improvements due to semantic changes being required @Emerentius I think discussion would be aided by a PR that makes this change and shows some performance numbers |
1.28 is in beta, so if we make a change it may need to be backported. I’m having a little hard time following what is being proposed exactly. Could you give some code examples and the behavior before/after the change? Is the change "only" in documentation of semantics, in terms of what implementations of a trait are allowed or not allowed to do? Is that the |
I'm not proposing any code changes right now, only a note in the docs giving us some freedom for future changes. The semantics of step_by are The concrete performance problem I have in mind are still ranges (#51557, #43064, #31155). Their problems are not entirely due to The problem therefore is, what if we need to be able to pre-step for performance reasons in the presence of side-effects? With I just want to keep our options open for now but change no actual code. We can sort this out without a stabilization countdown over our heads. Also, I don't have much time right now. |
@Emerentius could you expand on the proposed documentation change? What do you think should be added to the documentation to future-proof ourselves? |
Right now, the following code (0..10)
.inspect(|x| print!("({}) ", x))
.step_by(2)
.take(3)
.for_each(|x| println!("{}", x)); prints
I believe the request is to allow it the freedom to instead print
Which I think can be optimized better for ranges (one addition instead of two) and could be argued is a more pure translation of "step by two", but has the obvious downside of potentially reading more things from the source iterator (if it doesn't get exhausted). |
Ah ok, thanks @scottmcm! That makes total sense to me and I'd personally be down for such an implementation |
Yes, that's what I was trying to say. It's a bit difficult to express concisely in terms of existing iterator methods. Here's my attempt at the note to be added: Note:
fn advance_n_and_return_first<I>(iter: &mut I, total_step: usize) -> Option<I::Item>
where
I: Iterator,
{
let next = iter.next();
if total_step > 1 {
iter.nth(step-2);
}
next
} |
Sounds like a great clause to me to add! (I'd personally be down for a PR to change the behavior as well) |
PR is merged and backported |
step_by
has to special case the first element and this behaviour can act as a performance block because of the necessary branch innext
as seen with integer ranges. This branch can be removed, by always taking the next element and also stepping ahead in one go but that changes the semantics when the iterator has side-effects.step_by
will be stabilized with the next release and I fear that the need to preserve semantics may lock us into a sub-optimally performingstep_by
or some other lacking solution. I propose that we amend a note tostep_by
, allowing us to use the "always take and step ahead" semantics for optimization for now. In the future, when we have this issue figured out, we can then limit the cases where pre-stepping is allowed. The general case should not use it.One example where preserving semantics causes inefficiency is
RangeFrom
iterators. When pre-stepping, aRangeFrom
will overflow itsstart
member one iteration earlier and will therefore, in debug mode, panic one iteration earlier. That may cause a panic in a program that doesn't actually use any elements after the overflow, i.e. a program without any user error. If we have to preserve these semantics,RangeFrom
can not be optimized forstep_by
.(
RangeFrom
reserves the right to change overflow behaviour so we could "fix" this particular one by allowing overflow in debug mode)Furthermore, if range iterators need to pre-step for performance reasons, then the currently still unstable
Step
trait'sstep*
methods must be limited to be side-effect free or the pre-stepping optimization would be invalid.The text was updated successfully, but these errors were encountered: