-
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
Implement Chain with Fuses #70332
Implement Chain with Fuses #70332
Conversation
@bors try @rust-timer queue |
Awaiting bors try build completion |
⌛ Trying commit da75e67f3f78b50e5a7f9845f43e389c4bdc93fc with merge 6f956ef5779fcbc05aaf93da3972715167a03291... |
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.
nth
and nth_back
can be improved by avoiding the for loop
☀️ Try build successful - checks-azure |
Queued 6f956ef5779fcbc05aaf93da3972715167a03291 with parent 55299b2, future comparison URL. |
@KrishnaSannasi
I agree that's a good idea, but maybe it should be a followup PR? For the moment, I've kept mostly the same style of computation, apart from the direct simplification in state tracking. FWIW, the internal |
@cuviper That sounds like a good idea, can I make a PR after if this one is merged? [ed: I resolved the comments above given this direction ~ scottmcm] |
Sure, it's all yours. |
1188.7% change in deeply-nested-opt!?! |
Oh, that benchmark is this: Box::new(empty()
.chain(empty())
.chain(empty())
.chain(empty())
.chain(empty())
.chain(empty())
.chain(empty())
.chain(empty())
.chain(empty())
.chain(empty())
.chain(empty()) // 10th .chain(empty())
.chain(empty())
.chain(empty())
.chain(empty())
.chain(empty())
.chain(empty())
.chain(empty()) // 16th .chain(empty())
) I guess it makes sense that if this change is going to impact anything, it'd be that 😕 Looks like it's way more for LLVM to chug through, Hmm, come to think of it, (Looking at the bug that added that benchmark, #38528, this is hitting cost in a different part.) |
@cuviper I suppose this is making two changes at once. Your idea to do |
I was extra confused for a bit that it looks like that benchmark only tests construction, but it returns a |
That The original performance problem was up to Rust 1.25, where I get 11.37s for However, I'll file a new issue for that -- something in the compiler needs to be fixed regardless of whether we change The rest of the perf report still showed some slowdowns, though not as drastic, so I'll also explore if there are any other tweaks I can make here. |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
When rebased onto #70750, this PR is down to 0.45s compiling deeply-nested. So the added iterator layers still slow us down, but maybe it's close enough to write off for this pathological test.
See issue #70749 for the hang. With #70750, this PR gets down to 0.54s. |
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.
By letting `Fuse` track iterator exhaustion, the implementation of `Chain` becomes a lot simpler. However, we don't want `FusedIterator` specialization to keep visiting an exhausted side of the chain, especially with nested chains, so a `Defuse` wrapper hides that possibility.
Let's measure again now that #70750 has landed. @bors try @rust-timer queue |
Awaiting bors try build completion |
⌛ Trying commit 1e2a504 with merge 601c05538b7638fb0937bd659aab41fdfb41444d... |
☀️ Try build successful - checks-azure |
Queued 601c05538b7638fb0937bd659aab41fdfb41444d with parent 4015890, future comparison URL. |
Finished benchmarking try commit 601c05538b7638fb0937bd659aab41fdfb41444d, comparison URL. |
|
I've posted an alternate in #70896 -- the code is not as pretty as the free |
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
#70896 has merged, so I'm closing this. |
Hides default fns inside Fuse impl to avoid exposing it to any crate Fixes rust-lang#70796 @cuviper I've added some default, private traits to do the job for us. If required, I can expose them to a specific visibility if you want to call these functions for rust-lang#70332 r? @cuviper
This change was inspired by the proposal on the internals forum.
By implementing
Chain
withFuse
s, it no longer has to juggle the exhaustion state of each of its iterators, greatly simplifying the implementation. However, there was some concern that nested chains could wastefully recurse into exhausted sides whenFusedIterator
specialization is involved, so that possibility is hidden by a simpleDefuse
wrapper.r? @scottmcm
(edit: the original "Fuse with Option" part of this PR was merged in #70366.)