From 70d5a4600b21451aa98b447cd59384d86e2eadce Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Thu, 1 Mar 2018 01:57:25 -0800 Subject: [PATCH 1/3] Specialize Zip::nth for TrustedRandomAccess Makes the bench asked about on URLO 58x faster :) --- src/libcore/benches/iter.rs | 29 ++++++++++++++++++++++++++++ src/libcore/iter/mod.rs | 38 +++++++++++++++++++++++++++++++++++++ src/libcore/tests/iter.rs | 17 +++++++++++++++++ 3 files changed, 84 insertions(+) diff --git a/src/libcore/benches/iter.rs b/src/libcore/benches/iter.rs index b284d855c4515..6c597301ac204 100644 --- a/src/libcore/benches/iter.rs +++ b/src/libcore/benches/iter.rs @@ -281,3 +281,32 @@ bench_sums! { bench_take_while_chain_ref_sum, (0i64..1000000).chain(1000000..).take_while(|&x| x < 1111111) } + +// Checks whether Skip> is as fast as Zip, Skip>, from +// https://users.rust-lang.org/t/performance-difference-between-iterator-zip-and-skip-order/15743 +#[bench] +fn bench_zip_then_skip(b: &mut Bencher) { + let v: Vec<_> = (0..100_000).collect(); + let t: Vec<_> = (0..100_000).collect(); + + b.iter(|| { + let s = v.iter().zip(t.iter()).skip(10000) + .take_while(|t| *t.0 < 10100) + .map(|(a, b)| *a + *b) + .sum::(); + assert_eq!(s, 2009900); + }); +} +#[bench] +fn bench_skip_then_zip(b: &mut Bencher) { + let v: Vec<_> = (0..100_000).collect(); + let t: Vec<_> = (0..100_000).collect(); + + b.iter(|| { + let s = v.iter().skip(10000).zip(t.iter().skip(10000)) + .take_while(|t| *t.0 < 10100) + .map(|(a, b)| *a + *b) + .sum::(); + assert_eq!(s, 2009900); + }); +} diff --git a/src/libcore/iter/mod.rs b/src/libcore/iter/mod.rs index 623cad754dd72..533ff388b7604 100644 --- a/src/libcore/iter/mod.rs +++ b/src/libcore/iter/mod.rs @@ -1045,6 +1045,11 @@ impl Iterator for Zip where A: Iterator, B: Iterator fn size_hint(&self) -> (usize, Option) { ZipImpl::size_hint(self) } + + #[inline] + fn nth(&mut self, n: usize) -> Option { + ZipImpl::nth(self, n) + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -1065,6 +1070,14 @@ trait ZipImpl { fn new(a: A, b: B) -> Self; fn next(&mut self) -> Option; fn size_hint(&self) -> (usize, Option); + fn nth(&mut self, n: usize) -> Option; + fn super_nth(&mut self, mut n: usize) -> Option { + while let Some(x) = self.next() { + if n == 0 { return Some(x) } + n -= 1; + } + None + } fn next_back(&mut self) -> Option where A: DoubleEndedIterator + ExactSizeIterator, B: DoubleEndedIterator + ExactSizeIterator; @@ -1094,6 +1107,12 @@ impl ZipImpl for Zip }) } + #[inline] + default fn nth(&mut self, n: usize) -> Option + { + self.super_nth(n) + } + #[inline] default fn next_back(&mut self) -> Option<(A::Item, B::Item)> where A: DoubleEndedIterator + ExactSizeIterator, @@ -1174,6 +1193,25 @@ impl ZipImpl for Zip (len, Some(len)) } + #[inline] + fn nth(&mut self, n: usize) -> Option + { + let delta = cmp::min(n, self.len - self.index); + let end = self.index + delta; + while self.index < end { + let i = self.index; + self.index += 1; + if A::may_have_side_effect() { + unsafe { self.a.get_unchecked(i); } + } + if B::may_have_side_effect() { + unsafe { self.b.get_unchecked(i); } + } + } + + self.super_nth(n - delta) + } + #[inline] fn next_back(&mut self) -> Option<(A::Item, B::Item)> where A: DoubleEndedIterator + ExactSizeIterator, diff --git a/src/libcore/tests/iter.rs b/src/libcore/tests/iter.rs index edd75f7795ed7..5e2fef95d2606 100644 --- a/src/libcore/tests/iter.rs +++ b/src/libcore/tests/iter.rs @@ -144,6 +144,23 @@ fn test_iterator_chain_find() { assert_eq!(iter.next(), None); } +#[test] +fn test_zip_nth() { + let xs = [0, 1, 2, 4, 5]; + let ys = [10, 11, 12]; + + let mut it = xs.iter().zip(&ys); + assert_eq!(it.nth(0), Some((&0, &10))); + assert_eq!(it.nth(1), Some((&2, &12))); + assert_eq!(it.nth(0), None); + + let mut it = xs.iter().zip(&ys); + assert_eq!(it.nth(3), None); + + let mut it = ys.iter().zip(&xs); + assert_eq!(it.nth(3), None); +} + #[test] fn test_iterator_step_by() { // Identity From 11fefeb61c959c6a964120036992aed34f9d4282 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Thu, 1 Mar 2018 02:17:50 -0800 Subject: [PATCH 2/3] Add a Zip::nth test for side effects --- src/libcore/tests/iter.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/libcore/tests/iter.rs b/src/libcore/tests/iter.rs index 5e2fef95d2606..a962efadd64e9 100644 --- a/src/libcore/tests/iter.rs +++ b/src/libcore/tests/iter.rs @@ -161,6 +161,26 @@ fn test_zip_nth() { assert_eq!(it.nth(3), None); } +#[test] +fn test_zip_nth_side_effects() { + let mut a = Vec::new(); + let mut b = Vec::new(); + let value = [1, 2, 3, 4, 5, 6].iter().cloned() + .map(|n| { + a.push(n); + n * 10 + }) + .zip([2, 3, 4, 5, 6, 7, 8].iter().cloned().map(|n| { + b.push(n * 100); + n * 1000 + })) + .skip(1) + .nth(3); + assert_eq!(value, Some((50, 6000))); + assert_eq!(a, vec![1, 2, 3, 4, 5]); + assert_eq!(b, vec![200, 300, 400, 500, 600]); +} + #[test] fn test_iterator_step_by() { // Identity From 5105fc1681bb5be631850a6ae699dd97f929dd76 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Thu, 1 Mar 2018 02:29:46 -0800 Subject: [PATCH 3/3] Fix braces --- src/libcore/iter/mod.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/libcore/iter/mod.rs b/src/libcore/iter/mod.rs index 533ff388b7604..257d7d6caaaf8 100644 --- a/src/libcore/iter/mod.rs +++ b/src/libcore/iter/mod.rs @@ -1108,8 +1108,7 @@ impl ZipImpl for Zip } #[inline] - default fn nth(&mut self, n: usize) -> Option - { + default fn nth(&mut self, n: usize) -> Option { self.super_nth(n) } @@ -1194,8 +1193,7 @@ impl ZipImpl for Zip } #[inline] - fn nth(&mut self, n: usize) -> Option - { + fn nth(&mut self, n: usize) -> Option { let delta = cmp::min(n, self.len - self.index); let end = self.index + delta; while self.index < end {