Skip to content

Commit 613c078

Browse files
committedNov 29, 2024
Auto merge of rust-lang#133566 - lcnr:fast-reject-perf, r=<try>
fast-reject: add cache slightly modified version of rust-lang#133524 I originally wanted to simply bail after recursion for a certain amount of times, however, looking at the number of steps taken while compiling different crates we get the following results[^1]: typenum ```rust 1098842 counts ( 1) 670511 (61.0%, 61.0%): dropping after 1 ( 2) 358785 (32.7%, 93.7%): dropping after 0 ( 3) 25191 ( 2.3%, 96.0%): dropping after 2 ( 4) 10912 ( 1.0%, 97.0%): dropping after 4 ( 5) 6461 ( 0.6%, 97.5%): dropping after 3 ( 6) 5239 ( 0.5%, 98.0%): dropping after 5 ( 7) 2528 ( 0.2%, 98.3%): dropping after 8 ( 8) 2188 ( 0.2%, 98.5%): dropping after 1094 ( 9) 2097 ( 0.2%, 98.6%): dropping after 6 ( 10) 1179 ( 0.1%, 98.7%): dropping after 34 ( 11) 1148 ( 0.1%, 98.9%): dropping after 7 ( 12) 822 ( 0.1%, 98.9%): dropping after 10 ``` bitmaps ```rust 533346 counts ( 1) 526166 (98.7%, 98.7%): dropping after 1 ( 2) 4562 ( 0.9%, 99.5%): dropping after 0 ( 3) 2072 ( 0.4%, 99.9%): dropping after 1024 ( 4) 305 ( 0.1%,100.0%): dropping after 2 ( 5) 106 ( 0.0%,100.0%): dropping after 4 ( 6) 30 ( 0.0%,100.0%): dropping after 8 ( 7) 18 ( 0.0%,100.0%): dropping after 3 ( 8) 17 ( 0.0%,100.0%): dropping after 44 ( 9) 15 ( 0.0%,100.0%): dropping after 168 ( 10) 8 ( 0.0%,100.0%): dropping after 14 ( 11) 7 ( 0.0%,100.0%): dropping after 13 ( 12) 7 ( 0.0%,100.0%): dropping after 24 ``` stage 2 compiler is mostly trivial, but has a few cases where we get >5000 ```rust 12987156 counts ( 1) 9280476 (71.5%, 71.5%): dropping after 0 ( 2) 2277841 (17.5%, 89.0%): dropping after 1 ( 3) 724888 ( 5.6%, 94.6%): dropping after 2 ( 4) 204005 ( 1.6%, 96.2%): dropping after 4 ( 5) 146537 ( 1.1%, 97.3%): dropping after 3 ( 6) 64287 ( 0.5%, 97.8%): dropping after 5 ( 7) 43938 ( 0.3%, 98.1%): dropping after 6 ( 8) 43758 ( 0.3%, 98.4%): dropping after 8 ( 9) 27220 ( 0.2%, 98.7%): dropping after 7 ( 10) 17374 ( 0.1%, 98.8%): dropping after 9 ( 11) 16015 ( 0.1%, 98.9%): dropping after 10 ( 12) 12855 ( 0.1%, 99.0%): dropping after 12 ( 13) 10494 ( 0.1%, 99.1%): dropping after 11 ( 14) 7553 ( 0.1%, 99.2%): dropping after 14 ``` Given that we have crates which frequently rely on fairly deep recursion, actually using a cache seems better than using an arbitrary cutoff here. Having an impl which is large enough to trigger a cutoff instead of getting rejected noticeably impacts perf, so just using a cache in these cases seems better to me. Does not matter too much in the end, we only have to make sure we don't regress crates which don't recurse deeply. [^1]: i've incremented a counter in the place I now call `if cache.get(&(lhs, rhs))` and then printed it on drop r? `@compiler-errors`
2 parents cb2bd2b + f6f4396 commit 613c078

File tree

6 files changed

+30
-16
lines changed

6 files changed

+30
-16
lines changed
 

‎compiler/rustc_trait_selection/src/traits/coherence.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ pub fn overlapping_impls(
9898
// Before doing expensive operations like entering an inference context, do
9999
// a quick check via fast_reject to tell if the impl headers could possibly
100100
// unify.
101-
let drcx = DeepRejectCtxt::relate_infer_infer(tcx);
101+
let mut drcx = DeepRejectCtxt::relate_infer_infer(tcx);
102102
let impl1_ref = tcx.impl_trait_ref(impl1_def_id);
103103
let impl2_ref = tcx.impl_trait_ref(impl2_def_id);
104104
let may_overlap = match (impl1_ref, impl2_ref) {

‎compiler/rustc_trait_selection/src/traits/effects.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ fn evaluate_host_effect_from_bounds<'tcx>(
7979
obligation: &HostEffectObligation<'tcx>,
8080
) -> Result<ThinVec<PredicateObligation<'tcx>>, EvaluationFailure> {
8181
let infcx = selcx.infcx;
82-
let drcx = DeepRejectCtxt::relate_rigid_rigid(selcx.tcx());
82+
let mut drcx = DeepRejectCtxt::relate_rigid_rigid(selcx.tcx());
8383
let mut candidate = None;
8484

8585
for predicate in obligation.param_env.caller_bounds() {

‎compiler/rustc_trait_selection/src/traits/project.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -866,7 +866,7 @@ fn assemble_candidates_from_predicates<'cx, 'tcx>(
866866
potentially_unnormalized_candidates: bool,
867867
) {
868868
let infcx = selcx.infcx;
869-
let drcx = DeepRejectCtxt::relate_rigid_rigid(selcx.tcx());
869+
let mut drcx = DeepRejectCtxt::relate_rigid_rigid(selcx.tcx());
870870
for predicate in env_predicates {
871871
let bound_predicate = predicate.kind();
872872
if let ty::ClauseKind::Projection(data) = predicate.kind().skip_binder() {

‎compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
232232
.filter(|p| p.def_id() == stack.obligation.predicate.def_id())
233233
.filter(|p| p.polarity() == stack.obligation.predicate.polarity());
234234

235-
let drcx = DeepRejectCtxt::relate_rigid_rigid(self.tcx());
235+
let mut drcx = DeepRejectCtxt::relate_rigid_rigid(self.tcx());
236236
let obligation_args = stack.obligation.predicate.skip_binder().trait_ref.args;
237237
// Keep only those bounds which may apply, and propagate overflow if it occurs.
238238
for bound in bounds {
@@ -548,7 +548,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
548548
obligation: &PolyTraitObligation<'tcx>,
549549
candidates: &mut SelectionCandidateSet<'tcx>,
550550
) {
551-
let drcx = DeepRejectCtxt::relate_rigid_infer(self.tcx());
551+
let mut drcx = DeepRejectCtxt::relate_rigid_infer(self.tcx());
552552
let obligation_args = obligation.predicate.skip_binder().trait_ref.args;
553553
self.tcx().for_each_relevant_impl(
554554
obligation.predicate.def_id(),

‎compiler/rustc_type_ir/src/fast_reject.rs

+23-9
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher, ToStableHas
1111
#[cfg(feature = "nightly")]
1212
use rustc_macros::{HashStable_NoContext, TyDecodable, TyEncodable};
1313

14+
use crate::data_structures::DelayedSet;
1415
use crate::inherent::*;
1516
use crate::visit::TypeVisitableExt as _;
1617
use crate::{self as ty, Interner};
@@ -181,41 +182,44 @@ impl<DefId> SimplifiedType<DefId> {
181182
/// We also use this function during coherence. For coherence the
182183
/// impls only have to overlap for some value, so we treat parameters
183184
/// on both sides like inference variables.
184-
#[derive(Debug, Clone, Copy)]
185+
#[derive(Debug)]
185186
pub struct DeepRejectCtxt<
186187
I: Interner,
187188
const INSTANTIATE_LHS_WITH_INFER: bool,
188189
const INSTANTIATE_RHS_WITH_INFER: bool,
189190
> {
190191
_interner: PhantomData<I>,
192+
/// We use a cache here as exponentially large - but self-similar - types otherwise
193+
/// cause hangs, e.g. when compiling itertools with the `-Znext-solver`.
194+
cache: DelayedSet<(I::Ty, I::Ty)>,
191195
}
192196

193197
impl<I: Interner> DeepRejectCtxt<I, false, false> {
194198
/// Treat parameters in both the lhs and the rhs as rigid.
195199
pub fn relate_rigid_rigid(_interner: I) -> DeepRejectCtxt<I, false, false> {
196-
DeepRejectCtxt { _interner: PhantomData }
200+
DeepRejectCtxt { _interner: PhantomData, cache: Default::default() }
197201
}
198202
}
199203

200204
impl<I: Interner> DeepRejectCtxt<I, true, true> {
201205
/// Treat parameters in both the lhs and the rhs as infer vars.
202206
pub fn relate_infer_infer(_interner: I) -> DeepRejectCtxt<I, true, true> {
203-
DeepRejectCtxt { _interner: PhantomData }
207+
DeepRejectCtxt { _interner: PhantomData, cache: Default::default() }
204208
}
205209
}
206210

207211
impl<I: Interner> DeepRejectCtxt<I, false, true> {
208212
/// Treat parameters in the lhs as rigid, and in rhs as infer vars.
209213
pub fn relate_rigid_infer(_interner: I) -> DeepRejectCtxt<I, false, true> {
210-
DeepRejectCtxt { _interner: PhantomData }
214+
DeepRejectCtxt { _interner: PhantomData, cache: Default::default() }
211215
}
212216
}
213217

214218
impl<I: Interner, const INSTANTIATE_LHS_WITH_INFER: bool, const INSTANTIATE_RHS_WITH_INFER: bool>
215219
DeepRejectCtxt<I, INSTANTIATE_LHS_WITH_INFER, INSTANTIATE_RHS_WITH_INFER>
216220
{
217221
pub fn args_may_unify(
218-
self,
222+
&mut self,
219223
obligation_args: I::GenericArgs,
220224
impl_args: I::GenericArgs,
221225
) -> bool {
@@ -234,7 +238,7 @@ impl<I: Interner, const INSTANTIATE_LHS_WITH_INFER: bool, const INSTANTIATE_RHS_
234238
})
235239
}
236240

237-
pub fn types_may_unify(self, lhs: I::Ty, rhs: I::Ty) -> bool {
241+
pub fn types_may_unify(&mut self, lhs: I::Ty, rhs: I::Ty) -> bool {
238242
match rhs.kind() {
239243
// Start by checking whether the `rhs` type may unify with
240244
// pretty much everything. Just return `true` in that case.
@@ -273,8 +277,12 @@ impl<I: Interner, const INSTANTIATE_LHS_WITH_INFER: bool, const INSTANTIATE_RHS_
273277
| ty::Placeholder(_) => {}
274278
};
275279

280+
if self.cache.contains(&(lhs, rhs)) {
281+
return true;
282+
}
283+
276284
// For purely rigid types, use structural equivalence.
277-
match lhs.kind() {
285+
let result = match lhs.kind() {
278286
ty::Ref(_, lhs_ty, lhs_mutbl) => match rhs.kind() {
279287
ty::Ref(_, rhs_ty, rhs_mutbl) => {
280288
lhs_mutbl == rhs_mutbl && self.types_may_unify(lhs_ty, rhs_ty)
@@ -414,10 +422,16 @@ impl<I: Interner, const INSTANTIATE_LHS_WITH_INFER: bool, const INSTANTIATE_RHS_
414422
}
415423

416424
ty::Error(..) => true,
425+
};
426+
427+
if result {
428+
self.cache.insert((lhs, rhs));
417429
}
430+
431+
result
418432
}
419433

420-
pub fn consts_may_unify(self, lhs: I::Const, rhs: I::Const) -> bool {
434+
pub fn consts_may_unify(&mut self, lhs: I::Const, rhs: I::Const) -> bool {
421435
match rhs.kind() {
422436
ty::ConstKind::Param(_) => {
423437
if INSTANTIATE_RHS_WITH_INFER {
@@ -465,7 +479,7 @@ impl<I: Interner, const INSTANTIATE_LHS_WITH_INFER: bool, const INSTANTIATE_RHS_
465479
}
466480
}
467481

468-
fn var_and_ty_may_unify(self, var: ty::InferTy, ty: I::Ty) -> bool {
482+
fn var_and_ty_may_unify(&mut self, var: ty::InferTy, ty: I::Ty) -> bool {
469483
if !ty.is_known_rigid() {
470484
return true;
471485
}

‎src/librustdoc/html/render/write_shared.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -918,8 +918,8 @@ impl<'item> DocVisitor<'item> for TypeImplCollector<'_, '_, 'item> {
918918
// Be aware of `tests/rustdoc/type-alias/deeply-nested-112515.rs` which might regress.
919919
let Some(impl_did) = impl_item_id.as_def_id() else { continue };
920920
let for_ty = self.cx.tcx().type_of(impl_did).skip_binder();
921-
let reject_cx = DeepRejectCtxt::relate_infer_infer(self.cx.tcx());
922-
if !reject_cx.types_may_unify(aliased_ty, for_ty) {
921+
let mut drcx = DeepRejectCtxt::relate_infer_infer(self.cx.tcx());
922+
if !drcx.types_may_unify(aliased_ty, for_ty) {
923923
continue;
924924
}
925925
// Avoid duplicates

0 commit comments

Comments
 (0)