From 81632513c13495fd269082d35916ebcd91d15658 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Thu, 19 Dec 2013 13:23:37 +1100 Subject: [PATCH 1/2] std::vec: replace .insert with a small amount of unsafe code. This makes the included benchmark more than 3 times faster. Also, `.unshift(x)` is now faster as `.insert(0, x)` which can reuse the allocation if necessary. --- src/libstd/vec.rs | 38 ++++++++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/src/libstd/vec.rs b/src/libstd/vec.rs index 65be214c14e..e58be99e564 100644 --- a/src/libstd/vec.rs +++ b/src/libstd/vec.rs @@ -1661,20 +1661,28 @@ impl OwnedVector for ~[T] { } fn unshift(&mut self, x: T) { - let v = util::replace(self, ~[x]); - self.push_all_move(v); + self.insert(0, x) } - fn insert(&mut self, i: uint, x:T) { + + fn insert(&mut self, i: uint, x: T) { let len = self.len(); assert!(i <= len); + // space for the new element + self.reserve_additional(1); - self.push(x); - let mut j = len; - while j > i { - self.swap(j, j - 1); - j -= 1; + unsafe { // infallible + // The spot to put the new value + let p = self.as_mut_ptr().offset(i as int); + // Shift everything over to make space. (Duplicating the + // `i`th element into two consecutive places.) + ptr::copy_memory(p.offset(1), p, len - i); + // Write it in, overwriting the first copy of the `i`th + // element. + intrinsics::move_val_init(&mut *p, x); + self.set_len(len + 1); } } + fn remove(&mut self, i: uint) -> T { let len = self.len(); assert!(i < len); @@ -4144,6 +4152,7 @@ mod bench { use vec::VectorVector; use option::*; use ptr; + use rand::{weak_rng, Rng}; #[bench] fn iterator(bh: &mut BenchHarness) { @@ -4320,4 +4329,17 @@ mod bench { } }); } + + #[bench] + fn random_inserts(bh: &mut BenchHarness) { + let mut rng = weak_rng(); + bh.iter(|| { + let mut v = vec::from_elem(30, (0u, 0u)); + for _ in range(0, 100) { + let l = v.len(); + v.insert(rng.gen::() % (l + 1), + (1, 1)); + } + }) + } } From acd2be46f144e932d207680a7ab1c51b529c20e3 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Thu, 19 Dec 2013 13:56:53 +1100 Subject: [PATCH 2/2] std::vec: use some unsafe code to optimise `remove`. Also, add `.remove_opt` and replace `.unshift` with `.remove(0)`. The code size reduction seem to compensate for not having the optimised special cases. This makes the included benchmark more than 3 times faster. --- src/libstd/vec.rs | 117 ++++++++++++++++++++++++++++++---------------- 1 file changed, 76 insertions(+), 41 deletions(-) diff --git a/src/libstd/vec.rs b/src/libstd/vec.rs index e58be99e564..a5c99348357 100644 --- a/src/libstd/vec.rs +++ b/src/libstd/vec.rs @@ -1416,6 +1416,22 @@ pub trait OwnedVector { /// elements after position i one position to the right. fn insert(&mut self, i: uint, x:T); + /// Remove and return the element at position `i` within `v`, + /// shifting all elements after position `i` one position to the + /// left. Returns `None` if `i` is out of bounds. + /// + /// # Example + /// ```rust + /// let mut v = ~[1, 2, 3]; + /// assert_eq!(v.remove_opt(1), Some(2)); + /// assert_eq!(v, ~[1, 3]); + /// + /// assert_eq!(v.remove_opt(4), None); + /// // v is unchanged: + /// assert_eq!(v, ~[1, 3]); + /// ``` + fn remove_opt(&mut self, i: uint) -> Option; + /// Remove and return the element at position i within v, shifting /// all elements after position i one position to the left. fn remove(&mut self, i: uint) -> T; @@ -1625,39 +1641,7 @@ impl OwnedVector for ~[T] { } fn shift_opt(&mut self) -> Option { - match self.len() { - 0 => None, - 1 => self.pop_opt(), - 2 => { - let last = self.pop(); - let first = self.pop_opt(); - self.push(last); - first - } - len => { - unsafe { - let next_len = len - 1; - - let ptr = self.as_ptr(); - - // copy out the head element, for the moment it exists - // unsafely on the stack and as the first element of the - // vector. - let head = ptr::read_ptr(ptr); - - // Memcpy everything to the left one element (leaving the - // last element unsafely in two consecutive memory - // locations) - ptr::copy_memory(self.as_mut_ptr(), ptr.offset(1), next_len); - - // set the new length, which means the second instance of - // the last element is forgotten. - self.set_len(next_len); - - Some(head) - } - } - } + self.remove_opt(0) } fn unshift(&mut self, x: T) { @@ -1683,16 +1667,33 @@ impl OwnedVector for ~[T] { } } + #[inline] fn remove(&mut self, i: uint) -> T { - let len = self.len(); - assert!(i < len); - - let mut j = i; - while j < len - 1 { - self.swap(j, j + 1); - j += 1; + match self.remove_opt(i) { + Some(t) => t, + None => fail!("remove: the len is {} but the index is {}", self.len(), i) + } + } + + fn remove_opt(&mut self, i: uint) -> Option { + let len = self.len(); + if i < len { + unsafe { // infallible + // the place we are taking from. + let ptr = self.as_mut_ptr().offset(i as int); + // copy it out, unsafely having a copy of the value on + // the stack and in the vector at the same time. + let ret = Some(ptr::read_ptr(ptr as *T)); + + // Shift everything down to fill in that spot. + ptr::copy_memory(ptr, ptr.offset(1), len - i - 1); + self.set_len(len - 1); + + ret + } + } else { + None } - self.pop() } fn swap_remove(&mut self, index: uint) -> T { let ln = self.len(); @@ -3422,6 +3423,29 @@ mod tests { a.insert(4, 5); } + #[test] + fn test_remove_opt() { + let mut a = ~[1,2,3,4]; + + assert_eq!(a.remove_opt(2), Some(3)); + assert_eq!(a, ~[1,2,4]); + + assert_eq!(a.remove_opt(2), Some(4)); + assert_eq!(a, ~[1,2]); + + assert_eq!(a.remove_opt(2), None); + assert_eq!(a, ~[1,2]); + + assert_eq!(a.remove_opt(0), Some(1)); + assert_eq!(a, ~[2]); + + assert_eq!(a.remove_opt(0), Some(2)); + assert_eq!(a, ~[]); + + assert_eq!(a.remove_opt(0), None); + assert_eq!(a.remove_opt(10), None); + } + #[test] fn test_remove() { let mut a = ~[1, 2, 3, 4]; @@ -4342,4 +4366,15 @@ mod bench { } }) } + #[bench] + fn random_removes(bh: &mut BenchHarness) { + let mut rng = weak_rng(); + bh.iter(|| { + let mut v = vec::from_elem(130, (0u, 0u)); + for _ in range(0, 100) { + let l = v.len(); + v.remove(rng.gen::() % l); + } + }) + } }