Skip to content

Commit 0aaa3ae

Browse files
committed
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.
1 parent acda5e9 commit 0aaa3ae

File tree

1 file changed

+33
-9
lines changed

1 file changed

+33
-9
lines changed

library/alloc/src/vec/is_zero.rs

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use core::mem::SizedTypeProperties;
12
use core::num::{NonZero, Saturating, Wrapping};
23

34
use crate::boxed::Box;
@@ -20,6 +21,8 @@ macro_rules! impl_is_zero {
2021
};
2122
}
2223

24+
impl_is_zero!((), |_: ()| true); // It is needed to impl for arrays and tuples of ().
25+
2326
impl_is_zero!(i8, |x| x == 0); // It is needed to impl for arrays and tuples of i8.
2427
impl_is_zero!(i16, |x| x == 0);
2528
impl_is_zero!(i32, |x| x == 0);
@@ -43,25 +46,46 @@ impl_is_zero!(f64, |x: f64| x.to_bits() == 0);
4346
// `IsZero` cannot be soundly implemented for pointers because of provenance
4447
// (see #135338).
4548

49+
unsafe impl<T, const N: usize> IsZero for [T; N] {
50+
#[inline]
51+
default fn is_zero(&self) -> bool {
52+
// If the array is of length zero,
53+
// then it doesn't actually contain any `T`s,
54+
// so `T::clone` doesn't need to be called,
55+
// and we can "zero-initialize" all zero bytes of the array.
56+
N == 0
57+
}
58+
}
59+
4660
unsafe impl<T: IsZero, const N: usize> IsZero for [T; N] {
4761
#[inline]
4862
fn is_zero(&self) -> bool {
49-
// Because this is generated as a runtime check, it's not obvious that
50-
// it's worth doing if the array is really long. The threshold here
51-
// is largely arbitrary, but was picked because as of 2022-07-01 LLVM
52-
// fails to const-fold the check in `vec![[1; 32]; n]`
53-
// See https://github.com/rust-lang/rust/pull/97581#issuecomment-1166628022
54-
// Feel free to tweak if you have better evidence.
55-
56-
N <= 16 && self.iter().all(IsZero::is_zero)
63+
if T::IS_ZST {
64+
// If T is a ZST, then there is at most one possible value of `T`,
65+
// so we only need to check one element for zeroness.
66+
// We can't unconditionally return `true` here, since, e.g.
67+
// `T = [NonTrivialCloneZst; 5]` is a ZST that implements `IsZero`
68+
// due to the generic array impl, but `T::is_zero` returns `false`
69+
// since the length is not 0.
70+
self.get(0).is_none_or(IsZero::is_zero)
71+
} else {
72+
// Because this is generated as a runtime check, it's not obvious that
73+
// it's worth doing if the array is really long. The threshold here
74+
// is largely arbitrary, but was picked because as of 2022-07-01 LLVM
75+
// fails to const-fold the check in `vec![[1; 32]; n]`
76+
// See https://github.com/rust-lang/rust/pull/97581#issuecomment-1166628022
77+
// Feel free to tweak if you have better evidence.
78+
79+
N <= 16 && self.iter().all(IsZero::is_zero)
80+
}
5781
}
5882
}
5983

6084
// This is recursive macro.
6185
macro_rules! impl_is_zero_tuples {
6286
// Stopper
6387
() => {
64-
// No use for implementing for empty tuple because it is ZST.
88+
// We already have an impl for () above.
6589
};
6690
($first_arg:ident $(,$rest:ident)*) => {
6791
unsafe impl <$first_arg: IsZero, $($rest: IsZero,)*> IsZero for ($first_arg, $($rest,)*){

0 commit comments

Comments
 (0)