perf: avoid re-running the trait-solver fast path on unchanged obligations#22910
perf: avoid re-running the trait-solver fast path on unchanged obligations#22910tilladam wants to merge 1 commit into
Conversation
…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.
e616aae to
041e6ab
Compare
|
As I said in #22911 (comment):
|
|
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. |
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? |
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. |
Looks so, yes. This doesn't seem to require additional changes on our side. |
FulfillmentCtxt::try_evaluate_obligationsre-processes every pending ambiguous obligation on every call. For obligations resolved bycompute_goal_fast_path, theCertainty::Maybebranch discarded the obligation'sGoalStalledOn(re-registering withNone), 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:
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.