Rollup merge of #68857 - Marwes:allocations, r=matthewjasper

perf: Reduce Vec allocations in normalization by passing &mut Vec

Complicates the code a bit but allocation/freeing were a few percent of the overall runtime in trait heavy code.
This commit is contained in:
Dylan DPC 2020-02-09 00:53:53 +01:00 committed by GitHub
commit 664d87f9b1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 100 additions and 61 deletions

View File

@ -50,7 +50,9 @@
pub use self::object_safety::ObjectSafetyViolation;
pub use self::on_unimplemented::{OnUnimplementedDirective, OnUnimplementedNote};
pub use self::project::MismatchedProjectionTypes;
pub use self::project::{normalize, normalize_projection_type, poly_project_and_unify_type};
pub use self::project::{
normalize, normalize_projection_type, normalize_to, poly_project_and_unify_type,
};
pub use self::project::{Normalized, ProjectionCache, ProjectionCacheSnapshot};
pub use self::select::{IntercrateAmbiguityCause, SelectionContext};
pub use self::specialize::find_associated_item;

View File

@ -216,7 +216,22 @@ pub fn normalize<'a, 'b, 'tcx, T>(
where
T: TypeFoldable<'tcx>,
{
normalize_with_depth(selcx, param_env, cause, 0, value)
let mut obligations = Vec::new();
let value = normalize_to(selcx, param_env, cause, value, &mut obligations);
Normalized { value, obligations }
}
pub fn normalize_to<'a, 'b, 'tcx, T>(
selcx: &'a mut SelectionContext<'b, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
cause: ObligationCause<'tcx>,
value: &T,
obligations: &mut Vec<PredicateObligation<'tcx>>,
) -> T
where
T: TypeFoldable<'tcx>,
{
normalize_with_depth_to(selcx, param_env, cause, 0, value, obligations)
}
/// As `normalize`, but with a custom depth.
@ -227,11 +242,27 @@ pub fn normalize_with_depth<'a, 'b, 'tcx, T>(
depth: usize,
value: &T,
) -> Normalized<'tcx, T>
where
T: TypeFoldable<'tcx>,
{
let mut obligations = Vec::new();
let value = normalize_with_depth_to(selcx, param_env, cause, depth, value, &mut obligations);
Normalized { value, obligations }
}
pub fn normalize_with_depth_to<'a, 'b, 'tcx, T>(
selcx: &'a mut SelectionContext<'b, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
cause: ObligationCause<'tcx>,
depth: usize,
value: &T,
obligations: &mut Vec<PredicateObligation<'tcx>>,
) -> T
where
T: TypeFoldable<'tcx>,
{
debug!("normalize_with_depth(depth={}, value={:?})", depth, value);
let mut normalizer = AssocTypeNormalizer::new(selcx, param_env, cause, depth);
let mut normalizer = AssocTypeNormalizer::new(selcx, param_env, cause, depth, obligations);
let result = normalizer.fold(value);
debug!(
"normalize_with_depth: depth={} result={:?} with {} obligations",
@ -240,14 +271,14 @@ pub fn normalize_with_depth<'a, 'b, 'tcx, T>(
normalizer.obligations.len()
);
debug!("normalize_with_depth: depth={} obligations={:?}", depth, normalizer.obligations);
Normalized { value: result, obligations: normalizer.obligations }
result
}
struct AssocTypeNormalizer<'a, 'b, 'tcx> {
selcx: &'a mut SelectionContext<'b, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
cause: ObligationCause<'tcx>,
obligations: Vec<PredicateObligation<'tcx>>,
obligations: &'a mut Vec<PredicateObligation<'tcx>>,
depth: usize,
}
@ -257,8 +288,9 @@ fn new(
param_env: ty::ParamEnv<'tcx>,
cause: ObligationCause<'tcx>,
depth: usize,
obligations: &'a mut Vec<PredicateObligation<'tcx>>,
) -> AssocTypeNormalizer<'a, 'b, 'tcx> {
AssocTypeNormalizer { selcx, param_env, cause, obligations: vec![], depth }
AssocTypeNormalizer { selcx, param_env, cause, obligations, depth }
}
fn fold<T: TypeFoldable<'tcx>>(&mut self, value: &T) -> T {
@ -343,7 +375,7 @@ fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
);
debug!(
"AssocTypeNormalizer: depth={} normalized {:?} to {:?}, \
now with {} obligations",
now with {} obligations",
self.depth,
ty,
normalized_ty,
@ -441,8 +473,8 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
debug!(
"opt_normalize_projection_type(\
projection_ty={:?}, \
depth={})",
projection_ty={:?}, \
depth={})",
projection_ty, depth
);
@ -469,7 +501,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
// changes
debug!(
"opt_normalize_projection_type: \
found cache entry: ambiguous"
found cache entry: ambiguous"
);
if !projection_ty.has_closure_types() {
return None;
@ -498,7 +530,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
debug!(
"opt_normalize_projection_type: \
found cache entry: in-progress"
found cache entry: in-progress"
);
// But for now, let's classify this as an overflow:
@ -521,7 +553,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
// evaluations can causes ICEs (e.g., #43132).
debug!(
"opt_normalize_projection_type: \
found normalized ty `{:?}`",
found normalized ty `{:?}`",
ty
);
@ -546,7 +578,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
Err(ProjectionCacheEntry::Error) => {
debug!(
"opt_normalize_projection_type: \
found error"
found error"
);
let result = normalize_to_error(selcx, param_env, projection_ty, cause, depth);
obligations.extend(result.obligations);
@ -567,23 +599,28 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
debug!(
"opt_normalize_projection_type: \
projected_ty={:?} \
depth={} \
projected_obligations={:?}",
projected_ty={:?} \
depth={} \
projected_obligations={:?}",
projected_ty, depth, projected_obligations
);
let result = if projected_ty.has_projections() {
let mut normalizer = AssocTypeNormalizer::new(selcx, param_env, cause, depth + 1);
let mut normalizer = AssocTypeNormalizer::new(
selcx,
param_env,
cause,
depth + 1,
&mut projected_obligations,
);
let normalized_ty = normalizer.fold(&projected_ty);
debug!(
"opt_normalize_projection_type: \
normalized_ty={:?} depth={}",
normalized_ty={:?} depth={}",
normalized_ty, depth
);
projected_obligations.extend(normalizer.obligations);
Normalized { value: normalized_ty, obligations: projected_obligations }
} else {
Normalized { value: projected_ty, obligations: projected_obligations }
@ -597,7 +634,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
Ok(ProjectedTy::NoProgress(projected_ty)) => {
debug!(
"opt_normalize_projection_type: \
projected_ty={:?} no progress",
projected_ty={:?} no progress",
projected_ty
);
let result = Normalized { value: projected_ty, obligations: vec![] };
@ -608,7 +645,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
Err(ProjectionTyError::TooManyCandidates) => {
debug!(
"opt_normalize_projection_type: \
too many candidates"
too many candidates"
);
infcx.projection_cache.borrow_mut().ambiguous(cache_key);
None
@ -930,7 +967,7 @@ fn assemble_candidates_from_predicates<'cx, 'tcx, I>(
debug!(
"assemble_candidates_from_predicates: candidate={:?} \
is_match={} same_def_id={}",
is_match={} same_def_id={}",
data, is_match, same_def_id
);
@ -1192,7 +1229,7 @@ fn confirm_object_candidate<'cx, 'tcx>(
None => {
debug!(
"confirm_object_candidate: no env-predicate \
found in object type `{:?}`; ill-formed",
found in object type `{:?}`; ill-formed",
object_ty
);
return Progress::error(selcx.tcx());

View File

@ -9,7 +9,9 @@
use super::coherence::{self, Conflict};
use super::project;
use super::project::{normalize_with_depth, Normalized, ProjectionCacheKey};
use super::project::{
normalize_with_depth, normalize_with_depth_to, Normalized, ProjectionCacheKey,
};
use super::util;
use super::util::{closure_trait_ref_and_return_type, predicate_for_trait_def};
use super::wf;
@ -1019,7 +1021,7 @@ fn filter_negative_and_reservation_impls(
if let Some(value) = value {
debug!(
"filter_negative_and_reservation_impls: \
reservation impl ambiguity on {:?}",
reservation impl ambiguity on {:?}",
def_id
);
intercrate_ambiguity_clauses.push(
@ -1317,7 +1319,7 @@ fn insert_candidate_cache(
if !self.can_cache_candidate(&candidate) {
debug!(
"insert_candidate_cache(trait_ref={:?}, candidate={:?} -\
candidate is not cacheable",
candidate is not cacheable",
trait_ref, candidate
);
return;
@ -3484,25 +3486,23 @@ fn impl_or_trait_obligations(
// that order.
let predicates = tcx.predicates_of(def_id);
assert_eq!(predicates.parent, None);
let mut predicates: Vec<_> = predicates
.predicates
.iter()
.flat_map(|(predicate, _)| {
let predicate = normalize_with_depth(
self,
param_env,
cause.clone(),
recursion_depth,
&predicate.subst(tcx, substs),
);
predicate.obligations.into_iter().chain(Some(Obligation {
cause: cause.clone(),
recursion_depth,
param_env,
predicate: predicate.value,
}))
})
.collect();
let mut obligations = Vec::new();
for (predicate, _) in predicates.predicates {
let predicate = normalize_with_depth_to(
self,
param_env,
cause.clone(),
recursion_depth,
&predicate.subst(tcx, substs),
&mut obligations,
);
obligations.push(Obligation {
cause: cause.clone(),
recursion_depth,
param_env,
predicate,
});
}
// We are performing deduplication here to avoid exponential blowups
// (#38528) from happening, but the real cause of the duplication is
@ -3513,20 +3513,20 @@ fn impl_or_trait_obligations(
// This code is hot enough that it's worth avoiding the allocation
// required for the FxHashSet when possible. Special-casing lengths 0,
// 1 and 2 covers roughly 75-80% of the cases.
if predicates.len() <= 1 {
if obligations.len() <= 1 {
// No possibility of duplicates.
} else if predicates.len() == 2 {
} else if obligations.len() == 2 {
// Only two elements. Drop the second if they are equal.
if predicates[0] == predicates[1] {
predicates.truncate(1);
if obligations[0] == obligations[1] {
obligations.truncate(1);
}
} else {
// Three or more elements. Use a general deduplication process.
let mut seen = FxHashSet::default();
predicates.retain(|i| seen.insert(i.clone()));
obligations.retain(|i| seen.insert(i.clone()));
}
predicates
obligations
}
}

View File

@ -8,7 +8,6 @@
use rustc_hir::def_id::DefId;
use rustc_span::symbol::{kw, Ident};
use rustc_span::Span;
use std::iter::once;
/// Returns the set of obligations needed to make `ty` well-formed.
/// If `ty` contains unresolved inference variables, this may include
@ -26,6 +25,7 @@ pub fn obligations<'a, 'tcx>(
let mut wf = WfPredicates { infcx, param_env, body_id, span, out: vec![], item: None };
if wf.compute(ty) {
debug!("wf::obligations({:?}, body_id={:?}) = {:?}", ty, body_id, wf.out);
let result = wf.normalize();
debug!("wf::obligations({:?}, body_id={:?}) ~~> {:?}", ty, body_id, result);
Some(result)
@ -143,15 +143,15 @@ fn normalize(&mut self) -> Vec<traits::PredicateObligation<'tcx>> {
let cause = self.cause(traits::MiscObligation);
let infcx = &mut self.infcx;
let param_env = self.param_env;
self.out
.iter()
.inspect(|pred| assert!(!pred.has_escaping_bound_vars()))
.flat_map(|pred| {
let mut selcx = traits::SelectionContext::new(infcx);
let pred = traits::normalize(&mut selcx, param_env, cause.clone(), pred);
once(pred.value).chain(pred.obligations)
})
.collect()
let mut obligations = Vec::new();
self.out.iter().inspect(|pred| assert!(!pred.has_escaping_bound_vars())).for_each(|pred| {
let mut selcx = traits::SelectionContext::new(infcx);
let i = obligations.len();
let value =
traits::normalize_to(&mut selcx, param_env, cause.clone(), pred, &mut obligations);
obligations.insert(i, value);
});
obligations
}
/// Pushes the obligations required for `trait_ref` to be WF into `self.out`.