Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 61b77a2

Browse files
committed
Auto merge of rust-lang#137122 - yotamofek:pr/std/iter-eq-exact-size, r=<try>
Specialize `Iterator::eq{_by}` for `TrustedLen` iterators I'm sure I got some stuff wrong here, but opening this to get feedback and make sure it's a viable idea at all. ### Motivation I had a piece of code that open-coded `Iterator::eq`, something like: ```rust if current.len() != other.len() || current.iter().zip(other.iter()).any(|(a, b)| a != b) { ... } ``` ... where both `current` and `other` are slices of the same type. Changing the code to use `current.iter().eq(other)` made it a lot slower, since it wasn't checking the length of the two slices beforehand anymore, which in this instance made a big difference in perf. So I thought I'd see if I can improve `Iterator::eq`. ### Questions 1. I can't specialize for `ExactSizeIterator`, I think it's a limitation of `min_specialization` but not sure exactly why. Is specializing for `TrustedLen` good enough? 2. Should I make a codegen test for this? If so, then how? (I manually checked the assembly to make sure it works as expected) 3. Where should I put `SpecIterCompare`? 4. Can I get a perf run for this, please? I think the compiler uses this in a few places, so it might have an affect.
2 parents ce36a96 + d9f58d4 commit 61b77a2

File tree

1 file changed

+37
-1
lines changed

1 file changed

+37
-1
lines changed

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

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use super::super::{
44
Product, Rev, Scan, Skip, SkipWhile, StepBy, Sum, Take, TakeWhile, TrustedRandomAccessNoCoerce,
55
Zip, try_process,
66
};
7+
use super::TrustedLen;
78
use crate::array;
89
use crate::cmp::{self, Ordering};
910
use crate::num::NonZero;
@@ -3762,7 +3763,7 @@ pub trait Iterator {
37623763
}
37633764
}
37643765

3765-
match iter_compare(self, other.into_iter(), compare(eq)) {
3766+
match SpecIterCompare::spec_iter_compare(self, other.into_iter(), compare(eq)) {
37663767
ControlFlow::Continue(ord) => ord == Ordering::Equal,
37673768
ControlFlow::Break(()) => false,
37683769
}
@@ -3984,6 +3985,41 @@ pub trait Iterator {
39843985
}
39853986
}
39863987

3988+
trait SpecIterCompare<B: Iterator>: Iterator {
3989+
fn spec_iter_compare<F, T>(self, b: B, f: F) -> ControlFlow<T, Ordering>
3990+
where
3991+
F: FnMut(Self::Item, B::Item) -> ControlFlow<T>;
3992+
}
3993+
3994+
impl<A: Iterator, B: Iterator> SpecIterCompare<B> for A {
3995+
#[inline]
3996+
default fn spec_iter_compare<F, T>(self, b: B, f: F) -> ControlFlow<T, Ordering>
3997+
where
3998+
F: FnMut(A::Item, <B as Iterator>::Item) -> ControlFlow<T>,
3999+
{
4000+
iter_compare(self, b, f)
4001+
}
4002+
}
4003+
4004+
impl<A: Iterator + TrustedLen, B: Iterator + TrustedLen> SpecIterCompare<B> for A {
4005+
#[inline]
4006+
fn spec_iter_compare<F, T>(self, b: B, f: F) -> ControlFlow<T, Ordering>
4007+
where
4008+
F: FnMut(Self::Item, <B as Iterator>::Item) -> ControlFlow<T>,
4009+
{
4010+
if let (_, Some(a)) = self.size_hint()
4011+
&& let (_, Some(b)) = b.size_hint()
4012+
{
4013+
let ord = a.cmp(&b);
4014+
if ord != Ordering::Equal {
4015+
return ControlFlow::Continue(ord);
4016+
}
4017+
}
4018+
4019+
iter_compare(self, b, f)
4020+
}
4021+
}
4022+
39874023
/// Compares two iterators element-wise using the given function.
39884024
///
39894025
/// If `ControlFlow::Continue(())` is returned from the function, the comparison moves on to the next

0 commit comments

Comments
 (0)