diff options
| author | kadmin <julianknodt@gmail.com> | 2020-11-18 22:59:47 +0000 |
|---|---|---|
| committer | kadmin <julianknodt@gmail.com> | 2020-11-22 22:22:03 +0000 |
| commit | a9915581d7cb73e7c8fb8193f48dbef36a7d09ac (patch) | |
| tree | 1ee97f767f67417a36f9d3789a3bb555ec9a7b8a | |
| parent | a1a13b2bc4fa6370b9501135d97c5fe0bc401894 (diff) | |
| download | rust-a9915581d7cb73e7c8fb8193f48dbef36a7d09ac.tar.gz rust-a9915581d7cb73e7c8fb8193f48dbef36a7d09ac.zip | |
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
| -rw-r--r-- | library/alloc/src/slice.rs | 66 | ||||
| -rw-r--r-- | library/alloc/src/vec.rs | 26 | ||||
| -rw-r--r-- | src/test/codegen/to_vec.rs | 10 |
3 files changed, 85 insertions, 17 deletions
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<T, A: AllocRef>(s: &[T], alloc: A) -> Vec<T, A> - where - T: Clone, - { - let mut vec = Vec::with_capacity_in(s.len(), alloc); - vec.extend_from_slice(s); - vec + pub fn to_vec<T: ConvertVec, A: AllocRef>(s: &[T], alloc: A) -> Vec<T, A> { + T::to_vec(s, alloc) + } + + pub trait ConvertVec { + fn to_vec<A: AllocRef>(s: &[Self], alloc: A) -> Vec<Self, A> + where + Self: Sized; + } + + impl<T: Clone> ConvertVec for T { + #[inline] + default fn to_vec<A: AllocRef>(s: &[Self], alloc: A) -> Vec<Self, A> { + struct DropGuard<'a, T, A: AllocRef> { + vec: &'a mut Vec<T, A>, + 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<T: Copy> ConvertVec for T { + #[inline] + fn to_vec<A: AllocRef>(s: &[Self], alloc: A) -> Vec<Self, A> { + 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<T> -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<T> { + #[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<u64> { + s.to_vec() + // CHECK: call void @llvm.memcpy +} |
