Skip to content

Commit a991558

Browse files
committed
Change slice::to_vec to not use extend_from_slice
This also required adding a loop guard in case clone panics Add specialization for copy There is a better version for copy, so I've added specialization for that function and hopefully that should speed it up even more. Switch FromIter<slice::Iter> to use `to_vec` Test different unrolling version for to_vec Revert to impl From benchmarking, it appears this version is faster
1 parent a1a13b2 commit a991558

File tree

3 files changed

+85
-17
lines changed

3 files changed

+85
-17
lines changed

library/alloc/src/slice.rs

+59-7
Original file line numberDiff line numberDiff line change
@@ -155,13 +155,65 @@ mod hack {
155155
}
156156

157157
#[inline]
158-
pub fn to_vec<T, A: AllocRef>(s: &[T], alloc: A) -> Vec<T, A>
159-
where
160-
T: Clone,
161-
{
162-
let mut vec = Vec::with_capacity_in(s.len(), alloc);
163-
vec.extend_from_slice(s);
164-
vec
158+
pub fn to_vec<T: ConvertVec, A: AllocRef>(s: &[T], alloc: A) -> Vec<T, A> {
159+
T::to_vec(s, alloc)
160+
}
161+
162+
pub trait ConvertVec {
163+
fn to_vec<A: AllocRef>(s: &[Self], alloc: A) -> Vec<Self, A>
164+
where
165+
Self: Sized;
166+
}
167+
168+
impl<T: Clone> ConvertVec for T {
169+
#[inline]
170+
default fn to_vec<A: AllocRef>(s: &[Self], alloc: A) -> Vec<Self, A> {
171+
struct DropGuard<'a, T, A: AllocRef> {
172+
vec: &'a mut Vec<T, A>,
173+
num_init: usize,
174+
}
175+
impl<'a, T, A: AllocRef> Drop for DropGuard<'a, T, A> {
176+
#[inline]
177+
fn drop(&mut self) {
178+
// SAFETY:
179+
// items were marked initialized in the loop below
180+
unsafe {
181+
self.vec.set_len(self.num_init);
182+
}
183+
}
184+
}
185+
let mut vec = Vec::with_capacity_in(s.len(), alloc);
186+
let mut guard = DropGuard { vec: &mut vec, num_init: 0 };
187+
let slots = guard.vec.spare_capacity_mut();
188+
// .take(slots.len()) is necessary for LLVM to remove bounds checks
189+
// and has better codegen than zip.
190+
for (i, b) in s.iter().enumerate().take(slots.len()) {
191+
guard.num_init = i;
192+
slots[i].write(b.clone());
193+
}
194+
core::mem::forget(guard);
195+
// SAFETY:
196+
// the vec was allocated and initialized above to at least this length.
197+
unsafe {
198+
vec.set_len(s.len());
199+
}
200+
vec
201+
}
202+
}
203+
204+
impl<T: Copy> ConvertVec for T {
205+
#[inline]
206+
fn to_vec<A: AllocRef>(s: &[Self], alloc: A) -> Vec<Self, A> {
207+
let mut v = Vec::with_capacity_in(s.len(), alloc);
208+
// SAFETY:
209+
// allocated above with the capacity of `s`, and initialize to `s.len()` in
210+
// ptr::copy_to_non_overlapping below.
211+
unsafe {
212+
s.as_ptr().copy_to_nonoverlapping(v.as_mut_ptr(), s.len());
213+
v.set_len(s.len());
214+
}
215+
v
216+
}
165217
}
166218
}
167219

library/alloc/src/vec.rs

+16-10
Original file line numberDiff line numberDiff line change
@@ -2508,17 +2508,23 @@ where
25082508
}
25092509
}
25102510

2511-
impl<'a, T: 'a> SpecFromIter<&'a T, slice::Iter<'a, T>> for Vec<T>
2512-
where
2513-
T: Copy,
2514-
{
2515-
// reuses the extend specialization for T: Copy
2511+
// This utilizes `iterator.as_slice().to_vec()` since spec_extend
2512+
// must take more steps to reason about the final capacity + length
2513+
// and thus do more work. `to_vec()` directly allocates the correct amount
2514+
// and fills it exactly.
2515+
impl<'a, T: 'a + Clone> SpecFromIter<&'a T, slice::Iter<'a, T>> for Vec<T> {
2516+
#[cfg(not(test))]
25162517
fn from_iter(iterator: slice::Iter<'a, T>) -> Self {
2517-
let mut vec = Vec::new();
2518-
// must delegate to spec_extend() since extend() itself delegates
2519-
// to spec_from for empty Vecs
2520-
vec.spec_extend(iterator);
2521-
vec
2518+
iterator.as_slice().to_vec()
2519+
}
2520+
2521+
// HACK(japaric): with cfg(test) the inherent `[T]::to_vec` method, which is
2522+
// required for this method definition, is not available. Instead use the
2523+
// `slice::to_vec` function which is only available with cfg(test)
2524+
// NB see the slice::hack module in slice.rs for more information
2525+
#[cfg(test)]
2526+
fn from_iter(iterator: slice::Iter<'a, T>) -> Self {
2527+
crate::slice::to_vec(iterator.as_slice(), Global)
25222528
}
25232529
}
25242530

src/test/codegen/to_vec.rs

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// compile-flags: -O
2+
3+
#![crate_type = "lib"]
4+
5+
// CHECK-LABEL: @copy_to_vec
6+
#[no_mangle]
7+
fn copy_to_vec(s: &[u64]) -> Vec<u64> {
8+
s.to_vec()
9+
// CHECK: call void @llvm.memcpy
10+
}

0 commit comments

Comments
 (0)