-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
Implement Fuse with Option #70366
Implement Fuse with Option #70366
Conversation
The former `done` flag was roughly similar to an `Option` tag, but left the possibity of misuse. By using a real `Option`, we can set `None` when the iterator is exhausted, removing any way to call it again. We also allow niche layout this way, so the `Fuse` may be smaller. The `FusedIterator` specialization does want to ignore the possibility of exhaustion though, so it uses `unsafe { intrinsics::unreachable() }` to optimize that branch away. The entire `Fuse` implementation is now isolated in its own module to contain that unsafety.
This was extracted from #70332 -- let's go ahead and get a perf run: @bors try @rust-timer queue |
Awaiting bors try build completion |
⌛ Trying commit 212e6ce with merge bad75affe428dbcd6e24d9ae6a248ac2e9364c7f... |
If this one also slows down codegen, I think it might help to use direct |
match self.iter { | ||
Some(ref iter) => iter, | ||
// SAFETY: the specialized iterator never sets `None` | ||
None => unsafe { intrinsics::unreachable() }, |
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.
Ideally all of these should be hint::unreachable_unchecked()
today I think
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.
Why is that better? For core
that's free to use intrinsics, going through hint
just adds more inline indirection...
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.
Clearer meaning, IMO. But not that important, certainly no need to change things in this PR.
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.
Why is that better?
For example, if we make the hint
method panic in debug-builds at some point (to detect incorrect use), then using the intrinsic would not benefit from that check.
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.
One should note that this code triggers UB if an iterator implements
FusedIterator
by error. The implementor don't even have to useunsafe
in order to achieve that.
I don't think so. Regardless of I
's FusedIterator implementation or anything in I
, it can't set the iter
field of Fuse
to None. This modules never sets iter
to None if I
is FusedIterator.
☀️ Try build successful - checks-azure |
Queued bad75affe428dbcd6e24d9ae6a248ac2e9364c7f with parent 2dcf54f, future comparison URL. |
Does this affect when drop handlers are called? (I don't know how important this is) |
@timvermeulen Yes it does. @rust-lang/libs I was going to r+ this, as it's a size win and seems fine in perf. But now that #70374 showed up too, I wanted to ask how you feel about different approaches here. (And to double-check that where |
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.
This PR drops the impl TrustedRandomAccess for Fuse<I> if I is !FusedIterator, which seems unintentional.
unsafe impl<I> TrustedRandomAccess for Fuse<I>
where
- I: TrustedRandomAccess,
+ I: TrustedRandomAccess + FusedIterator,
Otherwise looks good to me.
@dtolnay that was semi-intentional, because what is that supposed to do if we've already set But |
The caller of get_unchecked guarantees that the fuse isn't already None, because they have a specific index at which they know an element exists. So you should be able to use the same approach as in as_inner_mut. |
@dtolnay OK, done! |
@bors r+ |
📌 Commit 4f429c0 has been approved by |
Implement Fuse with Option The former `done` flag was roughly similar to an `Option` tag, but left the possibity of misuse. By using a real `Option`, we can set `None` when the iterator is exhausted, removing any way to call it again. We also allow niche layout this way, so the `Fuse` may be smaller. The `FusedIterator` specialization does want to ignore the possibility of exhaustion though, so it uses `unsafe { intrinsics::unreachable() }` to optimize that branch away. The entire `Fuse` implementation is now isolated in its own module to contain that unsafety. r? @scottmcm
Implement Fuse with Option The former `done` flag was roughly similar to an `Option` tag, but left the possibity of misuse. By using a real `Option`, we can set `None` when the iterator is exhausted, removing any way to call it again. We also allow niche layout this way, so the `Fuse` may be smaller. The `FusedIterator` specialization does want to ignore the possibility of exhaustion though, so it uses `unsafe { intrinsics::unreachable() }` to optimize that branch away. The entire `Fuse` implementation is now isolated in its own module to contain that unsafety. r? @scottmcm
Thanks for taking this on, BTW, @cuviper! |
Rollup of 5 pull requests Successful merges: - rust-lang#70226 (use checked casts and arithmetic in Miri engine) - rust-lang#70319 (correctly normalize constants) - rust-lang#70352 (Add long error explanation for E0710 ) - rust-lang#70366 (Implement Fuse with Option) - rust-lang#70379 (fix incorrect type name in doc comments) Failed merges: - rust-lang#70375 (avoid catching InterpError) r? @ghost
Match options directly in the Fuse implementation Rather than using `as_ref()`, `as_mut()`, and `?`, we can use `match` directly to save a lot of generated code. This was mentioned as a possibility in rust-lang#70366 (comment), and I found that it had a very large impact on rust-lang#70332 using `Fuse` within `Chain`. Let's evaluate this change on its own first.
Match options directly in the Fuse implementation Rather than using `as_ref()`, `as_mut()`, and `?`, we can use `match` directly to save a lot of generated code. This was mentioned as a possibility in rust-lang#70366 (comment), and I found that it had a very large impact on rust-lang#70332 using `Fuse` within `Chain`. Let's evaluate this change on its own first.
Implement Chain with Option fuses The iterators are now "fused" with `Option` so we don't need separate state to track which part is already exhausted, and we may also get niche layout for `None`. We don't use the real `Fuse` adapter because its specialization for `FusedIterator` unconditionally descends into the iterator, and that could be expensive to keep revisiting stuff like nested chains. It also hurts compiler performance to add more iterator layers to `Chain`. This change was inspired by the [proposal](https://internals.rust-lang.org/t/proposal-implement-iter-chain-using-fuse/12006) on the internals forum. This is an alternate to rust-lang#70332, directly employing some of the same `Fuse` optimizations as rust-lang#70366 and rust-lang#70750. r? @scottmcm
The former
done
flag was roughly similar to anOption
tag, but leftthe possibity of misuse. By using a real
Option
, we can setNone
when the iterator is exhausted, removing any way to call it again. We
also allow niche layout this way, so the
Fuse
may be smaller.The
FusedIterator
specialization does want to ignore the possibilityof exhaustion though, so it uses
unsafe { intrinsics::unreachable() }
to optimize that branch away. The entire
Fuse
implementation is nowisolated in its own module to contain that unsafety.
r? @scottmcm