-
Notifications
You must be signed in to change notification settings - Fork 13.3k
Conditional mutation in Iterator::next doesn't optimise well #24660
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
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: