Skip to content

perf: Drop irrelevant caller bounds from query type op keys - #79

Draft
xmakro wants to merge 1 commit into
perf/base-0713from
perf/type-op-irrelevant-param-env
Draft

perf: Drop irrelevant caller bounds from query type op keys#79
xmakro wants to merge 1 commit into
perf/base-0713from
perf/type-op-irrelevant-param-env

Conversation

@xmakro

@xmakro xmakro commented Jul 31, 2026

Copy link
Copy Markdown
Owner

Query type ops canonicalize the param env into their cache key, so the same goal asked under two different sets of where-clauses gets two entries. Async trait desugaring makes that expensive: every impl's method carries Self: 'at, which expands to a bound naming that impl's own type, so each impl gets its own key and the work behind it runs once per impl instead of once. Regions are not what matters here, a bound with no regions at all behaves the same way and a bound that is identical in every impl does not.

Most of those bounds cannot take part in the answer. A caller bound is reached only by unifying it with the goal, so a bound naming a type or const parameter is out of reach for a goal that has none, and outlives bounds are never assembled as candidates. When every bound is one of those two, the empty env goes into the key instead. This happens at the single point where every query type op canonicalizes, and the response is still instantiated against the original env, so only the key changes.

Lifetime parameters deliberately do not count as putting a bound out of reach. is_global admits 'static in the goal and unification relates regions freely, so Foo<'a>: Trait really can be selected for Foo<'static>: Trait. Treating a lifetime like a type parameter here makes MIR borrow checking fail to prove goals that HIR typeck accepted:

use std::marker::PhantomData;

trait Trait {}
struct Foo<'a, T>(PhantomData<(&'a (), T)>);

fn g<T>() where Foo<'static, T>: Trait {}

fn f<'a>() where Foo<'a, u8>: Trait, 'a: 'static {
    g::<u8>();
}

Measured locally on a stage2 build with CI LLVM and jemalloc but no PGO or BOLT, so branch-shaped effects will look different upstream: -0.16% over hyper, ripgrep, serde and syn, and -21.5% on a generated file of 80 async-trait impls. rust-timer should settle the crate numbers.

@xmakro xmakro changed the title perf: Drop irrelevant caller bounds from query type op keys perf: Drop irrelevant caller bounds from query type op keys [-0.16% primary; -21.5% async-trait-heavy] Aug 1, 2026
@xmakro
xmakro force-pushed the perf/type-op-irrelevant-param-env branch from a374fe2 to 01d80d0 Compare August 4, 2026 07:22
@xmakro xmakro changed the title perf: Drop irrelevant caller bounds from query type op keys [-0.16% primary; -21.5% async-trait-heavy] perf: Drop irrelevant caller bounds from query type op keys Aug 4, 2026
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.

1 participant