Skip to content

perf: avoid re-running the trait-solver fast path on unchanged obligations#22910

Closed
tilladam wants to merge 1 commit into
rust-lang:masterfrom
tilladam:perf/fulfillment-stalled-on-fast-path
Closed

perf: avoid re-running the trait-solver fast path on unchanged obligations#22910
tilladam wants to merge 1 commit into
rust-lang:masterfrom
tilladam:perf/fulfillment-stalled-on-fast-path

Conversation

@tilladam

Copy link
Copy Markdown
Contributor

FulfillmentCtxt::try_evaluate_obligations re-processes every pending ambiguous obligation on every call. For obligations resolved by compute_goal_fast_path, the Certainty::Maybe branch discarded the obligation's GoalStalledOn (re-registering with None), which meant every subsequent call fully re-ran the fast path's predicate resolution (shallow_resolve + is_trivially_wf) instead of being able to skip it, even though the real solver's own internal stall-check exists precisely to avoid this.

I measured the speedup:

  • rust-analyzer: whole-workspace inference was about 1.3–1.4× faster (24.4s → 17–18s); the worst prefill body was 1.79× faster (7.79s → 4.35s).
  • Slint: the worst generate_enums_pyi body was roughly 4.7× faster (~17s → 3.64s).

The bigger problem is that frequent fulfillment calls × rescanning every pending obligation = O(n²), but I'll address that in a separate PR, to keep this minimal.

Tokens were burned in the analysis, profiling and implementation of this PR.

@rustbot rustbot added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Jul 25, 2026
…tions

`FulfillmentCtxt::try_evaluate_obligations` re-processes every pending
ambiguous obligation on every call. For obligations resolved by
`compute_goal_fast_path`, the `Certainty::Maybe` branch discarded the
obligation's `GoalStalledOn` (re-registering with `None`), which meant
every subsequent call fully re-ran the fast path's predicate resolution
(`shallow_resolve` + `is_trivially_wf`) instead of being able to skip it,
even though the real solver's own internal stall-check exists precisely
to avoid this.

Reconstruct a `GoalStalledOn` for these obligations by walking the
goal's predicate for the inference variables it depends on
(`fast_path_stalled_on`), and check it up front with the same primitives
the solver's internal fast path already uses (`is_still_stalled`), so an
unchanged obligation is skipped before touching the solver at all.

This is a constant-factor fix, not asymptotic (the per-call scan over
all pending obligations remains O(n)). Measured: prefill 7.79s -> 4.35s
(1.79x), full self-analysis inference 24.4s -> ~17-18s (~1.4x), up to
3.9x per-obligation on a synthetic worst case. Full-repo differential
(unknown type / type mismatches / pattern unknown type / pattern type
mismatches / mir failed bodies / failed const evals) identical
before/after; `cargo test -p hir-ty` 1015/0 both ways; clippy clean.
@tilladam
tilladam force-pushed the perf/fulfillment-stalled-on-fast-path branch from e616aae to 041e6ab Compare July 25, 2026 12:41
@ChayimFriedman2

Copy link
Copy Markdown
Contributor

As I said in #22911 (comment):

This code is copied from rustc and we will absolutely not change it from what rustc does. If rustc was already changed and we haven't followed, please point at the rustc code. If not, the only way for this to be accepted is to first suggest this to rust-lang/rust.

@ChayimFriedman2

Copy link
Copy Markdown
Contributor

Also note that the rustc people may not be happy if someone that may not understand the type system well suggest LLM changes to the solver, and also, if you used a LLM to author the PR description, that we disallow that.

@tilladam

Copy link
Copy Markdown
Contributor Author

As I said in #22911 (comment):

This code is copied from rustc and we will absolutely not change it from what rustc does. If rustc was already changed and we haven't followed, please point at the rustc code. If not, the only way for this to be accepted is to first suggest this to rust-lang/rust.

Turns out this issue was fixed similarly in rust-lang/rust#158249 in rustc. I guess we can drop this and wait for that to be pulled in?

@tilladam

Copy link
Copy Markdown
Contributor Author

Also note that the rustc people may not be happy if someone that may not understand the type system well suggest LLM changes to the solver, and also, if you used a LLM to author the PR description, that we disallow that.

I'm aware of the policy and was not attempting to circumvent it. I did copy and paste the benchmark numbers, assuming that was acceptable.

@tilladam tilladam closed this Jul 25, 2026
@rustbot rustbot removed the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Jul 25, 2026
@ChayimFriedman2

Copy link
Copy Markdown
Contributor

Turns out this issue was fixed similarly in rust-lang/rust#158249 in rustc. I guess we can drop this and wait for that to be pulled in?

Looks so, yes. This doesn't seem to require additional changes on our side.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants