-
Notifications
You must be signed in to change notification settings - Fork 13.2k
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
mir_build: Avoid some useless work when visiting "primary" bindings #137465
Conversation
e7489a4
to
1d8a6e7
Compare
My earlier drafts didn't have any measurable perf effect, but let's try the real thing just in case: @bors try @rust-timer queue |
This comment has been minimized.
This comment has been minimized.
mir_build: Avoid some useless work when visiting "primary" bindings While looking over `visit_primary_bindings`, I noticed that it does a bunch of extra work to build up a collection of “user-type projections”, even though 2/3 of its call sites don't even use them. Those callers can get the same result via `thir::Pat::walk_always`. (And it turns out that doing so also avoids creating some redundant user-type entries in MIR for some binding constructs.) I also noticed that even when the user-type projections *are* used, the process of building them ends up eagerly cloning some nested vectors at every recursion step, even in cases where they won't be used because the current subpattern has no bindings. To avoid this, the visit method now assembles a linked list on the stack containing the information that *would* be needed to create projections, and only creates the concrete projections as needed when a primary binding is encountered. Some relevant prior PRs: - rust-lang#55274 - rust-lang@0bfe184 in rust-lang#55937
☀️ Try build successful - checks-actions |
This comment has been minimized.
This comment has been minimized.
Finished benchmarking commit (cde7127): comparison URL. Overall result: ✅ improvements - no action neededBenchmarking this pull request likely means that it is perf-sensitive, so we're automatically marking it as not fit for rolling up. While you can manually mark this PR as fit for rollup, we strongly recommend not doing so since this PR may lead to changes in compiler perf. @bors rollup=never Instruction countThis is the most reliable metric that we have; it was used to determine the overall result at the top of this comment. However, even this metric can sometimes exhibit noise.
Max RSS (memory usage)This benchmark run did not return any relevant results for this metric. CyclesResults (secondary -4.0%)This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.
Binary sizeThis benchmark run did not return any relevant results for this metric. Bootstrap: 770.304s -> 770.384s (0.01%) |
@bors rollup=maybe |
(Rebased; no changes.) |
/// onto `canonical_user_type_annotations`, so that they end up in MIR | ||
/// even if they aren't associated with any bindings. | ||
#[instrument(level = "debug", skip(self, f))] | ||
fn visit_primary_bindings_special( |
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.
I couldn't come up with a good name for this method, so I punted and went with something that signals “please read the docs for this weird method instead of just calling it”.
One of the problems with the old name (visit_primary_bindings
) was that it sounded like a very normal visitor method, when in fact it was also doing some non-trivial extra stuff, and I think that's how we ended up in a situation where it was being called in other places that didn't need the extra stuff.
Some changes occurred in match lowering cc @Nadrieril |
☔ The latest upstream changes (presumably #138464) made this pull request unmergeable. Please resolve the merge conflicts. |
This avoids the need to unwrap an option after ensuring that it is some.
The existing method does some non-obvious extra work to collect user types and build user-type projections, which is specifically needed by `declare_bindings` and not by the other two callers.
Rebased to resolve trivial import conflict. |
let ops_reversed = self.iter_ops_reversed().cloned().collect::<Vec<_>>(); | ||
// The "first" op should always be `PushUserType`. | ||
// Other projections are only added if there is at least one user type. | ||
assert_matches!(ops_reversed.last(), Some(ProjectedUserTypesOp::PushUserType { .. })); | ||
|
||
let mut projections = vec![]; | ||
for op in ops_reversed.into_iter().rev() { |
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.
you could avoid this rev-collect-rev by just asserting that projections
are not empty when pushing projections on them
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.
Hmm, I don't think that's right.
The ops need to be traversed in “forwards” order because each projection needs to be applied to all types that have already been pushed, but not to any types that haven't been pushed yet.
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.
ah right, we can't iterate the other direction unless we use a recursive function to walk the linked list, which has its own problems
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 is somewhat academic, because currently I don't think it's possible for surface syntax to give us a pattern binding within multiple nested user-type-ascriptions. But the existing code handles the fully-general case of multiple ascriptions.)
@bors r+ |
…iaskrgr Rollup of 7 pull requests Successful merges: - rust-lang#133870 (Stabilize `asm_goto` feature gate) - rust-lang#137449 (Denote `ControlFlow` as `#[must_use]`) - rust-lang#137465 (mir_build: Avoid some useless work when visiting "primary" bindings) - rust-lang#138349 (Emit function declarations for functions with `#[linkage="extern_weak"]`) - rust-lang#138412 (Install licenses into `share/doc/rust/licenses`) - rust-lang#138577 (rustdoc-json: Don't also include `#[deprecated]` in `Item::attrs`) - rust-lang#138588 (Avoid double lowering of idents) Failed merges: - rust-lang#138321 ([bootstrap] Distribute split debuginfo if present) r? `@ghost` `@rustbot` modify labels: rollup
Rollup merge of rust-lang#137465 - Zalathar:visit-primary, r=oli-obk mir_build: Avoid some useless work when visiting "primary" bindings While looking over `visit_primary_bindings`, I noticed that it does a bunch of extra work to build up a collection of “user-type projections”, even though 2/3 of its call sites don't even use them. Those callers can get the same result via `thir::Pat::walk_always`. (And it turns out that doing so also avoids creating some redundant user-type entries in MIR for some binding constructs.) I also noticed that even when the user-type projections *are* used, the process of building them ends up eagerly cloning some nested vectors at every recursion step, even in cases where they won't be used because the current subpattern has no bindings. To avoid this, the visit method now assembles a linked list on the stack containing the information that *would* be needed to create projections, and only creates the concrete projections as needed when a primary binding is encountered. Some relevant prior PRs: - rust-lang#55274 - rust-lang@0bfe184 in rust-lang#55937 --- There should be no user-visible change in compiler output.
While looking over
visit_primary_bindings
, I noticed that it does a bunch of extra work to build up a collection of “user-type projections”, even though 2/3 of its call sites don't even use them. Those callers can get the same result viathir::Pat::walk_always
.(And it turns out that doing so also avoids creating some redundant user-type entries in MIR for some binding constructs.)
I also noticed that even when the user-type projections are used, the process of building them ends up eagerly cloning some nested vectors at every recursion step, even in cases where they won't be used because the current subpattern has no bindings. To avoid this, the visit method now assembles a linked list on the stack containing the information that would be needed to create projections, and only creates the concrete projections as needed when a primary binding is encountered.
Some relevant prior PRs:
There should be no user-visible change in compiler output.