From 065b98d5774954a42733bcc3de382029dcdcf0cf Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Sat, 5 Jul 2014 23:07:28 -0700 Subject: [PATCH 1/6] collections: flesh out Vec benchmarks --- src/libcollections/vec.rs | 418 +++++++++++++++++++++++++++++++++----- 1 file changed, 364 insertions(+), 54 deletions(-) diff --git a/src/libcollections/vec.rs b/src/libcollections/vec.rs index d53ecabd5a9cb..e10fc66ea8567 100644 --- a/src/libcollections/vec.rs +++ b/src/libcollections/vec.rs @@ -1857,107 +1857,417 @@ mod tests { #[bench] fn bench_new(b: &mut Bencher) { b.iter(|| { - let v: Vec = Vec::new(); + let v: Vec = Vec::new(); + assert_eq!(v.len(), 0); assert_eq!(v.capacity(), 0); - assert!(v.as_slice() == []); }) } - #[bench] - fn bench_with_capacity_0(b: &mut Bencher) { + fn do_bench_with_capacity(b: &mut Bencher, src_len: uint) { + b.bytes = src_len as u64; + b.iter(|| { - let v: Vec = Vec::with_capacity(0); - assert_eq!(v.capacity(), 0); - assert!(v.as_slice() == []); + let v: Vec = Vec::with_capacity(src_len); + assert_eq!(v.len(), 0); + assert_eq!(v.capacity(), src_len); }) } + #[bench] + fn bench_with_capacity_0000(b: &mut Bencher) { + do_bench_with_capacity(b, 0) + } #[bench] - fn bench_with_capacity_5(b: &mut Bencher) { - b.iter(|| { - let v: Vec = Vec::with_capacity(5); - assert_eq!(v.capacity(), 5); - assert!(v.as_slice() == []); - }) + fn bench_with_capacity_0010(b: &mut Bencher) { + do_bench_with_capacity(b, 10) + } + + #[bench] + fn bench_with_capacity_0100(b: &mut Bencher) { + do_bench_with_capacity(b, 100) } #[bench] - fn bench_with_capacity_100(b: &mut Bencher) { + fn bench_with_capacity_1000(b: &mut Bencher) { + do_bench_with_capacity(b, 1000) + } + + fn do_bench_from_fn(b: &mut Bencher, src_len: uint) { + b.bytes = src_len as u64; + b.iter(|| { - let v: Vec = Vec::with_capacity(100); - assert_eq!(v.capacity(), 100); - assert!(v.as_slice() == []); + let dst = Vec::from_fn(src_len, |i| i); + assert_eq!(dst.len(), src_len); + assert!(dst.iter().enumerate().all(|(i, x)| i == *x)); }) } #[bench] - fn bench_from_fn_0(b: &mut Bencher) { + fn bench_from_fn_0000(b: &mut Bencher) { + do_bench_from_fn(b, 0) + } + + #[bench] + fn bench_from_fn_0010(b: &mut Bencher) { + do_bench_from_fn(b, 10) + } + + #[bench] + fn bench_from_fn_0100(b: &mut Bencher) { + do_bench_from_fn(b, 100) + } + + #[bench] + fn bench_from_fn_1000(b: &mut Bencher) { + do_bench_from_fn(b, 1000) + } + + fn do_bench_from_elem(b: &mut Bencher, src_len: uint) { + b.bytes = src_len as u64; + b.iter(|| { - let v: Vec = Vec::from_fn(0, |_| 5); - assert!(v.as_slice() == []); + let dst: Vec = Vec::from_elem(src_len, 5); + assert_eq!(dst.len(), src_len); + assert!(dst.iter().all(|x| *x == 5)); }) } #[bench] - fn bench_from_fn_5(b: &mut Bencher) { + fn bench_from_elem_0000(b: &mut Bencher) { + do_bench_from_elem(b, 0) + } + + #[bench] + fn bench_from_elem_0010(b: &mut Bencher) { + do_bench_from_elem(b, 10) + } + + #[bench] + fn bench_from_elem_0100(b: &mut Bencher) { + do_bench_from_elem(b, 100) + } + + #[bench] + fn bench_from_elem_1000(b: &mut Bencher) { + do_bench_from_elem(b, 1000) + } + + fn do_bench_from_slice(b: &mut Bencher, src_len: uint) { + let src: Vec = FromIterator::from_iter(range(0, src_len)); + + b.bytes = src_len as u64; + b.iter(|| { - let v: Vec = Vec::from_fn(5, |_| 5); - assert!(v.as_slice() == [5, 5, 5, 5, 5]); - }) + let dst = Vec::from_slice(src.clone().as_slice()); + assert_eq!(dst.len(), src_len); + assert!(dst.iter().enumerate().all(|(i, x)| i == *x)); + }); + } + + #[bench] + fn bench_from_slice_0000(b: &mut Bencher) { + do_bench_from_slice(b, 0) } #[bench] - fn bench_from_slice_0(b: &mut Bencher) { + fn bench_from_slice_0010(b: &mut Bencher) { + do_bench_from_slice(b, 10) + } + + #[bench] + fn bench_from_slice_0100(b: &mut Bencher) { + do_bench_from_slice(b, 100) + } + + #[bench] + fn bench_from_slice_1000(b: &mut Bencher) { + do_bench_from_slice(b, 1000) + } + + fn do_bench_from_iter(b: &mut Bencher, src_len: uint) { + let src: Vec = FromIterator::from_iter(range(0, src_len)); + + b.bytes = src_len as u64; + b.iter(|| { - let v: Vec = Vec::from_slice([]); - assert!(v.as_slice() == []); - }) + let dst: Vec = FromIterator::from_iter(src.clone().move_iter()); + assert_eq!(dst.len(), src_len); + assert!(dst.iter().enumerate().all(|(i, x)| i == *x)); + }); + } + + #[bench] + fn bench_from_iter_0000(b: &mut Bencher) { + do_bench_from_iter(b, 0) + } + + #[bench] + fn bench_from_iter_0010(b: &mut Bencher) { + do_bench_from_iter(b, 10) } #[bench] - fn bench_from_slice_5(b: &mut Bencher) { + fn bench_from_iter_0100(b: &mut Bencher) { + do_bench_from_iter(b, 100) + } + + #[bench] + fn bench_from_iter_1000(b: &mut Bencher) { + do_bench_from_iter(b, 1000) + } + + fn do_bench_extend(b: &mut Bencher, dst_len: uint, src_len: uint) { + let dst: Vec = FromIterator::from_iter(range(0, dst_len)); + let src: Vec = FromIterator::from_iter(range(dst_len, dst_len + src_len)); + + b.bytes = src_len as u64; + b.iter(|| { - let v: Vec = Vec::from_slice([1i, 2, 3, 4, 5]); - assert!(v.as_slice() == [1, 2, 3, 4, 5]); - }) + let mut dst = dst.clone(); + dst.extend(src.clone().move_iter()); + assert_eq!(dst.len(), dst_len + src_len); + assert!(dst.iter().enumerate().all(|(i, x)| i == *x)); + }); + } + + #[bench] + fn bench_extend_0000_0000(b: &mut Bencher) { + do_bench_extend(b, 0, 0) } #[bench] - fn bench_from_iter_0(b: &mut Bencher) { + fn bench_extend_0000_0010(b: &mut Bencher) { + do_bench_extend(b, 0, 10) + } + + #[bench] + fn bench_extend_0000_0100(b: &mut Bencher) { + do_bench_extend(b, 0, 100) + } + + #[bench] + fn bench_extend_0000_1000(b: &mut Bencher) { + do_bench_extend(b, 0, 1000) + } + + #[bench] + fn bench_extend_0010_0010(b: &mut Bencher) { + do_bench_extend(b, 10, 10) + } + + #[bench] + fn bench_extend_0100_0100(b: &mut Bencher) { + do_bench_extend(b, 100, 100) + } + + #[bench] + fn bench_extend_1000_1000(b: &mut Bencher) { + do_bench_extend(b, 1000, 1000) + } + + fn do_bench_push_all(b: &mut Bencher, dst_len: uint, src_len: uint) { + let dst: Vec = FromIterator::from_iter(range(0, dst_len)); + let src: Vec = FromIterator::from_iter(range(dst_len, dst_len + src_len)); + + b.bytes = src_len as u64; + b.iter(|| { - let v0: Vec = vec!(); - let v1: Vec = FromIterator::from_iter(v0.move_iter()); - assert!(v1.as_slice() == []); - }) + let mut dst = dst.clone(); + dst.push_all(src.as_slice()); + assert_eq!(dst.len(), dst_len + src_len); + assert!(dst.iter().enumerate().all(|(i, x)| i == *x)); + }); + } + + #[bench] + fn bench_push_all_0000_0000(b: &mut Bencher) { + do_bench_push_all(b, 0, 0) } #[bench] - fn bench_from_iter_5(b: &mut Bencher) { + fn bench_push_all_0000_0010(b: &mut Bencher) { + do_bench_push_all(b, 0, 10) + } + + #[bench] + fn bench_push_all_0000_0100(b: &mut Bencher) { + do_bench_push_all(b, 0, 100) + } + + #[bench] + fn bench_push_all_0000_1000(b: &mut Bencher) { + do_bench_push_all(b, 0, 1000) + } + + #[bench] + fn bench_push_all_0010_0010(b: &mut Bencher) { + do_bench_push_all(b, 10, 10) + } + + #[bench] + fn bench_push_all_0100_0100(b: &mut Bencher) { + do_bench_push_all(b, 100, 100) + } + + #[bench] + fn bench_push_all_1000_1000(b: &mut Bencher) { + do_bench_push_all(b, 1000, 1000) + } + + fn do_bench_push_all_move(b: &mut Bencher, dst_len: uint, src_len: uint) { + let dst: Vec = FromIterator::from_iter(range(0u, dst_len)); + let src: Vec = FromIterator::from_iter(range(dst_len, dst_len + src_len)); + + b.bytes = src_len as u64; + b.iter(|| { - let v0: Vec = vec!(1, 2, 3, 4, 5); - let v1: Vec = FromIterator::from_iter(v0.move_iter()); - assert!(v1.as_slice() == [1, 2, 3, 4, 5]); - }) + let mut dst = dst.clone(); + dst.push_all_move(src.clone()); + assert_eq!(dst.len(), dst_len + src_len); + assert!(dst.iter().enumerate().all(|(i, x)| i == *x)); + }); + } + + #[bench] + fn bench_push_all_move_0000_0000(b: &mut Bencher) { + do_bench_push_all_move(b, 0, 0) } #[bench] - fn bench_extend_0(b: &mut Bencher) { + fn bench_push_all_move_0000_0010(b: &mut Bencher) { + do_bench_push_all_move(b, 0, 10) + } + + #[bench] + fn bench_push_all_move_0000_0100(b: &mut Bencher) { + do_bench_push_all_move(b, 0, 100) + } + + #[bench] + fn bench_push_all_move_0000_1000(b: &mut Bencher) { + do_bench_push_all_move(b, 0, 1000) + } + + #[bench] + fn bench_push_all_move_0010_0010(b: &mut Bencher) { + do_bench_push_all_move(b, 10, 10) + } + + #[bench] + fn bench_push_all_move_0100_0100(b: &mut Bencher) { + do_bench_push_all_move(b, 100, 100) + } + + #[bench] + fn bench_push_all_move_1000_1000(b: &mut Bencher) { + do_bench_push_all_move(b, 1000, 1000) + } + + fn do_bench_clone(b: &mut Bencher, src_len: uint) { + let src: Vec = FromIterator::from_iter(range(0, src_len)); + + b.bytes = src_len as u64; + b.iter(|| { - let v0: Vec = vec!(); - let mut v1: Vec = vec!(1, 2, 3, 4, 5); - v1.extend(v0.move_iter()); - assert!(v1.as_slice() == [1, 2, 3, 4, 5]); - }) + let dst = src.clone(); + assert_eq!(dst.len(), src_len); + assert!(dst.iter().enumerate().all(|(i, x)| i == *x)); + }); } #[bench] - fn bench_extend_5(b: &mut Bencher) { + fn bench_clone_0000(b: &mut Bencher) { + do_bench_clone(b, 0) + } + + #[bench] + fn bench_clone_0010(b: &mut Bencher) { + do_bench_clone(b, 10) + } + + #[bench] + fn bench_clone_0100(b: &mut Bencher) { + do_bench_clone(b, 100) + } + + #[bench] + fn bench_clone_1000(b: &mut Bencher) { + do_bench_clone(b, 1000) + } + + fn do_bench_clone_from(b: &mut Bencher, dst_len: uint, src_len: uint) { + let dst: Vec = FromIterator::from_iter(range(0, src_len)); + let src: Vec = FromIterator::from_iter(range(dst_len, dst_len + src_len)); + + b.bytes = src_len as u64; + b.iter(|| { - let v0: Vec = vec!(1, 2, 3, 4, 5); - let mut v1: Vec = vec!(1, 2, 3, 4, 5); - v1.extend(v0.move_iter()); - assert!(v1.as_slice() == [1, 2, 3, 4, 5, 1, 2, 3, 4, 5]); - }) + let mut dst = dst.clone(); + let src = src.clone(); + dst.clone_from(&src); + assert_eq!(dst.len(), src_len); + assert!(dst.iter().enumerate().all(|(i, x)| dst_len + i == *x)); + }); + } + + #[bench] + fn bench_clone_from_0000_0000(b: &mut Bencher) { + do_bench_clone_from(b, 0, 0) + } + + #[bench] + fn bench_clone_from_0000_0010(b: &mut Bencher) { + do_bench_clone_from(b, 0, 10) + } + + #[bench] + fn bench_clone_from_0000_0100(b: &mut Bencher) { + do_bench_clone_from(b, 0, 100) + } + + #[bench] + fn bench_clone_from_0000_1000(b: &mut Bencher) { + do_bench_clone_from(b, 0, 1000) + } + + #[bench] + fn bench_clone_from_0010_0010(b: &mut Bencher) { + do_bench_clone_from(b, 10, 10) + } + + #[bench] + fn bench_clone_from_0100_0100(b: &mut Bencher) { + do_bench_clone_from(b, 100, 100) + } + + #[bench] + fn bench_clone_from_1000_1000(b: &mut Bencher) { + do_bench_clone_from(b, 1000, 1000) + } + + #[bench] + fn bench_clone_from_0010_0100(b: &mut Bencher) { + do_bench_clone_from(b, 10, 100) + } + + #[bench] + fn bench_clone_from_0100_1000(b: &mut Bencher) { + do_bench_clone_from(b, 100, 1000) + } + + #[bench] + fn bench_clone_from_0010_0000(b: &mut Bencher) { + do_bench_clone_from(b, 10, 0) + } + + #[bench] + fn bench_clone_from_0100_0010(b: &mut Bencher) { + do_bench_clone_from(b, 100, 10) + } + + #[bench] + fn bench_clone_from_1000_0100(b: &mut Bencher) { + do_bench_clone_from(b, 1000, 100) } } From f1ea540e9024db9b5d2e6a6e92431875feb345b3 Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Sat, 5 Jul 2014 23:11:18 -0700 Subject: [PATCH 2/6] collections: Optimize Vec when cloning from a slice llvm is currently not able to conver `Vec::extend` into a memcpy for `Copy` types, which results in methods like `Vec::push_all` to run twice as slow as it should be running. This patch takes the unsafe `Vec::clone` optimization to speed up all the operations that are cloning a slice into a `Vec`. before: test vec::tests::bench_clone_from_0000_0000 ... bench: 12 ns/iter (+/- 2) test vec::tests::bench_clone_from_0000_0010 ... bench: 125 ns/iter (+/- 4) = 80 MB/s test vec::tests::bench_clone_from_0000_0100 ... bench: 360 ns/iter (+/- 33) = 277 MB/s test vec::tests::bench_clone_from_0000_1000 ... bench: 2601 ns/iter (+/- 175) = 384 MB/s test vec::tests::bench_clone_from_0010_0000 ... bench: 12 ns/iter (+/- 2) test vec::tests::bench_clone_from_0010_0010 ... bench: 125 ns/iter (+/- 10) = 80 MB/s test vec::tests::bench_clone_from_0010_0100 ... bench: 361 ns/iter (+/- 28) = 277 MB/s test vec::tests::bench_clone_from_0100_0010 ... bench: 131 ns/iter (+/- 13) = 76 MB/s test vec::tests::bench_clone_from_0100_0100 ... bench: 360 ns/iter (+/- 9) = 277 MB/s test vec::tests::bench_clone_from_0100_1000 ... bench: 2575 ns/iter (+/- 168) = 388 MB/s test vec::tests::bench_clone_from_1000_0100 ... bench: 356 ns/iter (+/- 20) = 280 MB/s test vec::tests::bench_clone_from_1000_1000 ... bench: 2605 ns/iter (+/- 167) = 383 MB/s test vec::tests::bench_from_slice_0000 ... bench: 11 ns/iter (+/- 0) test vec::tests::bench_from_slice_0010 ... bench: 115 ns/iter (+/- 5) = 86 MB/s test vec::tests::bench_from_slice_0100 ... bench: 309 ns/iter (+/- 170) = 323 MB/s test vec::tests::bench_from_slice_1000 ... bench: 2065 ns/iter (+/- 198) = 484 MB/s test vec::tests::bench_push_all_0000_0000 ... bench: 7 ns/iter (+/- 0) test vec::tests::bench_push_all_0000_0010 ... bench: 79 ns/iter (+/- 7) = 126 MB/s test vec::tests::bench_push_all_0000_0100 ... bench: 342 ns/iter (+/- 18) = 292 MB/s test vec::tests::bench_push_all_0000_1000 ... bench: 2873 ns/iter (+/- 75) = 348 MB/s test vec::tests::bench_push_all_0010_0010 ... bench: 154 ns/iter (+/- 8) = 64 MB/s test vec::tests::bench_push_all_0100_0100 ... bench: 518 ns/iter (+/- 18) = 193 MB/s test vec::tests::bench_push_all_1000_1000 ... bench: 4490 ns/iter (+/- 223) = 222 MB/s after: test vec::tests::bench_clone_from_0000_0000 ... bench: 12 ns/iter (+/- 1) test vec::tests::bench_clone_from_0000_0010 ... bench: 123 ns/iter (+/- 5) = 81 MB/s test vec::tests::bench_clone_from_0000_0100 ... bench: 367 ns/iter (+/- 23) = 272 MB/s test vec::tests::bench_clone_from_0000_1000 ... bench: 2618 ns/iter (+/- 252) = 381 MB/s test vec::tests::bench_clone_from_0010_0000 ... bench: 12 ns/iter (+/- 1) test vec::tests::bench_clone_from_0010_0010 ... bench: 124 ns/iter (+/- 7) = 80 MB/s test vec::tests::bench_clone_from_0010_0100 ... bench: 369 ns/iter (+/- 34) = 271 MB/s test vec::tests::bench_clone_from_0100_0010 ... bench: 123 ns/iter (+/- 6) = 81 MB/s test vec::tests::bench_clone_from_0100_0100 ... bench: 371 ns/iter (+/- 25) = 269 MB/s test vec::tests::bench_clone_from_0100_1000 ... bench: 2713 ns/iter (+/- 532) = 368 MB/s test vec::tests::bench_clone_from_1000_0100 ... bench: 369 ns/iter (+/- 14) = 271 MB/s test vec::tests::bench_clone_from_1000_1000 ... bench: 2611 ns/iter (+/- 194) = 382 MB/s test vec::tests::bench_from_slice_0000 ... bench: 7 ns/iter (+/- 0) test vec::tests::bench_from_slice_0010 ... bench: 108 ns/iter (+/- 4) = 92 MB/s test vec::tests::bench_from_slice_0100 ... bench: 235 ns/iter (+/- 24) = 425 MB/s test vec::tests::bench_from_slice_1000 ... bench: 1318 ns/iter (+/- 96) = 758 MB/s test vec::tests::bench_push_all_0000_0000 ... bench: 7 ns/iter (+/- 0) test vec::tests::bench_push_all_0000_0010 ... bench: 70 ns/iter (+/- 4) = 142 MB/s test vec::tests::bench_push_all_0000_0100 ... bench: 176 ns/iter (+/- 16) = 568 MB/s test vec::tests::bench_push_all_0000_1000 ... bench: 1125 ns/iter (+/- 94) = 888 MB/s test vec::tests::bench_push_all_0010_0010 ... bench: 159 ns/iter (+/- 15) = 62 MB/s test vec::tests::bench_push_all_0100_0100 ... bench: 363 ns/iter (+/- 12) = 275 MB/s test vec::tests::bench_push_all_1000_1000 ... bench: 2860 ns/iter (+/- 415) = 349 MB/s --- src/libcollections/vec.rs | 75 +++++++++++++++++++++++---------------- 1 file changed, 44 insertions(+), 31 deletions(-) diff --git a/src/libcollections/vec.rs b/src/libcollections/vec.rs index e10fc66ea8567..4dd283dbefa05 100644 --- a/src/libcollections/vec.rs +++ b/src/libcollections/vec.rs @@ -197,7 +197,9 @@ impl Vec { /// ``` #[inline] pub fn from_slice(values: &[T]) -> Vec { - values.iter().map(|x| x.clone()).collect() + let mut vector = Vec::with_capacity(values.len()); + vector.push_all(values); + vector } /// Constructs a `Vec` with copies of a value. @@ -238,7 +240,10 @@ impl Vec { /// ``` #[inline] pub fn push_all(&mut self, other: &[T]) { - self.extend(other.iter().map(|e| e.clone())); + unsafe { + self.reserve_additional(other.len()); + unsafe_push_all_clone(self, other) + } } /// Grows the `Vec` in-place. @@ -318,41 +323,31 @@ impl Vec { #[unstable] impl Clone for Vec { fn clone(&self) -> Vec { - let len = self.len; - let mut vector = Vec::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 - { - let this_slice = self.as_slice(); - while vector.len < len { - unsafe { - let len = vector.len; - ptr::write( - vector.as_mut_slice().unsafe_mut_ref(len), - this_slice.unsafe_ref(len).clone()); - } - vector.len += 1; - } + unsafe { + let mut vector = Vec::with_capacity(self.len); + unsafe_push_all_clone(&mut vector, self.as_slice()); + vector } - vector } fn clone_from(&mut self, other: &Vec) { - // drop anything in self that will not be overwritten - if self.len() > other.len() { - self.truncate(other.len()) - } + unsafe { + // drop anything in self that will not be overwritten + if self.len() > other.len() { + self.truncate(other.len()) + } - // reuse the contained values' allocations/resources. - for (place, thing) in self.mut_iter().zip(other.iter()) { - place.clone_from(thing) - } + // reuse the contained values' allocations/resources. + for (place, thing) in self.mut_iter().zip(other.iter()) { + place.clone_from(thing) + } - // self.len <= other.len due to the truncate above, so the - // slice here is always in-bounds. - let len = self.len(); - self.extend(other.slice_from(len).iter().map(|x| x.clone())); + // self.len <= other.len due to the truncate above, so the + // slice here is always in-bounds. + let slice = other.slice_from(self.len()); + self.reserve_additional(slice.len()); + unsafe_push_all_clone(self, slice) + } } } @@ -1555,6 +1550,24 @@ pub mod raw { } } +// 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. +// +// WARNING: You must preallocate space on the vector before you call this +// method. +#[inline(always)] +unsafe fn unsafe_push_all_clone(dst: &mut Vec, src: &[T]) { + let mut dst_len = dst.len(); + + for i in range(0, src.len()) { + ptr::write( + dst.as_mut_slice().unsafe_mut_ref(dst_len), + src.unsafe_ref(i).clone()); + dst_len += 1; + dst.set_len(dst_len); + } +} #[cfg(test)] mod tests { From 90fe1a632bd38633006ea53f92a1da14944fd835 Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Sat, 5 Jul 2014 22:31:57 -0700 Subject: [PATCH 3/6] std: flesh out MemWriter benchmarks --- src/libstd/io/mem.rs | 54 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 49 insertions(+), 5 deletions(-) diff --git a/src/libstd/io/mem.rs b/src/libstd/io/mem.rs index 3443a85b46819..e2500fd00bec5 100644 --- a/src/libstd/io/mem.rs +++ b/src/libstd/io/mem.rs @@ -607,17 +607,61 @@ mod test { assert_eq!(buf.as_slice(), &[7, 8, 6]); } - #[bench] - fn bench_mem_writer(b: &mut Bencher) { + fn do_bench_mem_writer(b: &mut Bencher, times: uint, len: uint) { + let src: Vec = Vec::from_elem(len, 5); + b.iter(|| { let mut wr = MemWriter::new(); - for _i in range(0u, 10) { - wr.write([5, .. 10]).unwrap(); + for _ in range(0, times) { + wr.write(src.as_slice()).unwrap(); } - assert_eq!(wr.unwrap().as_slice(), [5, .. 100].as_slice()); + + let v = wr.unwrap(); + assert_eq!(v.len(), times * len); + assert!(v.iter().all(|x| *x == 5)); }); } + #[bench] + fn bench_mem_writer_001_0000(b: &mut Bencher) { + do_bench_mem_writer(b, 1, 0) + } + + #[bench] + fn bench_mem_writer_001_0010(b: &mut Bencher) { + do_bench_mem_writer(b, 1, 10) + } + + #[bench] + fn bench_mem_writer_001_0100(b: &mut Bencher) { + do_bench_mem_writer(b, 1, 100) + } + + #[bench] + fn bench_mem_writer_001_1000(b: &mut Bencher) { + do_bench_mem_writer(b, 1, 1000) + } + + #[bench] + fn bench_mem_writer_100_0000(b: &mut Bencher) { + do_bench_mem_writer(b, 100, 0) + } + + #[bench] + fn bench_mem_writer_100_0010(b: &mut Bencher) { + do_bench_mem_writer(b, 100, 10) + } + + #[bench] + fn bench_mem_writer_100_0100(b: &mut Bencher) { + do_bench_mem_writer(b, 100, 100) + } + + #[bench] + fn bench_mem_writer_100_1000(b: &mut Bencher) { + do_bench_mem_writer(b, 100, 1000) + } + #[bench] fn bench_mem_reader(b: &mut Bencher) { b.iter(|| { From 7d3899430b71d3ebed6b3fbf6ec47b042f513979 Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Mon, 7 Jul 2014 09:07:49 -0700 Subject: [PATCH 4/6] collections: flesh out the Vec::clone_from benchmarks to cover reuse --- src/libcollections/vec.rs | 123 ++++++++++++++++++++++++++++---------- 1 file changed, 93 insertions(+), 30 deletions(-) diff --git a/src/libcollections/vec.rs b/src/libcollections/vec.rs index 4dd283dbefa05..ea7442b1f3455 100644 --- a/src/libcollections/vec.rs +++ b/src/libcollections/vec.rs @@ -2209,78 +2209,141 @@ mod tests { do_bench_clone(b, 1000) } - fn do_bench_clone_from(b: &mut Bencher, dst_len: uint, src_len: uint) { + fn do_bench_clone_from(b: &mut Bencher, times: uint, dst_len: uint, src_len: uint) { let dst: Vec = FromIterator::from_iter(range(0, src_len)); let src: Vec = FromIterator::from_iter(range(dst_len, dst_len + src_len)); - b.bytes = src_len as u64; + b.bytes = (times * src_len) as u64; b.iter(|| { let mut dst = dst.clone(); - let src = src.clone(); - dst.clone_from(&src); - assert_eq!(dst.len(), src_len); - assert!(dst.iter().enumerate().all(|(i, x)| dst_len + i == *x)); + + for _ in range(0, times) { + dst.clone_from(&src); + + assert_eq!(dst.len(), src_len); + assert!(dst.iter().enumerate().all(|(i, x)| dst_len + i == *x)); + } }); } #[bench] - fn bench_clone_from_0000_0000(b: &mut Bencher) { - do_bench_clone_from(b, 0, 0) + fn bench_clone_from_01_0000_0000(b: &mut Bencher) { + do_bench_clone_from(b, 1, 0, 0) + } + + #[bench] + fn bench_clone_from_01_0000_0010(b: &mut Bencher) { + do_bench_clone_from(b, 1, 0, 10) + } + + #[bench] + fn bench_clone_from_01_0000_0100(b: &mut Bencher) { + do_bench_clone_from(b, 1, 0, 100) + } + + #[bench] + fn bench_clone_from_01_0000_1000(b: &mut Bencher) { + do_bench_clone_from(b, 1, 0, 1000) + } + + #[bench] + fn bench_clone_from_01_0010_0010(b: &mut Bencher) { + do_bench_clone_from(b, 1, 10, 10) + } + + #[bench] + fn bench_clone_from_01_0100_0100(b: &mut Bencher) { + do_bench_clone_from(b, 1, 100, 100) + } + + #[bench] + fn bench_clone_from_01_1000_1000(b: &mut Bencher) { + do_bench_clone_from(b, 1, 1000, 1000) + } + + #[bench] + fn bench_clone_from_01_0010_0100(b: &mut Bencher) { + do_bench_clone_from(b, 1, 10, 100) + } + + #[bench] + fn bench_clone_from_01_0100_1000(b: &mut Bencher) { + do_bench_clone_from(b, 1, 100, 1000) + } + + #[bench] + fn bench_clone_from_01_0010_0000(b: &mut Bencher) { + do_bench_clone_from(b, 1, 10, 0) + } + + #[bench] + fn bench_clone_from_01_0100_0010(b: &mut Bencher) { + do_bench_clone_from(b, 1, 100, 10) + } + + #[bench] + fn bench_clone_from_01_1000_0100(b: &mut Bencher) { + do_bench_clone_from(b, 1, 1000, 100) + } + + #[bench] + fn bench_clone_from_10_0000_0000(b: &mut Bencher) { + do_bench_clone_from(b, 10, 0, 0) } #[bench] - fn bench_clone_from_0000_0010(b: &mut Bencher) { - do_bench_clone_from(b, 0, 10) + fn bench_clone_from_10_0000_0010(b: &mut Bencher) { + do_bench_clone_from(b, 10, 0, 10) } #[bench] - fn bench_clone_from_0000_0100(b: &mut Bencher) { - do_bench_clone_from(b, 0, 100) + fn bench_clone_from_10_0000_0100(b: &mut Bencher) { + do_bench_clone_from(b, 10, 0, 100) } #[bench] - fn bench_clone_from_0000_1000(b: &mut Bencher) { - do_bench_clone_from(b, 0, 1000) + fn bench_clone_from_10_0000_1000(b: &mut Bencher) { + do_bench_clone_from(b, 10, 0, 1000) } #[bench] - fn bench_clone_from_0010_0010(b: &mut Bencher) { - do_bench_clone_from(b, 10, 10) + fn bench_clone_from_10_0010_0010(b: &mut Bencher) { + do_bench_clone_from(b, 10, 10, 10) } #[bench] - fn bench_clone_from_0100_0100(b: &mut Bencher) { - do_bench_clone_from(b, 100, 100) + fn bench_clone_from_10_0100_0100(b: &mut Bencher) { + do_bench_clone_from(b, 10, 100, 100) } #[bench] - fn bench_clone_from_1000_1000(b: &mut Bencher) { - do_bench_clone_from(b, 1000, 1000) + fn bench_clone_from_10_1000_1000(b: &mut Bencher) { + do_bench_clone_from(b, 10, 1000, 1000) } #[bench] - fn bench_clone_from_0010_0100(b: &mut Bencher) { - do_bench_clone_from(b, 10, 100) + fn bench_clone_from_10_0010_0100(b: &mut Bencher) { + do_bench_clone_from(b, 10, 10, 100) } #[bench] - fn bench_clone_from_0100_1000(b: &mut Bencher) { - do_bench_clone_from(b, 100, 1000) + fn bench_clone_from_10_0100_1000(b: &mut Bencher) { + do_bench_clone_from(b, 10, 100, 1000) } #[bench] - fn bench_clone_from_0010_0000(b: &mut Bencher) { - do_bench_clone_from(b, 10, 0) + fn bench_clone_from_10_0010_0000(b: &mut Bencher) { + do_bench_clone_from(b, 10, 10, 0) } #[bench] - fn bench_clone_from_0100_0010(b: &mut Bencher) { - do_bench_clone_from(b, 100, 10) + fn bench_clone_from_10_0100_0010(b: &mut Bencher) { + do_bench_clone_from(b, 10, 100, 10) } #[bench] - fn bench_clone_from_1000_0100(b: &mut Bencher) { - do_bench_clone_from(b, 1000, 100) + fn bench_clone_from_10_1000_0100(b: &mut Bencher) { + do_bench_clone_from(b, 10, 1000, 100) } } From e2d107c397acbc7b0d9677c36882616308346ed9 Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Mon, 7 Jul 2014 09:11:03 -0700 Subject: [PATCH 5/6] collections: minimize code that's in unsafe blocks This changes Vec::from_slice to call unsafe_push_all_clone directly to avoid doing an unnecessary reserve_additional call --- src/libcollections/vec.rs | 44 +++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/libcollections/vec.rs b/src/libcollections/vec.rs index ea7442b1f3455..3c38e6d1c2e8c 100644 --- a/src/libcollections/vec.rs +++ b/src/libcollections/vec.rs @@ -198,7 +198,13 @@ impl Vec { #[inline] pub fn from_slice(values: &[T]) -> Vec { let mut vector = Vec::with_capacity(values.len()); - vector.push_all(values); + + // Directly call `unsafe_push_all_clone` so we can skip a call to + // `reserve_addtional`. + unsafe { + unsafe_push_all_clone(&mut vector, values); + } + vector } @@ -240,8 +246,9 @@ impl Vec { /// ``` #[inline] pub fn push_all(&mut self, other: &[T]) { + self.reserve_additional(other.len()); + unsafe { - self.reserve_additional(other.len()); unsafe_push_all_clone(self, other) } } @@ -323,31 +330,24 @@ impl Vec { #[unstable] impl Clone for Vec { fn clone(&self) -> Vec { - unsafe { - let mut vector = Vec::with_capacity(self.len); - unsafe_push_all_clone(&mut vector, self.as_slice()); - vector - } + Vec::from_slice(self.as_slice()) } fn clone_from(&mut self, other: &Vec) { - unsafe { - // drop anything in self that will not be overwritten - if self.len() > other.len() { - self.truncate(other.len()) - } - - // reuse the contained values' allocations/resources. - for (place, thing) in self.mut_iter().zip(other.iter()) { - place.clone_from(thing) - } + // drop anything in self that will not be overwritten + if self.len() > other.len() { + self.truncate(other.len()) + } - // self.len <= other.len due to the truncate above, so the - // slice here is always in-bounds. - let slice = other.slice_from(self.len()); - self.reserve_additional(slice.len()); - unsafe_push_all_clone(self, slice) + // reuse the contained values' allocations/resources. + for (place, thing) in self.mut_iter().zip(other.iter()) { + place.clone_from(thing) } + + // self.len <= other.len due to the truncate above, so the + // slice here is always in-bounds. + let slice = other.slice_from(self.len()); + self.push_all(slice); } } From b4984a490b7fe181ec72b8fb0188bbd0ab4c15e0 Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Mon, 7 Jul 2014 09:45:00 -0700 Subject: [PATCH 6/6] collections: merge unsafe_push_all_clone and push_all --- src/libcollections/vec.rs | 43 +++++++++++++-------------------------- 1 file changed, 14 insertions(+), 29 deletions(-) diff --git a/src/libcollections/vec.rs b/src/libcollections/vec.rs index 3c38e6d1c2e8c..03d06253a5ec7 100644 --- a/src/libcollections/vec.rs +++ b/src/libcollections/vec.rs @@ -197,14 +197,8 @@ impl Vec { /// ``` #[inline] pub fn from_slice(values: &[T]) -> Vec { - let mut vector = Vec::with_capacity(values.len()); - - // Directly call `unsafe_push_all_clone` so we can skip a call to - // `reserve_addtional`. - unsafe { - unsafe_push_all_clone(&mut vector, values); - } - + let mut vector = Vec::new(); + vector.push_all(values); vector } @@ -248,8 +242,18 @@ impl Vec { pub fn push_all(&mut self, other: &[T]) { self.reserve_additional(other.len()); - unsafe { - unsafe_push_all_clone(self, other) + for i in range(0, other.len()) { + let len = self.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 { + ptr::write( + self.as_mut_slice().unsafe_mut_ref(len), + other.unsafe_ref(i).clone()); + self.set_len(len + 1); + } } } @@ -1550,25 +1554,6 @@ pub mod raw { } } -// 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. -// -// WARNING: You must preallocate space on the vector before you call this -// method. -#[inline(always)] -unsafe fn unsafe_push_all_clone(dst: &mut Vec, src: &[T]) { - let mut dst_len = dst.len(); - - for i in range(0, src.len()) { - ptr::write( - dst.as_mut_slice().unsafe_mut_ref(dst_len), - src.unsafe_ref(i).clone()); - dst_len += 1; - dst.set_len(dst_len); - } -} - #[cfg(test)] mod tests { extern crate test;