-
Notifications
You must be signed in to change notification settings - Fork 13.8k
Deduplicate obligations in opt_normalize_projection_type
#91186
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,10 +8,14 @@ mod project; | |
mod structural_impls; | ||
pub mod util; | ||
|
||
use rustc_data_structures::fx::FxHashMap; | ||
use rustc_hir as hir; | ||
use rustc_middle::ty::error::{ExpectedFound, TypeError}; | ||
use rustc_middle::ty::{self, Const, Ty, TyCtxt}; | ||
use rustc_span::Span; | ||
use std::borrow::{Borrow, Cow}; | ||
use std::collections::hash_map::RawEntryMut; | ||
use std::hash::Hash; | ||
|
||
pub use self::FulfillmentErrorCode::*; | ||
pub use self::ImplSource::*; | ||
|
@@ -105,6 +109,63 @@ pub enum FulfillmentErrorCode<'tcx> { | |
CodeAmbiguity, | ||
} | ||
|
||
pub struct ObligationsDedup<'a, 'tcx, T> { | ||
obligations: &'a mut Vec<Obligation<'tcx, T>>, | ||
} | ||
|
||
impl<'a, 'tcx, T: 'tcx> ObligationsDedup<'a, 'tcx, T> | ||
where | ||
T: Clone + Hash + Eq, | ||
{ | ||
pub fn from_vec(vec: &'a mut Vec<Obligation<'tcx, T>>) -> Self { | ||
ObligationsDedup { obligations: vec } | ||
} | ||
|
||
pub fn extend<'b>(&mut self, iter: impl ExactSizeIterator<Item = Cow<'b, Obligation<'tcx, T>>>) | ||
where | ||
'tcx: 'b, | ||
{ | ||
// obligation tracing has shown that initial batches added to an empty vec do not | ||
// contain any duplicates, so there's no need to attempt deduplication | ||
if self.obligations.is_empty() { | ||
*self.obligations = iter.into_iter().map(Cow::into_owned).collect(); | ||
return; | ||
} | ||
|
||
let initial_size = self.obligations.len(); | ||
let current_capacity = self.obligations.capacity(); | ||
let expected_new = iter.len(); | ||
let combined_size = initial_size + expected_new; | ||
|
||
if combined_size <= 16 || combined_size <= current_capacity { | ||
|
||
// small case/not crossing a power of two. don't bother with dedup | ||
self.obligations.extend(iter.map(Cow::into_owned)); | ||
} else { | ||
// crossing power of two threshold. this would incur a vec growth anyway if we didn't do | ||
// anything. piggyback a dedup on that | ||
let mut seen = FxHashMap::with_capacity_and_hasher(initial_size, Default::default()); | ||
|
||
let mut is_duplicate = move |obligation: &Obligation<'tcx, _>| -> bool { | ||
return match seen.raw_entry_mut().from_key(obligation) { | ||
RawEntryMut::Occupied(..) => true, | ||
RawEntryMut::Vacant(vacant) => { | ||
vacant.insert(obligation.clone(), ()); | ||
false | ||
} | ||
}; | ||
}; | ||
|
||
self.obligations.retain(|obligation| !is_duplicate(obligation)); | ||
|
||
self.obligations.extend(iter.filter_map(|obligation| { | ||
if is_duplicate(obligation.borrow()) { | ||
return None; | ||
} | ||
Some(obligation.into_owned()) | ||
})); | ||
} | ||
} | ||
} | ||
|
||
impl<'tcx, O> Obligation<'tcx, O> { | ||
pub fn new( | ||
cause: ObligationCause<'tcx>, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm somewhat confused. Why does
Item
need to be aCow
? I'm not seeing where borrows make sense, but I might be missing something ^_^Wouldn't passing a
&[Obligation<'tcx, T>]
give us maybe better perf? Because doingobligations.into_iter().map(...)
in the callers is incurring new allocations in the hot path.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Items from the projection cache are borrowed, items from a fresh project_type + normalization are owned.
Do you mean
obligations.extend(result.obligations.into_iter().map(Cow::Owned));
? That doesn't allocate new obligations, it just stuffs already owned ones into aCow::Owned
enum variant.