From 0aaa3ae47bc89a0a712999c89f8c542bfcef0e2d Mon Sep 17 00:00:00 2001 From: Zachary S Date: Sat, 8 Nov 2025 22:24:41 -0600 Subject: [PATCH] Optimize Vec::from_elem for some more cases. Implement IsZero for (). Implement default `IsZero` for all arrays, only returning true if the array is empty (making the existing array impl for `IsZero` elements a specialization). Optimize `IsZero::is_zero` for arrays of zero-sized `IsZero` elements. --- library/alloc/src/vec/is_zero.rs | 42 +++++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/library/alloc/src/vec/is_zero.rs b/library/alloc/src/vec/is_zero.rs index a3ddd6f6e230e..04b50e5762986 100644 --- a/library/alloc/src/vec/is_zero.rs +++ b/library/alloc/src/vec/is_zero.rs @@ -1,3 +1,4 @@ +use core::mem::SizedTypeProperties; use core::num::{NonZero, Saturating, Wrapping}; use crate::boxed::Box; @@ -20,6 +21,8 @@ macro_rules! impl_is_zero { }; } +impl_is_zero!((), |_: ()| true); // It is needed to impl for arrays and tuples of (). + 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); @@ -43,17 +46,38 @@ impl_is_zero!(f64, |x: f64| x.to_bits() == 0); // `IsZero` cannot be soundly implemented for pointers because of provenance // (see #135338). +unsafe impl IsZero for [T; N] { + #[inline] + default fn is_zero(&self) -> bool { + // If the array is of length zero, + // then it doesn't actually contain any `T`s, + // so `T::clone` doesn't need to be called, + // and we can "zero-initialize" all zero bytes of the array. + N == 0 + } +} + unsafe impl IsZero for [T; N] { #[inline] 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-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 <= 16 && self.iter().all(IsZero::is_zero) + if T::IS_ZST { + // If T is a ZST, then there is at most one possible value of `T`, + // so we only need to check one element for zeroness. + // We can't unconditionally return `true` here, since, e.g. + // `T = [NonTrivialCloneZst; 5]` is a ZST that implements `IsZero` + // due to the generic array impl, but `T::is_zero` returns `false` + // since the length is not 0. + self.get(0).is_none_or(IsZero::is_zero) + } else { + // 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-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 <= 16 && self.iter().all(IsZero::is_zero) + } } } @@ -61,7 +85,7 @@ unsafe impl IsZero for [T; N] { macro_rules! impl_is_zero_tuples { // Stopper () => { - // No use for implementing for empty tuple because it is ZST. + // We already have an impl for () above. }; ($first_arg:ident $(,$rest:ident)*) => { unsafe impl <$first_arg: IsZero, $($rest: IsZero,)*> IsZero for ($first_arg, $($rest,)*){