Skip to content

Commit 0696895

Browse files
committed
Auto merge of rust-lang#100845 - timvermeulen:iter_compare, r=scottmcm
Use internal iteration in `Iterator` comparison methods Updates the `Iterator` methods `cmp_by`, `partial_cmp_by`, and `eq_by` to use internal iteration on `self`. I've also extracted their shared logic into a private helper function `iter_compare`, which will either short-circuit once the comparison result is known or return the comparison of the lengths of the iterators. This change also indirectly benefits calls to `cmp`, `partial_cmp`, `eq`, `lt`, `le`, `gt`, and `ge`. Unsurprising benchmark results: iterators that benefit from internal iteration (like `Chain`) see a speedup, while other iterators are unaffected. ``` name before ns/iter after ns/iter diff ns/iter diff % speedup iter::bench_chain_partial_cmp 208,301 54,978 -153,323 -73.61% x 3.79 iter::bench_partial_cmp 55,527 55,702 175 0.32% x 1.00 iter::bench_lt 55,502 55,322 -180 -0.32% x 1.00 ```
2 parents e1c28e0 + db2b4a3 commit 0696895

File tree

2 files changed

+88
-62
lines changed

2 files changed

+88
-62
lines changed

library/core/benches/iter.rs

+7
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,13 @@ fn bench_partial_cmp(b: &mut Bencher) {
363363
b.iter(|| (0..100000).map(black_box).partial_cmp((0..100000).map(black_box)))
364364
}
365365

366+
#[bench]
367+
fn bench_chain_partial_cmp(b: &mut Bencher) {
368+
b.iter(|| {
369+
(0..50000).chain(50000..100000).map(black_box).partial_cmp((0..100000).map(black_box))
370+
})
371+
}
372+
366373
#[bench]
367374
fn bench_lt(b: &mut Bencher) {
368375
b.iter(|| (0..100000).map(black_box).lt((0..100000).map(black_box)))

library/core/src/iter/traits/iterator.rs

+81-62
Original file line numberDiff line numberDiff line change
@@ -3461,36 +3461,27 @@ pub trait Iterator {
34613461
/// assert_eq!(xs.iter().cmp_by(&ys, |&x, &y| (2 * x).cmp(&y)), Ordering::Greater);
34623462
/// ```
34633463
#[unstable(feature = "iter_order_by", issue = "64295")]
3464-
fn cmp_by<I, F>(mut self, other: I, mut cmp: F) -> Ordering
3464+
fn cmp_by<I, F>(self, other: I, cmp: F) -> Ordering
34653465
where
34663466
Self: Sized,
34673467
I: IntoIterator,
34683468
F: FnMut(Self::Item, I::Item) -> Ordering,
34693469
{
3470-
let mut other = other.into_iter();
3471-
3472-
loop {
3473-
let x = match self.next() {
3474-
None => {
3475-
if other.next().is_none() {
3476-
return Ordering::Equal;
3477-
} else {
3478-
return Ordering::Less;
3479-
}
3480-
}
3481-
Some(val) => val,
3482-
};
3483-
3484-
let y = match other.next() {
3485-
None => return Ordering::Greater,
3486-
Some(val) => val,
3487-
};
3488-
3489-
match cmp(x, y) {
3490-
Ordering::Equal => (),
3491-
non_eq => return non_eq,
3470+
#[inline]
3471+
fn compare<X, Y, F>(mut cmp: F) -> impl FnMut(X, Y) -> ControlFlow<Ordering>
3472+
where
3473+
F: FnMut(X, Y) -> Ordering,
3474+
{
3475+
move |x, y| match cmp(x, y) {
3476+
Ordering::Equal => ControlFlow::CONTINUE,
3477+
non_eq => ControlFlow::Break(non_eq),
34923478
}
34933479
}
3480+
3481+
match iter_compare(self, other.into_iter(), compare(cmp)) {
3482+
ControlFlow::Continue(ord) => ord,
3483+
ControlFlow::Break(ord) => ord,
3484+
}
34943485
}
34953486

34963487
/// [Lexicographically](Ord#lexicographical-comparison) compares the elements of this [`Iterator`] with those
@@ -3546,36 +3537,27 @@ pub trait Iterator {
35463537
/// );
35473538
/// ```
35483539
#[unstable(feature = "iter_order_by", issue = "64295")]
3549-
fn partial_cmp_by<I, F>(mut self, other: I, mut partial_cmp: F) -> Option<Ordering>
3540+
fn partial_cmp_by<I, F>(self, other: I, partial_cmp: F) -> Option<Ordering>
35503541
where
35513542
Self: Sized,
35523543
I: IntoIterator,
35533544
F: FnMut(Self::Item, I::Item) -> Option<Ordering>,
35543545
{
3555-
let mut other = other.into_iter();
3556-
3557-
loop {
3558-
let x = match self.next() {
3559-
None => {
3560-
if other.next().is_none() {
3561-
return Some(Ordering::Equal);
3562-
} else {
3563-
return Some(Ordering::Less);
3564-
}
3565-
}
3566-
Some(val) => val,
3567-
};
3568-
3569-
let y = match other.next() {
3570-
None => return Some(Ordering::Greater),
3571-
Some(val) => val,
3572-
};
3573-
3574-
match partial_cmp(x, y) {
3575-
Some(Ordering::Equal) => (),
3576-
non_eq => return non_eq,
3546+
#[inline]
3547+
fn compare<X, Y, F>(mut partial_cmp: F) -> impl FnMut(X, Y) -> ControlFlow<Option<Ordering>>
3548+
where
3549+
F: FnMut(X, Y) -> Option<Ordering>,
3550+
{
3551+
move |x, y| match partial_cmp(x, y) {
3552+
Some(Ordering::Equal) => ControlFlow::CONTINUE,
3553+
non_eq => ControlFlow::Break(non_eq),
35773554
}
35783555
}
3556+
3557+
match iter_compare(self, other.into_iter(), compare(partial_cmp)) {
3558+
ControlFlow::Continue(ord) => Some(ord),
3559+
ControlFlow::Break(ord) => ord,
3560+
}
35793561
}
35803562

35813563
/// Determines if the elements of this [`Iterator`] are equal to those of
@@ -3613,29 +3595,26 @@ pub trait Iterator {
36133595
/// assert!(xs.iter().eq_by(&ys, |&x, &y| x * x == y));
36143596
/// ```
36153597
#[unstable(feature = "iter_order_by", issue = "64295")]
3616-
fn eq_by<I, F>(mut self, other: I, mut eq: F) -> bool
3598+
fn eq_by<I, F>(self, other: I, eq: F) -> bool
36173599
where
36183600
Self: Sized,
36193601
I: IntoIterator,
36203602
F: FnMut(Self::Item, I::Item) -> bool,
36213603
{
3622-
let mut other = other.into_iter();
3623-
3624-
loop {
3625-
let x = match self.next() {
3626-
None => return other.next().is_none(),
3627-
Some(val) => val,
3628-
};
3629-
3630-
let y = match other.next() {
3631-
None => return false,
3632-
Some(val) => val,
3633-
};
3634-
3635-
if !eq(x, y) {
3636-
return false;
3604+
#[inline]
3605+
fn compare<X, Y, F>(mut eq: F) -> impl FnMut(X, Y) -> ControlFlow<()>
3606+
where
3607+
F: FnMut(X, Y) -> bool,
3608+
{
3609+
move |x, y| {
3610+
if eq(x, y) { ControlFlow::CONTINUE } else { ControlFlow::BREAK }
36373611
}
36383612
}
3613+
3614+
match iter_compare(self, other.into_iter(), compare(eq)) {
3615+
ControlFlow::Continue(ord) => ord == Ordering::Equal,
3616+
ControlFlow::Break(()) => false,
3617+
}
36393618
}
36403619

36413620
/// Determines if the elements of this [`Iterator`] are unequal to those of
@@ -3860,6 +3839,46 @@ pub trait Iterator {
38603839
}
38613840
}
38623841

3842+
/// Compares two iterators element-wise using the given function.
3843+
///
3844+
/// If `ControlFlow::CONTINUE` is returned from the function, the comparison moves on to the next
3845+
/// elements of both iterators. Returning `ControlFlow::Break(x)` short-circuits the iteration and
3846+
/// returns `ControlFlow::Break(x)`. If one of the iterators runs out of elements,
3847+
/// `ControlFlow::Continue(ord)` is returned where `ord` is the result of comparing the lengths of
3848+
/// the iterators.
3849+
///
3850+
/// Isolates the logic shared by ['cmp_by'](Iterator::cmp_by),
3851+
/// ['partial_cmp_by'](Iterator::partial_cmp_by), and ['eq_by'](Iterator::eq_by).
3852+
#[inline]
3853+
fn iter_compare<A, B, F, T>(mut a: A, mut b: B, f: F) -> ControlFlow<T, Ordering>
3854+
where
3855+
A: Iterator,
3856+
B: Iterator,
3857+
F: FnMut(A::Item, B::Item) -> ControlFlow<T>,
3858+
{
3859+
#[inline]
3860+
fn compare<'a, B, X, T>(
3861+
b: &'a mut B,
3862+
mut f: impl FnMut(X, B::Item) -> ControlFlow<T> + 'a,
3863+
) -> impl FnMut(X) -> ControlFlow<ControlFlow<T, Ordering>> + 'a
3864+
where
3865+
B: Iterator,
3866+
{
3867+
move |x| match b.next() {
3868+
None => ControlFlow::Break(ControlFlow::Continue(Ordering::Greater)),
3869+
Some(y) => f(x, y).map_break(ControlFlow::Break),
3870+
}
3871+
}
3872+
3873+
match a.try_for_each(compare(&mut b, f)) {
3874+
ControlFlow::Continue(()) => ControlFlow::Continue(match b.next() {
3875+
None => Ordering::Equal,
3876+
Some(_) => Ordering::Less,
3877+
}),
3878+
ControlFlow::Break(x) => x,
3879+
}
3880+
}
3881+
38633882
#[stable(feature = "rust1", since = "1.0.0")]
38643883
impl<I: Iterator + ?Sized> Iterator for &mut I {
38653884
type Item = I::Item;

0 commit comments

Comments
 (0)