diff options
| author | AngelicosPhosphoros <xuzin.timur@gmail.com> | 2022-05-31 15:14:55 +0300 |
|---|---|---|
| committer | Mark Rousskov <mark.simulacrum@gmail.com> | 2022-07-24 15:56:39 -0400 |
| commit | 86d445eda8ca2d884551fd0ac00a89e4bfe1cc81 (patch) | |
| tree | 5eb6cf1a2b94f8d044333665f2d233c0481786d4 /library/alloc/src | |
| parent | b4151a41a0b275dee59ffbbc115e7bfc5be8a8c3 (diff) | |
| download | rust-86d445eda8ca2d884551fd0ac00a89e4bfe1cc81.tar.gz rust-86d445eda8ca2d884551fd0ac00a89e4bfe1cc81.zip | |
Support vec zero-alloc optimization for tuples and byte arrays
* Implement IsZero trait for tuples up to 8 IsZero elements; * Implement IsZero for u8/i8, leading to implementation of it for arrays of them too; * Add more codegen tests for this optimization. * Lower size of array for IsZero trait because it fails to inline checks
Diffstat (limited to 'library/alloc/src')
| -rw-r--r-- | library/alloc/src/vec/is_zero.rs | 36 | ||||
| -rw-r--r-- | library/alloc/src/vec/spec_from_elem.rs | 27 |
2 files changed, 46 insertions, 17 deletions
diff --git a/library/alloc/src/vec/is_zero.rs b/library/alloc/src/vec/is_zero.rs index edf270db81d..92a32779b8e 100644 --- a/library/alloc/src/vec/is_zero.rs +++ b/library/alloc/src/vec/is_zero.rs @@ -17,12 +17,14 @@ macro_rules! impl_is_zero { }; } +impl_is_zero!(i8, |x| x == 0); // It is needed to impl for arrays and tuples of i8. impl_is_zero!(i16, |x| x == 0); impl_is_zero!(i32, |x| x == 0); impl_is_zero!(i64, |x| x == 0); impl_is_zero!(i128, |x| x == 0); impl_is_zero!(isize, |x| x == 0); +impl_is_zero!(u8, |x| x == 0); // It is needed to impl for arrays and tuples of u8. impl_is_zero!(u16, |x| x == 0); impl_is_zero!(u32, |x| x == 0); impl_is_zero!(u64, |x| x == 0); @@ -54,15 +56,41 @@ unsafe impl<T: IsZero, const N: usize> IsZero for [T; N] { fn is_zero(&self) -> bool { // Because this is generated as a runtime check, it's not obvious that // it's worth doing if the array is really long. The threshold here - // is largely arbitrary, but was picked because as of 2022-05-01 LLVM - // can const-fold the check in `vec![[0; 32]; n]` but not in - // `vec![[0; 64]; n]`: https://godbolt.org/z/WTzjzfs5b + // is largely arbitrary, but was picked because as of 2022-07-01 LLVM + // fails to const-fold the check in `vec![[1; 32]; n]` + // See https://github.com/rust-lang/rust/pull/97581#issuecomment-1166628022 // Feel free to tweak if you have better evidence. - N <= 32 && self.iter().all(IsZero::is_zero) + N <= 16 && self.iter().all(IsZero::is_zero) } } +// This is recursive macro. +macro_rules! impl_for_tuples { + // Stopper + () => { + // No use for implementing for empty tuple because it is ZST. + }; + ($first_arg:ident $(,$rest:ident)*) => { + unsafe impl <$first_arg: IsZero, $($rest: IsZero,)*> IsZero for ($first_arg, $($rest,)*){ + #[inline] + fn is_zero(&self) -> bool{ + // Destructure tuple to N references + // Rust allows to hide generic params by local variable names. + #[allow(non_snake_case)] + let ($first_arg, $($rest,)*) = self; + + $first_arg.is_zero() + $( && $rest.is_zero() )* + } + } + + impl_for_tuples!($($rest),*); + } +} + +impl_for_tuples!(A, B, C, D, E, F, G, H); + // `Option<&T>` and `Option<Box<T>>` are guaranteed to represent `None` as null. // For fat pointers, the bytes that would be the pointer metadata in the `Some` // variant are padding in the `None` variant, so ignoring them and diff --git a/library/alloc/src/vec/spec_from_elem.rs b/library/alloc/src/vec/spec_from_elem.rs index de610174783..ff364c033ee 100644 --- a/library/alloc/src/vec/spec_from_elem.rs +++ b/library/alloc/src/vec/spec_from_elem.rs @@ -1,6 +1,7 @@ +use core::ptr; + use crate::alloc::Allocator; use crate::raw_vec::RawVec; -use core::ptr::{self}; use super::{ExtendElement, IsZero, Vec}; @@ -17,6 +18,18 @@ impl<T: Clone> SpecFromElem for T { } } +impl<T: Clone + IsZero> SpecFromElem for T { + #[inline] + default fn from_elem<A: Allocator>(elem: T, n: usize, alloc: A) -> Vec<T, A> { + if elem.is_zero() { + return Vec { buf: RawVec::with_capacity_zeroed_in(n, alloc), len: n }; + } + let mut v = Vec::with_capacity_in(n, alloc); + v.extend_with(n, ExtendElement(elem)); + v + } +} + impl SpecFromElem for i8 { #[inline] fn from_elem<A: Allocator>(elem: i8, n: usize, alloc: A) -> Vec<i8, A> { @@ -46,15 +59,3 @@ impl SpecFromElem for u8 { } } } - -impl<T: Clone + IsZero> SpecFromElem for T { - #[inline] - fn from_elem<A: Allocator>(elem: T, n: usize, alloc: A) -> Vec<T, A> { - if elem.is_zero() { - return Vec { buf: RawVec::with_capacity_zeroed_in(n, alloc), len: n }; - } - let mut v = Vec::with_capacity_in(n, alloc); - v.extend_with(n, ExtendElement(elem)); - v - } -} |
