Skip to content

Commit

Permalink
Optimize obligation gathering.
Browse files Browse the repository at this point in the history
By passing in a `&mut Vec` in various places and appending to it, rather
than building many tiny `Vec`'s and then combining them.

This removes about 20% of the allocations occurred in a `check` build of
a test program from rust-lang#87012, making it roughly 2% faster.
  • Loading branch information
nnethercote committed Apr 5, 2022
1 parent 6a9080b commit 528b6a4
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 38 deletions.
50 changes: 27 additions & 23 deletions compiler/rustc_trait_selection/src/traits/select/confirmation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
debug!(?obligation, ?has_nested, "confirm_builtin_candidate");

let lang_items = self.tcx().lang_items();
let obligations = if has_nested {
let mut obligations = vec![];
if has_nested {
let trait_def = obligation.predicate.def_id();
let conditions = if Some(trait_def) == lang_items.sized_trait() {
self.sized_conditions(obligation)
Expand All @@ -276,10 +277,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
obligation.recursion_depth + 1,
trait_def,
nested,
&mut obligations,
)
})
} else {
vec![]
};

debug!(?obligations);
Expand Down Expand Up @@ -315,32 +315,32 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
ensure_sufficient_stack(|| {
let cause = obligation.derived_cause(BuiltinDerivedObligation);

let trait_obligations: Vec<PredicateObligation<'_>> =
self.infcx.commit_unconditionally(|_| {
let poly_trait_ref = obligation.predicate.to_poly_trait_ref();
let trait_ref = self.infcx.replace_bound_vars_with_placeholders(poly_trait_ref);
self.impl_or_trait_obligations(
&cause,
obligation.recursion_depth + 1,
obligation.param_env,
trait_def_id,
&trait_ref.substs,
obligation.predicate,
)
});
let mut obligations = vec![];
self.infcx.commit_unconditionally(|_| {
let poly_trait_ref = obligation.predicate.to_poly_trait_ref();
let trait_ref = self.infcx.replace_bound_vars_with_placeholders(poly_trait_ref);
// Adds the predicates from the trait. Note that this contains a `Self: Trait`
// predicate as usual. It will have no effect because auto traits are coinductive.
self.impl_or_trait_obligations(
&cause,
obligation.recursion_depth + 1,
obligation.param_env,
trait_def_id,
&trait_ref.substs,
obligation.predicate,
&mut obligations,
)
});

let mut obligations = self.collect_predicates_for_types(
self.collect_predicates_for_types(
obligation.param_env,
cause,
obligation.recursion_depth + 1,
trait_def_id,
nested,
&mut obligations,
);

// Adds the predicates from the trait. Note that this contains a `Self: Trait`
// predicate as usual. It won't have any effect since auto traits are coinductive.
obligations.extend(trait_obligations);

debug!(?obligations, "vtable_auto_impl");

ImplSourceAutoImplData { trait_def_id, nested: obligations }
Expand Down Expand Up @@ -383,13 +383,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
) -> ImplSourceUserDefinedData<'tcx, PredicateObligation<'tcx>> {
debug!(?impl_def_id, ?substs, ?recursion_depth, "vtable_impl");

let mut impl_obligations = self.impl_or_trait_obligations(
let mut impl_obligations = vec![];
self.impl_or_trait_obligations(
cause,
recursion_depth,
param_env,
impl_def_id,
&substs.value,
parent_trait_pred,
&mut impl_obligations,
);

debug!(?impl_obligations, "vtable_impl");
Expand Down Expand Up @@ -623,13 +625,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
let trait_def_id = trait_ref.def_id;
let substs = trait_ref.substs;

let trait_obligations = self.impl_or_trait_obligations(
let mut trait_obligations = vec![];
self.impl_or_trait_obligations(
&obligation.cause,
obligation.recursion_depth,
obligation.param_env,
trait_def_id,
&substs,
obligation.predicate,
&mut trait_obligations,
);

debug!(?trait_def_id, ?trait_obligations, "trait alias obligations");
Expand Down
33 changes: 18 additions & 15 deletions compiler/rustc_trait_selection/src/traits/select/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ use rustc_middle::ty::subst::{GenericArgKind, Subst, SubstsRef};
use rustc_middle::ty::{self, PolyProjectionPredicate, ToPolyTraitRef, ToPredicate};
use rustc_middle::ty::{Ty, TyCtxt, TypeFoldable};
use rustc_span::symbol::sym;
use smallvec::SmallVec;

use std::cell::{Cell, RefCell};
use std::cmp;
Expand Down Expand Up @@ -1360,7 +1361,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
fn match_projection_obligation_against_definition_bounds(
&mut self,
obligation: &TraitObligation<'tcx>,
) -> smallvec::SmallVec<[usize; 2]> {
) -> SmallVec<[usize; 2]> {
let poly_trait_predicate = self.infcx().resolve_vars_if_possible(obligation.predicate);
let placeholder_trait_predicate =
self.infcx().replace_bound_vars_with_placeholders(poly_trait_predicate);
Expand Down Expand Up @@ -2016,7 +2017,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
recursion_depth: usize,
trait_def_id: DefId,
types: ty::Binder<'tcx, Vec<Ty<'tcx>>>,
) -> Vec<PredicateObligation<'tcx>> {
out: &mut Vec<PredicateObligation<'tcx>>,
) {
// Because the types were potentially derived from
// higher-ranked obligations they may reference late-bound
// regions. For example, `for<'a> Foo<&'a i32> : Copy` would
Expand All @@ -2030,17 +2032,20 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
// `for<'a> &'a i32` becomes `&0 i32`.
// 2. Produce something like `&'0 i32 : Copy`
// 3. Re-bind the regions back to `for<'a> &'a i32 : Copy`

//
// This function is called enough that it's better to append to a `&mut
// Vec` than return a `Vec`, to avoid allocations.
types
.as_ref()
.skip_binder() // binder moved -\
.iter()
.flat_map(|ty| {
.for_each(|ty| {
let ty: ty::Binder<'tcx, Ty<'tcx>> = types.rebind(*ty); // <----/

self.infcx.commit_unconditionally(|_| {
let placeholder_ty = self.infcx.replace_bound_vars_with_placeholders(ty);
let Normalized { value: normalized_ty, mut obligations } =
// `obligations` is almost always empty.
let Normalized { value: normalized_ty, obligations } =
ensure_sufficient_stack(|| {
project::normalize_with_depth(
self,
Expand All @@ -2050,6 +2055,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
placeholder_ty,
)
});
out.extend(obligations);
let placeholder_obligation = predicate_for_trait_def(
self.tcx(),
param_env,
Expand All @@ -2059,11 +2065,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
normalized_ty,
&[],
);
obligations.push(placeholder_obligation);
obligations
out.push(placeholder_obligation);
})
})
.collect()
});
}

///////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -2337,7 +2341,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
def_id: DefId, // of impl or trait
substs: SubstsRef<'tcx>, // for impl or trait
parent_trait_pred: ty::Binder<'tcx, ty::TraitPredicate<'tcx>>,
) -> Vec<PredicateObligation<'tcx>> {
out: &mut Vec<PredicateObligation<'tcx>>,
) {
let tcx = self.tcx();

// To allow for one-pass evaluation of the nested obligation,
Expand All @@ -2357,7 +2362,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
let predicates = tcx.predicates_of(def_id);
debug!(?predicates);
assert_eq!(predicates.parent, None);
let mut obligations = Vec::with_capacity(predicates.predicates.len());
out.reserve(predicates.predicates.len());
let parent_code = cause.clone_code();
for (predicate, span) in predicates.predicates {
let span = *span;
Expand All @@ -2375,12 +2380,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
cause.clone(),
recursion_depth,
predicate.subst(tcx, substs),
&mut obligations,
out,
);
obligations.push(Obligation { cause, recursion_depth, param_env, predicate });
out.push(Obligation { cause, recursion_depth, param_env, predicate });
}

obligations
}
}

Expand Down

0 comments on commit 528b6a4

Please sign in to comment.