Skip to content

Commit 0936b3d

Browse files
Auto merge of #137122 - yotamofek:pr/std/iter-eq-exact-size, r=<try>
Specialize `Iterator::eq{_by}` for `TrustedLen` iterators <!-- homu-ignore:start --> <!-- If this PR is related to an unstable feature or an otherwise tracked effort, please link to the relevant tracking issue here. If you don't know of a related tracking issue or there are none, feel free to ignore this. This PR will get automatically assigned to a reviewer. In case you would like a specific user to review your work, you can assign it to them by using r? <reviewer name> --> <!-- homu-ignore:end --> 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 b03b3a7 + d9f58d4 commit 0936b3d

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;
@@ -3816,7 +3817,7 @@ pub trait Iterator {
38163817
}
38173818
}
38183819

3819-
match iter_compare(self, other.into_iter(), compare(eq)) {
3820+
match SpecIterCompare::spec_iter_compare(self, other.into_iter(), compare(eq)) {
38203821
ControlFlow::Continue(ord) => ord == Ordering::Equal,
38213822
ControlFlow::Break(()) => false,
38223823
}
@@ -4038,6 +4039,41 @@ pub trait Iterator {
40384039
}
40394040
}
40404041

4042+
trait SpecIterCompare<B: Iterator>: Iterator {
4043+
fn spec_iter_compare<F, T>(self, b: B, f: F) -> ControlFlow<T, Ordering>
4044+
where
4045+
F: FnMut(Self::Item, B::Item) -> ControlFlow<T>;
4046+
}
4047+
4048+
impl<A: Iterator, B: Iterator> SpecIterCompare<B> for A {
4049+
#[inline]
4050+
default fn spec_iter_compare<F, T>(self, b: B, f: F) -> ControlFlow<T, Ordering>
4051+
where
4052+
F: FnMut(A::Item, <B as Iterator>::Item) -> ControlFlow<T>,
4053+
{
4054+
iter_compare(self, b, f)
4055+
}
4056+
}
4057+
4058+
impl<A: Iterator + TrustedLen, B: Iterator + TrustedLen> SpecIterCompare<B> for A {
4059+
#[inline]
4060+
fn spec_iter_compare<F, T>(self, b: B, f: F) -> ControlFlow<T, Ordering>
4061+
where
4062+
F: FnMut(Self::Item, <B as Iterator>::Item) -> ControlFlow<T>,
4063+
{
4064+
if let (_, Some(a)) = self.size_hint()
4065+
&& let (_, Some(b)) = b.size_hint()
4066+
{
4067+
let ord = a.cmp(&b);
4068+
if ord != Ordering::Equal {
4069+
return ControlFlow::Continue(ord);
4070+
}
4071+
}
4072+
4073+
iter_compare(self, b, f)
4074+
}
4075+
}
4076+
40414077
/// Compares two iterators element-wise using the given function.
40424078
///
40434079
/// If `ControlFlow::Continue(())` is returned from the function, the comparison moves on to the next

0 commit comments

Comments
 (0)