-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
internal: skip associated items and call generic_implements_goal
earlier in method resolution
#16498
internal: skip associated items and call generic_implements_goal
earlier in method resolution
#16498
Conversation
…rlier in method resolution
(Note that these numbers are coming from my M1 Pro-based MacBook Pro. Your experience may vary.) |
This change, however, does regress running Before
After
|
That slow down does not surprise me, trait solving is expensive, and now you are solving before checking the validity of a candidate. I'm more surprised that the change actually helps with completion speed here in your scenario. |
From a quick look we can (very very slightly only) optimize rust-analyzer/crates/hir-ty/src/method_resolution.rs Lines 1403 to 1414 in e2cfed1
rust-analyzer/crates/hir-ty/src/method_resolution.rs Lines 1480 to 1529 in e2cfed1
additionally the visibility check here rust-analyzer/crates/hir-ty/src/method_resolution.rs Lines 1397 to 1402 in e2cfed1
rust-analyzer/crates/hir-ty/src/method_resolution.rs Lines 1440 to 1445 in e2cfed1
should be unnecessary. The visibility of trait assoc items is the visibility of the trait which we can either check once prior to looping, or ignore if the check is implicitly done somewhere already (not 100% about that). This gets rid of a few database accesses as well as inserting extra query dependencies. (Note these are only irrelevant for this one call path here, not in general for the other callers of the |
Going off a previous profile I ran, rust-analyzer is doing a lot of
I'll try out your suggested changes! If it works out well, I'll inline the reduced |
You are correct that the optimizations are slight, but they are noticeable! Thanks for the tip. |
I'm not happy merging the first change of this fwiw. The slow down is just too big for this to be considered. |
Closing this PR in favor of opening a new one in order to make the conversation history more legible. |
…iting-orphan-rules, r=Veykril performance: Speed up Method Completions By Taking Advantage of Orphan Rules (Continues #16498) This PR speeds up method completions by doing two things without regressing `analysis-stats`[^1]: - Filter candidate traits prior to calling `iterate_path_candidates` by relying on orphan rules (see below for a slightly more in-depth explanation). When generating completions [on `slog::Logger`](https://github.com/oxidecomputer/omicron/blob/5e9e59c312cd787ba50cf6776f220951917dfa14/common/src/ledger.rs#L78) in `oxidecomputer/omicron` as a test, this PR halved my completion times—it's now 454ms cold and 281ms warm. Before this PR, it was 808ms cold and 579ms warm. - Inline some of the method candidate checks into `is_valid_method_candidate` and remove some unnecessary visibility checks. This was suggested by `@Veykril` in [this comment](#16498 (comment)). We filter candidate traits by taking advantage of orphan rules. For additional details, I'll rely on `@WaffleLapkin's` explanation [from Zulip](https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Frust-analyzer/topic/Trait.20Checking/near/420942417): > A type `A` can only implements traits which > 1. Have a blanket implementation (`impl<T> Trait for T {}`) > 2. Have implementation for `A` (`impl Trait for A {}`) > > Blanket implementation can only exist in `Trait`'s crate. Implementation for `A` can only exist in `A`'s or `Trait`'s crate. Big thanks to Waffle for its keen observation! --- I think some additional improvements are possible: - `for_trait_and_self_ty` seemingly does not distinguish between `&T`, `&mut T`, or `T`, resulting in seemingly irrelevant traits like `tokio::io::AsyncWrite` being being included for, e.g., `&slog::Logger`. I don't know they're being considered due to the [autoref/autoderef behavior](https://github.com/rust-lang/rust-analyzer/blob/a02a219773629686bd8ff123ca1aa995fa50d976/crates/hir-ty/src/method_resolution.rs#L945-L962), but I wonder if it'd make sense to filter by mutability earlier and not consider trait implementations that require `&mut T` when we only have a `&T`. - The method completions [spend a _lot_ of time in unification](https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Frust-analyzer/topic/Trait.20Checking/near/421072356), and while there might be low-hanging fruit there, it might make more sense to wait for the new trait solver in `rustc`. I dunno. [^1]: The filtering occurs outside of typechecking, after all.
This is a small change whose correctness I'm not entirely convinced of, but based on the discussion in #6432 and my testing in https://github.com/oxidecomputer/omicron, this seems to provide faster completions, especially when it comes to flyimport-based ones.
Without these changes, I'm seeing completions take roughly 808ms cold and 570ms warm. With these changes, I'm seeing completions take 473ms cold and 55-90ms warm.