-
Notifications
You must be signed in to change notification settings - Fork 405
Commit 92ed621
committed
Auto merge of #148737 - zachs18:unit-is-zero, r=joboet
Implement IsZero for (), and optimize `IsZero::is_zero` for arrays
These are probably not super useful optimizations, but they make it so that `vec![expr; LARGE_LENGTH]` has better performance for some `expr`s, e.g.
* array of length zero in debug mode
* tuple containing `()` and zero-valued integers in debug and release mode
* array of `()` or other zero-sized `IsZero` type in debug mode
<details> <summary>very rough benchmarks</summary>
```Rust
use std::time::Instant;
use std::sync::atomic::{AtomicUsize, Ordering::Relaxed};
struct NonCopyZst;
static COUNTER: AtomicUsize = AtomicUsize::new(0);
impl Clone for NonCopyZst {
fn clone(&self) -> Self {
COUNTER.fetch_add(1, Relaxed);
Self
}
}
macro_rules! timeit {
($e:expr) => {
let start = Instant::now();
_ = $e;
println!("{:56}: {:?}", stringify!($e), start.elapsed());
};
}
fn main() {
timeit!(vec![[String::from("hello"); 0]; 1_000_000_000]); // gets a lot better in debug mode
timeit!(vec![(0u8, (), 0u16); 1_000_000_000]); // gets a lot better in debug *and* release mode
timeit!(vec![[[(); 37]; 1_000_000_000]; 1_000_000_000]); // gets a lot better in debug mode
timeit!(vec![[NonCopyZst; 0]; 1_000_000_000]); // gets a lot better in debug mode
timeit!(vec![[[1u8; 0]; 1_000_000]; 1_000_000]); // gets a little bit better in debug mode
timeit!(vec![[[(); 37]; 1_000_000]; 1_000_000]); // gets a little bit better in debug mode
timeit!(vec![[[1u128; 0]; 1_000_000]; 1_000_000]); // gets a little bit better in debug mode
// check that we don't regress existing optimizations
timeit!(vec![(0u8, 0u16); 1_000_000_000]); // about the same time
timeit!(vec![0u32; 1_000_000_000]); // about the same time
// check that we still call clone for non-IsZero ZSTs
timeit!(vec![[const { NonCopyZst }; 2]; 1_000]); // about the same time
assert_eq!(COUNTER.load(Relaxed), 1998);
timeit!(vec![NonCopyZst; 10_000]); // about the same time
assert_eq!(COUNTER.load(Relaxed), 1998 + 9_999);
}
```
```rs
$ cargo +nightly run
// ...
vec![[String::from("hello"); 0]; 1_000_000_000] : 11.13999724s
vec![(0u8, (), 0u16); 1_000_000_000] : 5.254646651s
vec![[[(); 37]; 1_000_000_000]; 1_000_000_000] : 2.738062531s
vec![[NonCopyZst; 0]; 1_000_000_000] : 9.483690922s
vec![[[1u8; 0]; 1_000_000]; 1_000_000] : 2.919236ms
vec![[[(); 37]; 1_000_000]; 1_000_000] : 2.927755ms
vec![[[1u128; 0]; 1_000_000]; 1_000_000] : 2.931486ms
vec![(0u8, 0u16); 1_000_000_000] : 19.46µs
vec![0u32; 1_000_000_000] : 9.34µs
vec![[const { NonCopyZst }; 2]; 1_000] : 31.88µs
vec![NonCopyZst; 10_000] : 36.519µs
```
```rs
$ cargo +dev run
// ...
vec![[String::from("hello"); 0]; 1_000_000_000] : 4.12µs
vec![(0u8, (), 0u16); 1_000_000_000] : 16.299µs
vec![[[(); 37]; 1_000_000_000]; 1_000_000_000] : 210ns
vec![[NonCopyZst; 0]; 1_000_000_000] : 210ns
vec![[[1u8; 0]; 1_000_000]; 1_000_000] : 170ns
vec![[[(); 37]; 1_000_000]; 1_000_000] : 110ns
vec![[[1u128; 0]; 1_000_000]; 1_000_000] : 140ns
vec![(0u8, 0u16); 1_000_000_000] : 11.56µs
vec![0u32; 1_000_000_000] : 10.71µs
vec![[const { NonCopyZst }; 2]; 1_000] : 36.08µs
vec![NonCopyZst; 10_000] : 73.21µs
```
(checking release mode to make sure this doesn't regress perf there)
```rs
$ cargo +nightly run --release
// ...
vec![[String::from("hello"); 0]; 1_000_000_000] : 70ns
vec![(0u8, (), 0u16); 1_000_000_000] : 1.269457501s
vec![[[(); 37]; 1_000_000_000]; 1_000_000_000] : 10ns
vec![[NonCopyZst; 0]; 1_000_000_000] : 20ns
vec![[[1u8; 0]; 1_000_000]; 1_000_000] : 10ns
vec![[[(); 37]; 1_000_000]; 1_000_000] : 20ns
vec![[[1u128; 0]; 1_000_000]; 1_000_000] : 20ns
vec![(0u8, 0u16); 1_000_000_000] : 20ns
vec![0u32; 1_000_000_000] : 20ns
vec![[const { NonCopyZst }; 2]; 1_000] : 2.66µs
vec![NonCopyZst; 10_000] : 13.39µs
```
```rs
$ cargo +dev run --release
vec![[String::from("hello"); 0]; 1_000_000_000] : 90ns
vec![(0u8, (), 0u16); 1_000_000_000] : 30ns
vec![[[(); 37]; 1_000_000_000]; 1_000_000_000] : 20ns
vec![[NonCopyZst; 0]; 1_000_000_000] : 30ns
vec![[[1u8; 0]; 1_000_000]; 1_000_000] : 20ns
vec![[[(); 37]; 1_000_000]; 1_000_000] : 20ns
vec![[[1u128; 0]; 1_000_000]; 1_000_000] : 20ns
vec![(0u8, 0u16); 1_000_000_000] : 30ns
vec![0u32; 1_000_000_000] : 20ns
vec![[const { NonCopyZst }; 2]; 1_000] : 3.52µs
vec![NonCopyZst; 10_000] : 17.13µs
```
</details>
The specific expression I ran into a perf issue that this PR addresses is `vec![[(); LARGE]; LARGE]`, as I was trying to demonstrate `Vec::into_flattened` panicking on length overflow in the playground, but got a timeout error instead since `vec![[(); LARGE]; LARGE]` took so long to run in debug mode (it runs fine on the playground in release mode)File tree
Expand file treeCollapse file tree
0 file changed
+0
-0
lines changedOpen diff view settings
Filter options
Expand file treeCollapse file tree
0 file changed
+0
-0
lines changedOpen diff view settings
0 commit comments