Skip to content

Commit 3fa4bff

Browse files
committed
Rollup merge of rust-lang#48635 - scottmcm:faster-zip-nth, r=kennytm
Fixes rust-lang#47311. r? @nrc
2 parents 6fa14f0 + 5105fc1 commit 3fa4bff

File tree

3 files changed

+102
-0
lines changed

3 files changed

+102
-0
lines changed

src/libcore/benches/iter.rs

+29
Original file line numberDiff line numberDiff line change
@@ -281,3 +281,32 @@ bench_sums! {
281281
bench_take_while_chain_ref_sum,
282282
(0i64..1000000).chain(1000000..).take_while(|&x| x < 1111111)
283283
}
284+
285+
// Checks whether Skip<Zip<A,B>> is as fast as Zip<Skip<A>, Skip<B>>, from
286+
// https://users.rust-lang.org/t/performance-difference-between-iterator-zip-and-skip-order/15743
287+
#[bench]
288+
fn bench_zip_then_skip(b: &mut Bencher) {
289+
let v: Vec<_> = (0..100_000).collect();
290+
let t: Vec<_> = (0..100_000).collect();
291+
292+
b.iter(|| {
293+
let s = v.iter().zip(t.iter()).skip(10000)
294+
.take_while(|t| *t.0 < 10100)
295+
.map(|(a, b)| *a + *b)
296+
.sum::<u64>();
297+
assert_eq!(s, 2009900);
298+
});
299+
}
300+
#[bench]
301+
fn bench_skip_then_zip(b: &mut Bencher) {
302+
let v: Vec<_> = (0..100_000).collect();
303+
let t: Vec<_> = (0..100_000).collect();
304+
305+
b.iter(|| {
306+
let s = v.iter().skip(10000).zip(t.iter().skip(10000))
307+
.take_while(|t| *t.0 < 10100)
308+
.map(|(a, b)| *a + *b)
309+
.sum::<u64>();
310+
assert_eq!(s, 2009900);
311+
});
312+
}

src/libcore/iter/mod.rs

+36
Original file line numberDiff line numberDiff line change
@@ -1045,6 +1045,11 @@ impl<A, B> Iterator for Zip<A, B> where A: Iterator, B: Iterator
10451045
fn size_hint(&self) -> (usize, Option<usize>) {
10461046
ZipImpl::size_hint(self)
10471047
}
1048+
1049+
#[inline]
1050+
fn nth(&mut self, n: usize) -> Option<Self::Item> {
1051+
ZipImpl::nth(self, n)
1052+
}
10481053
}
10491054

10501055
#[stable(feature = "rust1", since = "1.0.0")]
@@ -1065,6 +1070,14 @@ trait ZipImpl<A, B> {
10651070
fn new(a: A, b: B) -> Self;
10661071
fn next(&mut self) -> Option<Self::Item>;
10671072
fn size_hint(&self) -> (usize, Option<usize>);
1073+
fn nth(&mut self, n: usize) -> Option<Self::Item>;
1074+
fn super_nth(&mut self, mut n: usize) -> Option<Self::Item> {
1075+
while let Some(x) = self.next() {
1076+
if n == 0 { return Some(x) }
1077+
n -= 1;
1078+
}
1079+
None
1080+
}
10681081
fn next_back(&mut self) -> Option<Self::Item>
10691082
where A: DoubleEndedIterator + ExactSizeIterator,
10701083
B: DoubleEndedIterator + ExactSizeIterator;
@@ -1094,6 +1107,11 @@ impl<A, B> ZipImpl<A, B> for Zip<A, B>
10941107
})
10951108
}
10961109

1110+
#[inline]
1111+
default fn nth(&mut self, n: usize) -> Option<Self::Item> {
1112+
self.super_nth(n)
1113+
}
1114+
10971115
#[inline]
10981116
default fn next_back(&mut self) -> Option<(A::Item, B::Item)>
10991117
where A: DoubleEndedIterator + ExactSizeIterator,
@@ -1174,6 +1192,24 @@ impl<A, B> ZipImpl<A, B> for Zip<A, B>
11741192
(len, Some(len))
11751193
}
11761194

1195+
#[inline]
1196+
fn nth(&mut self, n: usize) -> Option<Self::Item> {
1197+
let delta = cmp::min(n, self.len - self.index);
1198+
let end = self.index + delta;
1199+
while self.index < end {
1200+
let i = self.index;
1201+
self.index += 1;
1202+
if A::may_have_side_effect() {
1203+
unsafe { self.a.get_unchecked(i); }
1204+
}
1205+
if B::may_have_side_effect() {
1206+
unsafe { self.b.get_unchecked(i); }
1207+
}
1208+
}
1209+
1210+
self.super_nth(n - delta)
1211+
}
1212+
11771213
#[inline]
11781214
fn next_back(&mut self) -> Option<(A::Item, B::Item)>
11791215
where A: DoubleEndedIterator + ExactSizeIterator,

src/libcore/tests/iter.rs

+37
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,43 @@ fn test_iterator_chain_find() {
144144
assert_eq!(iter.next(), None);
145145
}
146146

147+
#[test]
148+
fn test_zip_nth() {
149+
let xs = [0, 1, 2, 4, 5];
150+
let ys = [10, 11, 12];
151+
152+
let mut it = xs.iter().zip(&ys);
153+
assert_eq!(it.nth(0), Some((&0, &10)));
154+
assert_eq!(it.nth(1), Some((&2, &12)));
155+
assert_eq!(it.nth(0), None);
156+
157+
let mut it = xs.iter().zip(&ys);
158+
assert_eq!(it.nth(3), None);
159+
160+
let mut it = ys.iter().zip(&xs);
161+
assert_eq!(it.nth(3), None);
162+
}
163+
164+
#[test]
165+
fn test_zip_nth_side_effects() {
166+
let mut a = Vec::new();
167+
let mut b = Vec::new();
168+
let value = [1, 2, 3, 4, 5, 6].iter().cloned()
169+
.map(|n| {
170+
a.push(n);
171+
n * 10
172+
})
173+
.zip([2, 3, 4, 5, 6, 7, 8].iter().cloned().map(|n| {
174+
b.push(n * 100);
175+
n * 1000
176+
}))
177+
.skip(1)
178+
.nth(3);
179+
assert_eq!(value, Some((50, 6000)));
180+
assert_eq!(a, vec![1, 2, 3, 4, 5]);
181+
assert_eq!(b, vec![200, 300, 400, 500, 600]);
182+
}
183+
147184
#[test]
148185
fn test_iterator_step_by() {
149186
// Identity

0 commit comments

Comments
 (0)