Skip to content

Commit fc8c039

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 fc8c039

File tree

1 file changed

+32
-9
lines changed

1 file changed

+32
-9
lines changed

library/alloc/src/vec/is_zero.rs

Lines changed: 32 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,45 @@ 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 could probably just return `true` here, since implementing
67+
// `IsZero` for a zero-sized type such that `self.is_zero()` returns
68+
// `false` would be useless, but to be safe we check anyway.
69+
self.get(0).is_none_or(IsZero::is_zero)
70+
} else {
71+
// Because this is generated as a runtime check, it's not obvious that
72+
// it's worth doing if the array is really long. The threshold here
73+
// is largely arbitrary, but was picked because as of 2022-07-01 LLVM
74+
// fails to const-fold the check in `vec![[1; 32]; n]`
75+
// See https://github.com/rust-lang/rust/pull/97581#issuecomment-1166628022
76+
// Feel free to tweak if you have better evidence.
77+
78+
N <= 16 && self.iter().all(IsZero::is_zero)
79+
}
5780
}
5881
}
5982

6083
// This is recursive macro.
6184
macro_rules! impl_is_zero_tuples {
6285
// Stopper
6386
() => {
64-
// No use for implementing for empty tuple because it is ZST.
87+
// We already have an impl for () above.
6588
};
6689
($first_arg:ident $(,$rest:ident)*) => {
6790
unsafe impl <$first_arg: IsZero, $($rest: IsZero,)*> IsZero for ($first_arg, $($rest,)*){

0 commit comments

Comments
 (0)