From a9915581d7cb73e7c8fb8193f48dbef36a7d09ac Mon Sep 17 00:00:00 2001 From: kadmin Date: Wed, 18 Nov 2020 22:59:47 +0000 Subject: [PATCH] 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 to use `to_vec` Test different unrolling version for to_vec Revert to impl From benchmarking, it appears this version is faster --- library/alloc/src/slice.rs | 66 ++++++++++++++++++++++++++++++++++---- library/alloc/src/vec.rs | 26 +++++++++------ src/test/codegen/to_vec.rs | 10 ++++++ 3 files changed, 85 insertions(+), 17 deletions(-) create mode 100644 src/test/codegen/to_vec.rs diff --git a/library/alloc/src/slice.rs b/library/alloc/src/slice.rs index 41ebb1cf654..949a3bb1d70 100644 --- a/library/alloc/src/slice.rs +++ b/library/alloc/src/slice.rs @@ -155,13 +155,65 @@ mod hack { } #[inline] - pub fn to_vec(s: &[T], alloc: A) -> Vec - where - T: Clone, - { - let mut vec = Vec::with_capacity_in(s.len(), alloc); - vec.extend_from_slice(s); - vec + pub fn to_vec(s: &[T], alloc: A) -> Vec { + T::to_vec(s, alloc) + } + + pub trait ConvertVec { + fn to_vec(s: &[Self], alloc: A) -> Vec + where + Self: Sized; + } + + impl ConvertVec for T { + #[inline] + default fn to_vec(s: &[Self], alloc: A) -> Vec { + struct DropGuard<'a, T, A: AllocRef> { + vec: &'a mut Vec, + num_init: usize, + } + impl<'a, T, A: AllocRef> Drop for DropGuard<'a, T, A> { + #[inline] + fn drop(&mut self) { + // SAFETY: + // items were marked initialized in the loop below + unsafe { + self.vec.set_len(self.num_init); + } + } + } + let mut vec = Vec::with_capacity_in(s.len(), alloc); + let mut guard = DropGuard { vec: &mut vec, num_init: 0 }; + let slots = guard.vec.spare_capacity_mut(); + // .take(slots.len()) is necessary for LLVM to remove bounds checks + // and has better codegen than zip. + for (i, b) in s.iter().enumerate().take(slots.len()) { + guard.num_init = i; + slots[i].write(b.clone()); + } + core::mem::forget(guard); + // SAFETY: + // the vec was allocated and initialized above to at least this length. + unsafe { + vec.set_len(s.len()); + } + vec + } + } + + impl ConvertVec for T { + #[inline] + fn to_vec(s: &[Self], alloc: A) -> Vec { + let mut v = Vec::with_capacity_in(s.len(), alloc); + // SAFETY: + // allocated above with the capacity of `s`, and initialize to `s.len()` in + // ptr::copy_to_non_overlapping below. + unsafe { + s.as_ptr().copy_to_nonoverlapping(v.as_mut_ptr(), s.len()); + v.set_len(s.len()); + } + v + } } } diff --git a/library/alloc/src/vec.rs b/library/alloc/src/vec.rs index 392c16546ef..21ae7d6d8a2 100644 --- a/library/alloc/src/vec.rs +++ b/library/alloc/src/vec.rs @@ -2508,17 +2508,23 @@ where } } -impl<'a, T: 'a> SpecFromIter<&'a T, slice::Iter<'a, T>> for Vec -where - T: Copy, -{ - // reuses the extend specialization for T: Copy +// This utilizes `iterator.as_slice().to_vec()` since spec_extend +// must take more steps to reason about the final capacity + length +// and thus do more work. `to_vec()` directly allocates the correct amount +// and fills it exactly. +impl<'a, T: 'a + Clone> SpecFromIter<&'a T, slice::Iter<'a, T>> for Vec { + #[cfg(not(test))] fn from_iter(iterator: slice::Iter<'a, T>) -> Self { - let mut vec = Vec::new(); - // must delegate to spec_extend() since extend() itself delegates - // to spec_from for empty Vecs - vec.spec_extend(iterator); - vec + iterator.as_slice().to_vec() + } + + // HACK(japaric): with cfg(test) the inherent `[T]::to_vec` method, which is + // required for this method definition, is not available. Instead use the + // `slice::to_vec` function which is only available with cfg(test) + // NB see the slice::hack module in slice.rs for more information + #[cfg(test)] + fn from_iter(iterator: slice::Iter<'a, T>) -> Self { + crate::slice::to_vec(iterator.as_slice(), Global) } } diff --git a/src/test/codegen/to_vec.rs b/src/test/codegen/to_vec.rs new file mode 100644 index 00000000000..60dc4efcb62 --- /dev/null +++ b/src/test/codegen/to_vec.rs @@ -0,0 +1,10 @@ +// compile-flags: -O + +#![crate_type = "lib"] + +// CHECK-LABEL: @copy_to_vec +#[no_mangle] +fn copy_to_vec(s: &[u64]) -> Vec { + s.to_vec() + // CHECK: call void @llvm.memcpy +}