diff options
| author | The 8472 <git@infinite-source.de> | 2022-06-26 22:53:25 +0200 |
|---|---|---|
| committer | The 8472 <git@infinite-source.de> | 2022-07-26 20:31:43 +0200 |
| commit | 2f9f2e507eaecf45074727e34af02642b95fa724 (patch) | |
| tree | 77f407e338439431f1087823c704cd02ebe238b4 /library/alloc/src/vec | |
| parent | 7425fb293f510a6f138e82a963a3bc599a5b9e1c (diff) | |
| download | rust-2f9f2e507eaecf45074727e34af02642b95fa724.tar.gz rust-2f9f2e507eaecf45074727e34af02642b95fa724.zip | |
Optimized vec::IntoIter::next_chunk impl
``` test vec::bench_next_chunk ... bench: 696 ns/iter (+/- 22) x86_64v1, pr test vec::bench_next_chunk ... bench: 309 ns/iter (+/- 4) znver2, default test vec::bench_next_chunk ... bench: 17,272 ns/iter (+/- 117) znver2, pr test vec::bench_next_chunk ... bench: 211 ns/iter (+/- 3) ``` The znver2 default impl seems to be slow due to inlining decisions. It goes through `core::array::iter_next_chunk` which has a deeper call tree.
Diffstat (limited to 'library/alloc/src/vec')
| -rw-r--r-- | library/alloc/src/vec/into_iter.rs | 41 |
1 files changed, 39 insertions, 2 deletions
diff --git a/library/alloc/src/vec/into_iter.rs b/library/alloc/src/vec/into_iter.rs index 28979457b7f..1b483e3fc77 100644 --- a/library/alloc/src/vec/into_iter.rs +++ b/library/alloc/src/vec/into_iter.rs @@ -2,13 +2,14 @@ use super::AsVecIntoIter; use crate::alloc::{Allocator, Global}; use crate::raw_vec::RawVec; +use core::array; use core::fmt; use core::intrinsics::arith_offset; use core::iter::{ FusedIterator, InPlaceIterable, SourceIter, TrustedLen, TrustedRandomAccessNoCoerce, }; use core::marker::PhantomData; -use core::mem::{self, ManuallyDrop}; +use core::mem::{self, ManuallyDrop, MaybeUninit}; #[cfg(not(no_global_oom_handling))] use core::ops::Deref; use core::ptr::{self, NonNull}; @@ -124,7 +125,6 @@ impl<T, A: Allocator> IntoIter<T, A> { } /// Forgets to Drop the remaining elements while still allowing the backing allocation to be freed. - #[cfg(not(no_global_oom_handling))] pub(crate) fn forget_remaining_elements(&mut self) { self.ptr = self.end; } @@ -204,6 +204,43 @@ impl<T, A: Allocator> Iterator for IntoIter<T, A> { self.len() } + #[inline] + fn next_chunk<const N: usize>(&mut self) -> Result<[T; N], core::array::IntoIter<T, N>> { + let mut raw_ary = MaybeUninit::uninit_array(); + + let len = self.len(); + + if mem::size_of::<T>() == 0 { + if len < N { + self.forget_remaining_elements(); + // Safety: ZSTs can be conjured ex nihilo, only the amount has to be correct + return Err(unsafe { array::IntoIter::new_unchecked(raw_ary, 0..len) }); + } + + self.ptr = unsafe { arith_offset(self.ptr as *const i8, N as isize) as *mut T }; + // Safety: ditto + return Ok(unsafe { MaybeUninit::array_assume_init(raw_ary) }); + } + + if len < N { + // Safety: `len` indicates that this many elements are available and we just checked that + // it fits into the array. + unsafe { + ptr::copy_nonoverlapping(self.ptr, raw_ary.as_mut_ptr() as *mut T, len); + self.forget_remaining_elements(); + return Err(array::IntoIter::new_unchecked(raw_ary, 0..len)); + } + } + + // Safety: `len` is larger than the array size. Copy a fixed amount here to fully initialize + // the array. + return unsafe { + ptr::copy_nonoverlapping(self.ptr, raw_ary.as_mut_ptr() as *mut T, N); + self.ptr = self.ptr.add(N); + Ok(MaybeUninit::array_assume_init(raw_ary)) + }; + } + unsafe fn __iterator_get_unchecked(&mut self, i: usize) -> Self::Item where Self: TrustedRandomAccessNoCoerce, |
