diff options
| author | bors <bors@rust-lang.org> | 2019-10-08 08:08:21 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2019-10-08 08:08:21 +0000 |
| commit | ec557aa8180ca08ff749793b3d42383618b96044 (patch) | |
| tree | 35c22eb20f376419317c73718cdb3153fbce3393 /src | |
| parent | 1e1f25e31b1d26bd62aaf5a4554d25c33f75a0d1 (diff) | |
| parent | d1a7bb36ad0a5932384eac03d3fb834efc0317e5 (diff) | |
| download | rust-ec557aa8180ca08ff749793b3d42383618b96044.tar.gz rust-ec557aa8180ca08ff749793b3d42383618b96044.zip | |
Auto merge of #64949 - nnethercote:avoid-SmallVec-collect, r=zackmdavis
Avoid `SmallVec::collect` We can get sizeable speed-ups by avoiding `SmallVec::collect` when the number of elements is small.
Diffstat (limited to 'src')
| -rw-r--r-- | src/librustc/ty/context.rs | 25 | ||||
| -rw-r--r-- | src/librustc/ty/structural_impls.rs | 17 | ||||
| -rw-r--r-- | src/librustc/ty/subst.rs | 43 |
3 files changed, 73 insertions, 12 deletions
diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index eb380eb12d8..cd52f8fa92c 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -2868,8 +2868,29 @@ impl<'a, T, R> InternIteratorElement<T, R> for &'a T impl<T, R, E> InternIteratorElement<T, R> for Result<T, E> { type Output = Result<R, E>; - fn intern_with<I: Iterator<Item=Self>, F: FnOnce(&[T]) -> R>(iter: I, f: F) -> Self::Output { - Ok(f(&iter.collect::<Result<SmallVec<[_; 8]>, _>>()?)) + fn intern_with<I: Iterator<Item=Self>, F: FnOnce(&[T]) -> R>(mut iter: I, f: F) + -> Self::Output { + // This code is hot enough that it's worth specializing for the most + // common length lists, to avoid the overhead of `SmallVec` creation. + // The match arms are in order of frequency. The 1, 2, and 0 cases are + // typically hit in ~95% of cases. We assume that if the upper and + // lower bounds from `size_hint` agree they are correct. + Ok(match iter.size_hint() { + (1, Some(1)) => { + f(&[iter.next().unwrap()?]) + } + (2, Some(2)) => { + let t0 = iter.next().unwrap()?; + let t1 = iter.next().unwrap()?; + f(&[t0, t1]) + } + (0, Some(0)) => { + f(&[]) + } + _ => { + f(&iter.collect::<Result<SmallVec<[_; 8]>, _>>()?) + } + }) } } diff --git a/src/librustc/ty/structural_impls.rs b/src/librustc/ty/structural_impls.rs index 6b0df7fb92a..5aa59cc309f 100644 --- a/src/librustc/ty/structural_impls.rs +++ b/src/librustc/ty/structural_impls.rs @@ -1223,8 +1223,21 @@ BraceStructTypeFoldableImpl! { impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::List<ty::Predicate<'tcx>> { fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self { - let v = self.iter().map(|p| p.fold_with(folder)).collect::<SmallVec<[_; 8]>>(); - folder.tcx().intern_predicates(&v) + // This code is hot enough that it's worth specializing for a list of + // length 0. (No other length is common enough to be worth singling + // out). + if self.len() == 0 { + self + } else { + // Don't bother interning if nothing changed, which is the common + // case. + let v = self.iter().map(|p| p.fold_with(folder)).collect::<SmallVec<[_; 8]>>(); + if v[..] == self[..] { + self + } else { + folder.tcx().intern_predicates(&v) + } + } } fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool { diff --git a/src/librustc/ty/subst.rs b/src/librustc/ty/subst.rs index 7c5daaf14d7..4081c02a33c 100644 --- a/src/librustc/ty/subst.rs +++ b/src/librustc/ty/subst.rs @@ -402,14 +402,41 @@ impl<'a, 'tcx> InternalSubsts<'tcx> { impl<'tcx> TypeFoldable<'tcx> for SubstsRef<'tcx> { fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self { - let params: SmallVec<[_; 8]> = self.iter().map(|k| k.fold_with(folder)).collect(); - - // If folding doesn't change the substs, it's faster to avoid - // calling `mk_substs` and instead reuse the existing substs. - if params[..] == self[..] { - self - } else { - folder.tcx().intern_substs(¶ms) + // This code is hot enough that it's worth specializing for the most + // common length lists, to avoid the overhead of `SmallVec` creation. + // The match arms are in order of frequency. The 1, 2, and 0 cases are + // typically hit in 90--99.99% of cases. When folding doesn't change + // the substs, it's faster to reuse the existing substs rather than + // calling `intern_substs`. + match self.len() { + 1 => { + let param0 = self[0].fold_with(folder); + if param0 == self[0] { + self + } else { + folder.tcx().intern_substs(&[param0]) + } + } + 2 => { + let param0 = self[0].fold_with(folder); + let param1 = self[1].fold_with(folder); + if param0 == self[0] && param1 == self[1] { + self + } else { + folder.tcx().intern_substs(&[param0, param1]) + } + } + 0 => { + self + } + _ => { + let params: SmallVec<[_; 8]> = self.iter().map(|k| k.fold_with(folder)).collect(); + if params[..] == self[..] { + self + } else { + folder.tcx().intern_substs(¶ms) + } + } } } |
