Skip to content

Commit ec557aa

Browse files
committed
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.
2 parents 1e1f25e + d1a7bb3 commit ec557aa

File tree

3 files changed

+73
-12
lines changed

3 files changed

+73
-12
lines changed

src/librustc/ty/context.rs

+23-2
Original file line numberDiff line numberDiff line change
@@ -2868,8 +2868,29 @@ impl<'a, T, R> InternIteratorElement<T, R> for &'a T
28682868

28692869
impl<T, R, E> InternIteratorElement<T, R> for Result<T, E> {
28702870
type Output = Result<R, E>;
2871-
fn intern_with<I: Iterator<Item=Self>, F: FnOnce(&[T]) -> R>(iter: I, f: F) -> Self::Output {
2872-
Ok(f(&iter.collect::<Result<SmallVec<[_; 8]>, _>>()?))
2871+
fn intern_with<I: Iterator<Item=Self>, F: FnOnce(&[T]) -> R>(mut iter: I, f: F)
2872+
-> Self::Output {
2873+
// This code is hot enough that it's worth specializing for the most
2874+
// common length lists, to avoid the overhead of `SmallVec` creation.
2875+
// The match arms are in order of frequency. The 1, 2, and 0 cases are
2876+
// typically hit in ~95% of cases. We assume that if the upper and
2877+
// lower bounds from `size_hint` agree they are correct.
2878+
Ok(match iter.size_hint() {
2879+
(1, Some(1)) => {
2880+
f(&[iter.next().unwrap()?])
2881+
}
2882+
(2, Some(2)) => {
2883+
let t0 = iter.next().unwrap()?;
2884+
let t1 = iter.next().unwrap()?;
2885+
f(&[t0, t1])
2886+
}
2887+
(0, Some(0)) => {
2888+
f(&[])
2889+
}
2890+
_ => {
2891+
f(&iter.collect::<Result<SmallVec<[_; 8]>, _>>()?)
2892+
}
2893+
})
28732894
}
28742895
}
28752896

src/librustc/ty/structural_impls.rs

+15-2
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

+35-8
Original file line numberDiff line numberDiff line change
@@ -402,14 +402,41 @@ impl<'a, 'tcx> InternalSubsts<'tcx> {
402402

403403
impl<'tcx> TypeFoldable<'tcx> for SubstsRef<'tcx> {
404404
fn super_fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
405-
let params: SmallVec<[_; 8]> = self.iter().map(|k| k.fold_with(folder)).collect();
406-
407-
// If folding doesn't change the substs, it's faster to avoid
408-
// calling `mk_substs` and instead reuse the existing substs.
409-
if params[..] == self[..] {
410-
self
411-
} else {
412-
folder.tcx().intern_substs(&params)
405+
// This code is hot enough that it's worth specializing for the most
406+
// common length lists, to avoid the overhead of `SmallVec` creation.
407+
// The match arms are in order of frequency. The 1, 2, and 0 cases are
408+
// typically hit in 90--99.99% of cases. When folding doesn't change
409+
// the substs, it's faster to reuse the existing substs rather than
410+
// calling `intern_substs`.
411+
match self.len() {
412+
1 => {
413+
let param0 = self[0].fold_with(folder);
414+
if param0 == self[0] {
415+
self
416+
} else {
417+
folder.tcx().intern_substs(&[param0])
418+
}
419+
}
420+
2 => {
421+
let param0 = self[0].fold_with(folder);
422+
let param1 = self[1].fold_with(folder);
423+
if param0 == self[0] && param1 == self[1] {
424+
self
425+
} else {
426+
folder.tcx().intern_substs(&[param0, param1])
427+
}
428+
}
429+
0 => {
430+
self
431+
}
432+
_ => {
433+
let params: SmallVec<[_; 8]> = self.iter().map(|k| k.fold_with(folder)).collect();
434+
if params[..] == self[..] {
435+
self
436+
} else {
437+
folder.tcx().intern_substs(&params)
438+
}
439+
}
413440
}
414441
}
415442

0 commit comments

Comments
 (0)