Skip to content

Commit 164c6cf

Browse files
Specialize Vec::extend_with for T: TrivialClone
After running perf on the previous commit, it was found that the compile-time regression was too significant to ignore. This introduces a specialized, simplified implementation for `T: TrivialClone` that will reduce the perf regression.
1 parent 17fc02b commit 164c6cf

File tree

2 files changed

+44
-1
lines changed

2 files changed

+44
-1
lines changed

library/alloc/src/vec/mod.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,12 @@ use self::spec_extend::SpecExtend;
172172
#[cfg(not(no_global_oom_handling))]
173173
mod spec_extend;
174174

175+
#[cfg(not(no_global_oom_handling))]
176+
use self::spec_extend_with::SpecExtendWith;
177+
178+
#[cfg(not(no_global_oom_handling))]
179+
mod spec_extend_with;
180+
175181
/// A contiguous growable array type, written as `Vec<T>`, short for 'vector'.
176182
///
177183
/// # Examples
@@ -3448,7 +3454,7 @@ impl<T: Clone, A: Allocator> Vec<T, A> {
34483454
#[cfg(not(no_global_oom_handling))]
34493455
/// Extend the vector by `n` clones of value.
34503456
fn extend_with(&mut self, n: usize, value: T) {
3451-
self.extend_trusted(iter::repeat_n(value, n));
3457+
<Self as SpecExtendWith<T>>::spec_extend_with(self, n, value);
34523458
}
34533459
}
34543460

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
use core::clone::TrivialClone;
2+
use core::mem::MaybeUninit;
3+
use core::{iter, ptr};
4+
5+
use super::Vec;
6+
use crate::alloc::Allocator;
7+
8+
// Specialization trait used for Vec::extend_with
9+
pub(super) trait SpecExtendWith<T> {
10+
fn spec_extend_with(&mut self, n: usize, value: T);
11+
}
12+
13+
impl<T: Clone, A: Allocator> SpecExtendWith<T> for Vec<T, A> {
14+
default fn spec_extend_with(&mut self, n: usize, value: T) {
15+
self.extend_trusted(iter::repeat_n(value, n));
16+
}
17+
}
18+
19+
impl<T: TrivialClone, A: Allocator> SpecExtendWith<T> for Vec<T, A> {
20+
fn spec_extend_with(&mut self, n: usize, value: T) {
21+
let len = self.len();
22+
self.reserve(n);
23+
let unfilled = self.spare_capacity_mut();
24+
25+
// SAFETY: the above `reserve` call guarantees `n` to be in bounds.
26+
let unfilled = unsafe { unfilled.get_unchecked_mut(..n) };
27+
28+
// SAFETY: because `T` is `TrivialClone`, this is equivalent to calling
29+
// `T::clone` for every element. Notably, `TrivialClone` also implies
30+
// that the `clone` implementation will not panic, so we can avoid
31+
// initialization guards and such.
32+
unfilled.fill_with(|| MaybeUninit::new(unsafe { ptr::read(&value) }));
33+
34+
// SAFETY: the elements have been initialized above.
35+
unsafe { self.set_len(len + n) }
36+
}
37+
}

0 commit comments

Comments
 (0)