-
Notifications
You must be signed in to change notification settings - Fork 13.5k
libcore::iter Refactor Iterator::next() for Skip #15990
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
Conversation
Iterators are generally pretty performance sensitive, could you verify that a benchmark for skip is the same both before and after? |
I wrote some benchmarks
Here is the code
|
} | ||
} | ||
let mut n = self.n; | ||
if n > 0 { self.n = 0 } |
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.
how about mem::replace(&mut self.n, 0)
?
You may add these benchmarks to |
Just adding that benchmark code at the end of the file or is there some way of presenting it ? |
Just at the end, like it is in mem.rs. |
It's like 10ns per iter slower with your changes
I don't know if these are worth the more complex code. What is weird is that with the -O flag the new code is 10ns faster
After
I am not expert on what these benchmarks actually mean, with or without optimizations etc. |
Yes, the path that eventually returns Some is slower by 10ns.
I think |
I am not sure that mem::replace is an issue, since the path that returns None is actually faster than before. And as you said with the optimization flag the whole code is faster than before. I will still try without mem::replace to see if it improves. |
Perhaps returning Some is slower in unoptimized code because the for loop first moves the value out of |
Ok it's even faster without mem::replace, I replaced it as you suggested.
Looks like we can have shorter and faster code at the end : ) |
The assignment you removed either doesn't matter or it should set |
An iterator adaptor doesn't have to worry about the behaviour after it's exhausted once, as long as it's not memory unsafe. |
If you look at the original code it sets self.n = 0 in case of exhaustion. Actually all pathes set self.n = 0. |
It doesn't have to do that, any code depending on it is buggy. |
Simplifying the code of that method Adding benchmarks for that method Fixing behaviour of the method which sets self.n to zero even when the iterator is consumed before skipping n values
@thestinger If I understand well what you say self.n should be set to n in case the iterator is exhausted ? Or do you mean that we shouldn't even care of updating self.n ? |
Fyi, only optimized benchmark runs matter at all. Comparing unoptimized results is next to meaningless in Rust. |
Hmm now that I understand more the code I think I need more benchmarks before changing that part. I need to test with big iterators and big skip ranges. I will close that request for now and think more. I will come back with more detailed benchmarks at least, even if I am not changing the code. |
We don't care at all of self.n in case the iterator is exhausted. The code seems entirely correct to me. The benchmarks should cover chained iterators: |
…c_item, r=Veykril feat: add trait_impl_reduntant_assoc_item diagnostic part of rust-lang/rust-analyzer#15958, will try to add quickfix for the diagnostic if this PR is ok with you guys
So I simplified the code, taking into account the points raised in the discussion.
I added some benchmarks, and verified that the performance improved. It's about twice as fast on the benchmarks.
Close reason (last comment)