Skip to content

Commit 64cbb2f

Browse files
committed
Auto merge of #64949 - nnethercote:avoid-SmallVec-collect, r=<try>
Avoid `SmallVec::collect` We can get sizeable speed-ups by avoiding `SmallVec::collect` when the number of elements is small.
2 parents 42ec683 + d1a7bb3 commit 64cbb2f

File tree

3 files changed

+73
-12
lines changed

3 files changed

+73
-12
lines changed

src/librustc/ty/context.rs

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2848,8 +2848,29 @@ impl<'a, T, R> InternIteratorElement<T, R> for &'a T
28482848

28492849
impl<T, R, E> InternIteratorElement<T, R> for Result<T, E> {
28502850
type Output = Result<R, E>;
2851-
fn intern_with<I: Iterator<Item=Self>, F: FnOnce(&[T]) -> R>(iter: I, f: F) -> Self::Output {
2852-
Ok(f(&iter.collect::<Result<SmallVec<[_; 8]>, _>>()?))
2851+
fn intern_with<I: Iterator<Item=Self>, F: FnOnce(&[T]) -> R>(mut iter: I, f: F)
2852+
-> Self::Output {
2853+
// This code is hot enough that it's worth specializing for the most
2854+
// common length lists, to avoid the overhead of `SmallVec` creation.
2855+
// The match arms are in order of frequency. The 1, 2, and 0 cases are
2856+
// typically hit in ~95% of cases. We assume that if the upper and
2857+
// lower bounds from `size_hint` agree they are correct.
2858+
Ok(match iter.size_hint() {
2859+
(1, Some(1)) => {
2860+
f(&[iter.next().unwrap()?])
2861+
}
2862+
(2, Some(2)) => {
2863+
let t0 = iter.next().unwrap()?;
2864+
let t1 = iter.next().unwrap()?;
2865+
f(&[t0, t1])
2866+
}
2867+
(0, Some(0)) => {
2868+
f(&[])
2869+
}
2870+
_ => {
2871+
f(&iter.collect::<Result<SmallVec<[_; 8]>, _>>()?)
2872+
}
2873+
})
28532874
}
28542875
}
28552876

src/librustc/ty/structural_impls.rs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1223,8 +1223,21 @@ BraceStructTypeFoldableImpl! {
12231223

12241224
impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::List<ty::Predicate<'tcx>> {
12251225
fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
1226-
let v = self.iter().map(|p| p.fold_with(folder)).collect::<SmallVec<[_; 8]>>();
1227-
folder.tcx().intern_predicates(&v)
1226+
// This code is hot enough that it's worth specializing for a list of
1227+
// length 0. (No other length is common enough to be worth singling
1228+
// out).
1229+
if self.len() == 0 {
1230+
self
1231+
} else {
1232+
// Don't bother interning if nothing changed, which is the common
1233+
// case.
1234+
let v = self.iter().map(|p| p.fold_with(folder)).collect::<SmallVec<[_; 8]>>();
1235+
if v[..] == self[..] {
1236+
self
1237+
} else {
1238+
folder.tcx().intern_predicates(&v)
1239+
}
1240+
}
12281241
}
12291242

12301243
fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {

src/librustc/ty/subst.rs

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -383,14 +383,41 @@ impl<'a, 'tcx> InternalSubsts<'tcx> {
383383

384384
impl<'tcx> TypeFoldable<'tcx> for SubstsRef<'tcx> {
385385
fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
386-
let params: SmallVec<[_; 8]> = self.iter().map(|k| k.fold_with(folder)).collect();
387-
388-
// If folding doesn't change the substs, it's faster to avoid
389-
// calling `mk_substs` and instead reuse the existing substs.
390-
if params[..] == self[..] {
391-
self
392-
} else {
393-
folder.tcx().intern_substs(&params)
386+
// This code is hot enough that it's worth specializing for the most
387+
// common length lists, to avoid the overhead of `SmallVec` creation.
388+
// The match arms are in order of frequency. The 1, 2, and 0 cases are
389+
// typically hit in 90--99.99% of cases. When folding doesn't change
390+
// the substs, it's faster to reuse the existing substs rather than
391+
// calling `intern_substs`.
392+
match self.len() {
393+
1 => {
394+
let param0 = self[0].fold_with(folder);
395+
if param0 == self[0] {
396+
self
397+
} else {
398+
folder.tcx().intern_substs(&[param0])
399+
}
400+
}
401+
2 => {
402+
let param0 = self[0].fold_with(folder);
403+
let param1 = self[1].fold_with(folder);
404+
if param0 == self[0] && param1 == self[1] {
405+
self
406+
} else {
407+
folder.tcx().intern_substs(&[param0, param1])
408+
}
409+
}
410+
0 => {
411+
self
412+
}
413+
_ => {
414+
let params: SmallVec<[_; 8]> = self.iter().map(|k| k.fold_with(folder)).collect();
415+
if params[..] == self[..] {
416+
self
417+
} else {
418+
folder.tcx().intern_substs(&params)
419+
}
420+
}
394421
}
395422
}
396423

0 commit comments

Comments
 (0)