-
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
Conditional mutation in Iterator::next doesn't optimise well #24660
Comments
Oh, this is possible due to our handling of "small" aggregates, e.g. The implementation of if self.start < self.end {
let mut n = &self.start + &A::one();
mem::swap(&mut n, &mut self.start);
Some(n)
} else {
None
} which appears in the given code: the first let mut n = &self.start + &A::one();
mem::swap(&mut n, &mut self.start);
if self.start < self.end {
Some(n)
} else {
None
} Unfortunately, this transformation will mean that calling cc @dotdash |
The rust iterator trait states:
So I don't think that is unfortunate at all. |
FWIW, changing |
I think the |
@huonw users can always use |
Yes, I understand that, but this is the sort of thing which subtly results in broken programs (similar to overflow assertions): most situations won't think to call |
You're right, it's a tradeoff, I just think that the vast majority of iterators are used in for loops or other standard constructs which will just quit after the first |
The conditional mutation of the previous implementation resulted in poor code, making it unconditional makes `Range` less well behaved as an Iterator (but still legal) but also makes it fast. The intention is that this change will be reverted when rustc/LLVM handle the best-behaved implementation better. cc rust-lang#24660
I've submitted #24705 with the intention that it's a temporary work-around. |
If you really want well behaved iterators in the standard library I would suggest having them |
You need to differentiate the (non-)guarantees of the trait and of specific implementations. I (and probably others) happily rely on the sanity of the standard slice iterator implementation, because I know I can. |
The conditional mutation of the previous implementation resulted in poor code, making it unconditional makes `Range` less well behaved as an Iterator (but still legal) but also makes it fast. The intention is that this change will be reverted when rustc/LLVM handle the best-behaved implementation better. cc rust-lang#24660
This optimizes properly now. Maybe due to the LLVM update? |
For completeness sake: .section .text._ZN4test20hb4b7c0c6e08ff614VaaE,"ax",@progbits
.globl _ZN4test20hb4b7c0c6e08ff614VaaE
.align 16, 0x90
.type _ZN4test20hb4b7c0c6e08ff614VaaE,@function
_ZN4test20hb4b7c0c6e08ff614VaaE:
.cfi_startproc
movl $100000, %eax
retq
.Lfunc_end1:
.size _ZN4test20hb4b7c0c6e08ff614VaaE, .Lfunc_end1-_ZN4test20hb4b7c0c6e08ff614VaaE
.cfi_endproc |
Compiles to:
It should at least look something like:
But really that case should be constant folded (clang folds the equivalent C
for
loop).(NB.
count
is currently implemented asself.fold(0, |c, _| c + 1)
, and the bad codegen occurs if that is definition is used directly, and also if one uses the mutating for loop:let mut c = 0; for _ in ... { c += 1 }
.)This previously affected
std::ops::Range
(i.e.x..y
), but #24705 implemented a work-around.The text was updated successfully, but these errors were encountered: