diff --git a/CHANGELOG.md b/CHANGELOG.md index 7808dfac..7499d5ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Changed +* Optimize the codegen of `Vec::clone` + ### Fixed * Inserting an item that replaces an already present item will no longer diff --git a/src/vec.rs b/src/vec.rs index 31d98ca7..bef2edd0 100644 --- a/src/vec.rs +++ b/src/vec.rs @@ -34,12 +34,18 @@ use hash32; /// assert_eq!(*vec, [7, 1, 2, 3]); /// ``` pub struct Vec { - buffer: [MaybeUninit; N], + // NOTE order is important for optimizations. the `len` first layout lets the compiler optimize + // `new` to: reserve stack space and zero the first word. With the fields in the reverse order + // the compiler optimizes `new` to `memclr`-ing the *entire* stack space, including the `buffer` + // field which should be left uninitialized. Optimizations were last checked with Rust 1.60 len: usize, + + buffer: [MaybeUninit; N], } impl Vec { - const INIT: MaybeUninit = MaybeUninit::uninit(); + const ELEM: MaybeUninit = MaybeUninit::uninit(); + const INIT: [MaybeUninit; N] = [Self::ELEM; N]; // important for optimization of `new` /// Constructs a new, empty vector with a fixed capacity of `N` /// @@ -60,8 +66,8 @@ impl Vec { crate::sealed::greater_than_eq_0::(); Self { - buffer: [Self::INIT; N], len: 0, + buffer: Self::INIT, } } @@ -92,7 +98,12 @@ impl Vec { T: Clone, { let mut new = Self::new(); - new.extend_from_slice(self.as_slice()).unwrap(); + // avoid `extend_from_slice` as that introduces a runtime check / panicking branch + for elem in self { + unsafe { + new.push_unchecked(elem.clone()); + } + } new }