From 42b39924d87739f2dcda3e788c6e8655d310954f Mon Sep 17 00:00:00 2001 From: James Miller Date: Wed, 16 Apr 2014 11:35:18 +1200 Subject: [PATCH 1/2] Improve the copying code for slices and Vec --- src/libstd/slice.rs | 20 ++++++++++++++++---- src/libstd/vec.rs | 21 ++++++++++++++++++++- 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/src/libstd/slice.rs b/src/libstd/slice.rs index aedaa5b3c159f..f4f97ea8c9202 100644 --- a/src/libstd/slice.rs +++ b/src/libstd/slice.rs @@ -760,9 +760,20 @@ impl<'a, T: Clone> CloneableVector for &'a [T] { /// Returns a copy of `v`. #[inline] fn to_owned(&self) -> ~[T] { - let mut result = with_capacity(self.len()); - for e in self.iter() { - result.push((*e).clone()); + let len = self.len(); + let mut result = with_capacity(len); + unsafe { + // Unsafe code so this can be optimised to a memcpy (or something + // similarly fast) when T is Copy. LLVM is easily confused, so any + // extra operations during the loop can prevent this optimisation + result.set_len(len); + let mut i = 0; + while i < len { + mem::move_val_init( + result.unsafe_mut_ref(i), + self.unsafe_ref(i).clone()); + i = i + 1; + } } result } @@ -2584,7 +2595,8 @@ pub mod bytes { impl Clone for ~[A] { #[inline] fn clone(&self) -> ~[A] { - self.iter().map(|item| item.clone()).collect() + // Use the fast to_owned on &[A] for cloning + self.as_slice().to_owned() } fn clone_from(&mut self, source: &~[A]) { diff --git a/src/libstd/vec.rs b/src/libstd/vec.rs index a69120de00f7d..034d53aa78bdb 100644 --- a/src/libstd/vec.rs +++ b/src/libstd/vec.rs @@ -311,7 +311,26 @@ impl Vec { impl Clone for Vec { fn clone(&self) -> Vec { - self.iter().map(|x| x.clone()).collect() + let len = self.len; + let mut vector = Vec::with_capacity(len); + vector.len = len; + // Unsafe code so this can be optimised to a memcpy (or something + // similarly fast) when T is Copy. LLVM is easily confused, so any + // extra operations during the loop can prevent this optimisation + { + let slice = vector.as_mut_slice(); + let this_slice = self.as_slice(); + let mut i = 0; + while i < len { + unsafe { + mem::move_val_init( + slice.unsafe_mut_ref(i), + this_slice.unsafe_ref(i).clone()); + } + i = i + 1; + } + } + vector } fn clone_from(&mut self, other: &Vec) { From be334d582435a05ea56c1ca7fcb2e512cfc51f24 Mon Sep 17 00:00:00 2001 From: James Miller Date: Wed, 16 Apr 2014 14:29:36 +1200 Subject: [PATCH 2/2] Make Vec::clone and slice::to_owned failure-safe --- src/libstd/slice.rs | 25 +++++++++++++++---------- src/libstd/vec.rs | 11 ++++------- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/src/libstd/slice.rs b/src/libstd/slice.rs index f4f97ea8c9202..153e21c780c09 100644 --- a/src/libstd/slice.rs +++ b/src/libstd/slice.rs @@ -762,18 +762,23 @@ impl<'a, T: Clone> CloneableVector for &'a [T] { fn to_owned(&self) -> ~[T] { let len = self.len(); let mut result = with_capacity(len); + // Unsafe code so this can be optimised to a memcpy (or something + // similarly fast) when T is Copy. LLVM is easily confused, so any + // extra operations during the loop can prevent this optimisation unsafe { - // Unsafe code so this can be optimised to a memcpy (or something - // similarly fast) when T is Copy. LLVM is easily confused, so any - // extra operations during the loop can prevent this optimisation - result.set_len(len); let mut i = 0; - while i < len { - mem::move_val_init( - result.unsafe_mut_ref(i), - self.unsafe_ref(i).clone()); - i = i + 1; - } + let p = result.as_mut_ptr(); + // Use try_finally here otherwise the write to length + // inside the loop stops LLVM from optimising this. + try_finally( + &mut i, (), + |i, ()| while *i < len { + mem::move_val_init( + &mut(*p.offset(*i as int)), + self.unsafe_ref(*i).clone()); + *i += 1; + }, + |i| result.set_len(*i)); } result } diff --git a/src/libstd/vec.rs b/src/libstd/vec.rs index 034d53aa78bdb..96cbac8869ef2 100644 --- a/src/libstd/vec.rs +++ b/src/libstd/vec.rs @@ -313,21 +313,18 @@ impl Clone for Vec { fn clone(&self) -> Vec { let len = self.len; let mut vector = Vec::with_capacity(len); - vector.len = len; // Unsafe code so this can be optimised to a memcpy (or something // similarly fast) when T is Copy. LLVM is easily confused, so any // extra operations during the loop can prevent this optimisation { - let slice = vector.as_mut_slice(); let this_slice = self.as_slice(); - let mut i = 0; - while i < len { + while vector.len < len { unsafe { mem::move_val_init( - slice.unsafe_mut_ref(i), - this_slice.unsafe_ref(i).clone()); + vector.as_mut_slice().unsafe_mut_ref(vector.len), + this_slice.unsafe_ref(vector.len).clone()); } - i = i + 1; + vector.len += 1; } } vector