diff options
| author | Josh Stone <jistone@redhat.com> | 2021-01-08 13:02:23 -0800 |
|---|---|---|
| committer | Josh Stone <jistone@redhat.com> | 2021-01-11 17:43:10 -0800 |
| commit | 9aa7dd1e6afc0f8c944c63458fba0ea19ae2c392 (patch) | |
| tree | 3b142ea782aa5edc03f4179fcf14c410cee6ab22 /library/alloc/src | |
| parent | fe531d5a5f1404281e3fb237daaf87b8180bd13d (diff) | |
| download | rust-9aa7dd1e6afc0f8c944c63458fba0ea19ae2c392.tar.gz rust-9aa7dd1e6afc0f8c944c63458fba0ea19ae2c392.zip | |
Specialize Box clones to try to avoid locals
For generic `T: Clone`, we can allocate an uninitialized box beforehand, which gives the optimizer a chance to create the clone directly in the heap. For `T: Copy`, we can go further and do a simple memory copy, regardless of optimization level.
Diffstat (limited to 'library/alloc/src')
| -rw-r--r-- | library/alloc/src/boxed.rs | 30 |
1 files changed, 28 insertions, 2 deletions
diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 33b812ec59f..7a9cf1948ea 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -1014,10 +1014,14 @@ impl<T: Clone, A: Allocator + Clone> Clone for Box<T, A> { /// // But they are unique objects /// assert_ne!(&*x as *const i32, &*y as *const i32); /// ``` - #[rustfmt::skip] #[inline] fn clone(&self) -> Self { - Self::new_in((**self).clone(), self.1.clone()) + // Pre-allocate memory to allow writing the cloned value directly. + let mut boxed = Self::new_uninit_in(self.1.clone()); + unsafe { + (**self).write_clone_into_raw(boxed.as_mut_ptr()); + boxed.assume_init() + } } /// Copies `source`'s contents into `self` without creating a new allocation. @@ -1043,6 +1047,28 @@ impl<T: Clone, A: Allocator + Clone> Clone for Box<T, A> { } } +/// Specialize clones into pre-allocated, uninitialized memory. +pub(crate) trait WriteCloneIntoRaw: Sized { + unsafe fn write_clone_into_raw(&self, target: *mut Self); +} + +impl<T: Clone> WriteCloneIntoRaw for T { + #[inline] + default unsafe fn write_clone_into_raw(&self, target: *mut Self) { + // Having allocated *first* may allow the optimizer to create + // the cloned value in-place, skipping the local and move. + unsafe { target.write(self.clone()) }; + } +} + +impl<T: Copy> WriteCloneIntoRaw for T { + #[inline] + unsafe fn write_clone_into_raw(&self, target: *mut Self) { + // We can always copy in-place, without ever involving a local value. + unsafe { target.copy_from_nonoverlapping(self, 1) }; + } +} + #[stable(feature = "box_slice_clone", since = "1.3.0")] impl Clone for Box<str> { fn clone(&self) -> Self { |
