From 595a8d28d6b1b39e725b143a81d4d65ed5352ffe Mon Sep 17 00:00:00 2001 From: Paolo Barbolini Date: Sat, 30 Nov 2024 05:25:23 +0000 Subject: [PATCH 1/2] Use `iter::repeat_n` to implement `Vec::extend_with` This replaces the manual `Vec::extend_with` implementation with `iter::repeat_n` and `Vec::extend_trusted`. This simplifies the code and gets LLVM to remove the special case for the last element when `T` is trivial to clone. --- library/alloc/src/vec/mod.rs | 26 +------------------------- 1 file changed, 1 insertion(+), 25 deletions(-) diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index 9856e9c18ec68..b5207739f2892 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -3258,31 +3258,7 @@ impl Vec { #[track_caller] /// Extend the vector by `n` clones of value. fn extend_with(&mut self, n: usize, value: T) { - self.reserve(n); - - unsafe { - let mut ptr = self.as_mut_ptr().add(self.len()); - // Use SetLenOnDrop to work around bug where compiler - // might not realize the store through `ptr` through self.set_len() - // don't alias. - let mut local_len = SetLenOnDrop::new(&mut self.len); - - // Write all elements except the last one - for _ in 1..n { - ptr::write(ptr, value.clone()); - ptr = ptr.add(1); - // Increment the length in every step in case clone() panics - local_len.increment_len(1); - } - - if n > 0 { - // We can write the last element directly without cloning needlessly - ptr::write(ptr, value); - local_len.increment_len(1); - } - - // len set by scope guard - } + self.extend_trusted(iter::repeat_n(value, n)); } } From bbcb7720487926956778d351cada4f0a4e50cb49 Mon Sep 17 00:00:00 2001 From: Paolo Barbolini Date: Sat, 26 Jul 2025 05:05:00 +0200 Subject: [PATCH 2/2] Specialize `Vec::extend_with` for `T: Copy` 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: Copy` that will contain the perf regression. --- library/alloc/src/vec/mod.rs | 8 ++++- library/alloc/src/vec/spec_extend_with.rs | 36 +++++++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 library/alloc/src/vec/spec_extend_with.rs diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index b5207739f2892..930fa74971299 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -150,6 +150,12 @@ use self::spec_extend::SpecExtend; #[cfg(not(no_global_oom_handling))] mod spec_extend; +#[cfg(not(no_global_oom_handling))] +use self::spec_extend_with::SpecExtendWith; + +#[cfg(not(no_global_oom_handling))] +mod spec_extend_with; + /// A contiguous growable array type, written as `Vec`, short for 'vector'. /// /// # Examples @@ -3258,7 +3264,7 @@ impl Vec { #[track_caller] /// Extend the vector by `n` clones of value. fn extend_with(&mut self, n: usize, value: T) { - self.extend_trusted(iter::repeat_n(value, n)); + >::spec_extend_with(self, n, value); } } diff --git a/library/alloc/src/vec/spec_extend_with.rs b/library/alloc/src/vec/spec_extend_with.rs new file mode 100644 index 0000000000000..6dd754e226512 --- /dev/null +++ b/library/alloc/src/vec/spec_extend_with.rs @@ -0,0 +1,36 @@ +use core::iter; +use core::mem::MaybeUninit; + +use super::Vec; +use crate::alloc::Allocator; + +// Specialization trait used for Vec::extend_with +pub(super) trait SpecExtendWith { + #[track_caller] + fn spec_extend_with(&mut self, n: usize, value: T); +} + +impl SpecExtendWith for Vec { + #[track_caller] + default fn spec_extend_with(&mut self, n: usize, value: T) { + self.extend_trusted(iter::repeat_n(value, n)); + } +} + +impl SpecExtendWith for Vec { + #[track_caller] + fn spec_extend_with(&mut self, n: usize, value: T) { + let len = self.len(); + self.reserve(n); + let unfilled = self.spare_capacity_mut(); + + // SAFETY: the above `reserve` call guarantees `n` to be in bounds. + let unfilled = unsafe { unfilled.get_unchecked_mut(..n) }; + for elem in unfilled { + *elem = MaybeUninit::new(value); + } + + // SAFETY: the elements have been initialized above. + unsafe { self.set_len(len + n) } + } +}