Skip to content

Commit 9aa7dd1

Browse files
committed
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.
1 parent fe531d5 commit 9aa7dd1

File tree

1 file changed

+28
-2
lines changed

1 file changed

+28
-2
lines changed

library/alloc/src/boxed.rs

+28-2
Original file line numberDiff line numberDiff line change
@@ -1014,10 +1014,14 @@ impl<T: Clone, A: Allocator + Clone> Clone for Box<T, A> {
10141014
/// // But they are unique objects
10151015
/// assert_ne!(&*x as *const i32, &*y as *const i32);
10161016
/// ```
1017-
#[rustfmt::skip]
10181017
#[inline]
10191018
fn clone(&self) -> Self {
1020-
Self::new_in((**self).clone(), self.1.clone())
1019+
// Pre-allocate memory to allow writing the cloned value directly.
1020+
let mut boxed = Self::new_uninit_in(self.1.clone());
1021+
unsafe {
1022+
(**self).write_clone_into_raw(boxed.as_mut_ptr());
1023+
boxed.assume_init()
1024+
}
10211025
}
10221026

10231027
/// 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> {
10431047
}
10441048
}
10451049

1050+
/// Specialize clones into pre-allocated, uninitialized memory.
1051+
pub(crate) trait WriteCloneIntoRaw: Sized {
1052+
unsafe fn write_clone_into_raw(&self, target: *mut Self);
1053+
}
1054+
1055+
impl<T: Clone> WriteCloneIntoRaw for T {
1056+
#[inline]
1057+
default unsafe fn write_clone_into_raw(&self, target: *mut Self) {
1058+
// Having allocated *first* may allow the optimizer to create
1059+
// the cloned value in-place, skipping the local and move.
1060+
unsafe { target.write(self.clone()) };
1061+
}
1062+
}
1063+
1064+
impl<T: Copy> WriteCloneIntoRaw for T {
1065+
#[inline]
1066+
unsafe fn write_clone_into_raw(&self, target: *mut Self) {
1067+
// We can always copy in-place, without ever involving a local value.
1068+
unsafe { target.copy_from_nonoverlapping(self, 1) };
1069+
}
1070+
}
1071+
10461072
#[stable(feature = "box_slice_clone", since = "1.3.0")]
10471073
impl Clone for Box<str> {
10481074
fn clone(&self) -> Self {

0 commit comments

Comments
 (0)