Skip to content

Commit 82ee190

Browse files
committed
unroll slice::equal impl
1 parent 2624523 commit 82ee190

File tree

1 file changed

+41
-1
lines changed

1 file changed

+41
-1
lines changed

library/core/src/slice/cmp.rs

+41-1
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,50 @@ where
6060
return false;
6161
}
6262

63-
self.iter().zip(other.iter()).all(|(x, y)| x == y)
63+
// at least 8 items for unrolling to make sense (4 peeled + 4+ unrolled)
64+
if self.len() < 8 {
65+
return eq_small(self, other);
66+
}
67+
68+
eq_unroll(self, other)
6469
}
6570
}
6671

72+
#[inline]
73+
fn eq_small<A, B>(a: &[A], b: &[B]) -> bool
74+
where
75+
A: PartialEq<B>,
76+
{
77+
a.iter().zip(b).all(|(a, b)| a == b)
78+
}
79+
80+
fn eq_unroll<A, B>(a: &[A], b: &[B]) -> bool
81+
where
82+
A: PartialEq<B>,
83+
{
84+
let (mut chunks_a, residual_a) = a.as_chunks::<4>();
85+
let (mut chunks_b, residual_b) = b.as_chunks::<4>();
86+
let peeled_a = chunks_a.take_first().unwrap();
87+
let peeled_b = chunks_b.take_first().unwrap();
88+
89+
// peel the first chunk and do a short-circuiting comparison to bail early on mismatches
90+
// in case comparisons are expensive
91+
let mut result = eq_small(peeled_a, peeled_b);
92+
93+
// then check the residual, another chance to bail early
94+
result = result && eq_small(residual_a, residual_b);
95+
96+
// iter.all short-circuits which means the backend can't unroll the loop due to early exits.
97+
// So we unroll it manually.
98+
result = result
99+
&& chunks_a
100+
.iter()
101+
.zip(chunks_b)
102+
.all(|(a, b)| (a[0] == b[0]) & (a[1] == b[1]) & (a[2] == b[2]) & (a[3] == b[3]));
103+
104+
result
105+
}
106+
67107
// When each element can be compared byte-wise, we can compare all the bytes
68108
// from the whole size in one call to the intrinsics.
69109
impl<A, B> SlicePartialEq<B> for [A]

0 commit comments

Comments
 (0)