Skip to content

Commit 1cf5d79

Browse files
committed
Auto merge of rust-lang#116422 - the8472:chunked-generic-slice-eq, r=<try>
Chunked generic slice eq looks nice in a microbenchmark, let's see if perf agrees ``` OLD: slice::slice_cmp_generic 54.00ns/iter +/- 1.00ns NEW: slice::slice_cmp_generic 20.00ns/iter +/- 2.00ns ```
2 parents 598e29b + 5850f36 commit 1cf5d79

File tree

2 files changed

+59
-1
lines changed

2 files changed

+59
-1
lines changed

library/core/benches/slice.rs

+14
Original file line numberDiff line numberDiff line change
@@ -171,3 +171,17 @@ fn fold_to_last(b: &mut Bencher) {
171171
let slice: &[i32] = &[0; 1024];
172172
b.iter(|| black_box(slice).iter().fold(None, |_, r| Some(NonNull::from(r))));
173173
}
174+
175+
#[bench]
176+
fn slice_cmp_generic(b: &mut Bencher) {
177+
#[derive(PartialEq, Clone, Copy)]
178+
struct Foo(u32, u32);
179+
180+
let left = [Foo(128, 128); 128];
181+
let right = [Foo(128, 128); 128];
182+
183+
b.iter(|| {
184+
let (left, right) = (black_box(&left), black_box(&right));
185+
left.as_slice() == right.as_slice()
186+
});
187+
}

library/core/src/slice/cmp.rs

+45-1
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,51 @@ where
6060
return false;
6161
}
6262

63-
self.iter().zip(other.iter()).all(|(x, y)| x == y)
63+
if self.len() == 0 {
64+
return true;
65+
}
66+
67+
// ZSTs have no identity and slices don't guarantee which addresses-to-ZSTs they produce
68+
// so we only need to compare them once to determine the behavior of the PartialEq impl
69+
if const { mem::size_of::<A>() == 0 && mem::size_of::<B>() == 0 } {
70+
// zero-length slices are always equal
71+
// SAFETY: A and B are ZSTs so it's ok to conjure them out of thin air
72+
return unsafe { mem::zeroed::<A>() == mem::zeroed::<B>() };
73+
}
74+
75+
const UNROLL: usize = 4;
76+
let mut i = 0;
77+
let mut is_eq = true;
78+
79+
let a = self.as_ptr();
80+
let b = other.as_ptr();
81+
let len = self.len();
82+
83+
// compare items 1 by 1 in case comparisons are expensive. at least one item, then
84+
// until the remainder is a multiple of UNROLL
85+
loop {
86+
// SAFETY: slices are of the same length and loop conditions ensure indexes are in bounds
87+
unsafe {
88+
is_eq = is_eq & PartialEq::eq(&*a.add(i), &*b.add(i));
89+
i = i.unchecked_add(1);
90+
}
91+
92+
if !is_eq || i == len || (len - i) % UNROLL == 0 {
93+
break;
94+
}
95+
}
96+
while is_eq && i + UNROLL <= len {
97+
// SAFETY: slices are of the same length and loop conditions ensure indexes are in bounds
98+
unsafe {
99+
is_eq = is_eq & PartialEq::eq(&*a.add(i), &*b.add(i));
100+
is_eq = is_eq & PartialEq::eq(&*a.add(i + 1), &*b.add(i + 1));
101+
is_eq = is_eq & PartialEq::eq(&*a.add(i + 2), &*b.add(i + 2));
102+
is_eq = is_eq & PartialEq::eq(&*a.add(i + 3), &*b.add(i + 3));
103+
i = i.unchecked_add(UNROLL);
104+
}
105+
}
106+
107+
is_eq
64108
}
65109
}
66110

0 commit comments

Comments
 (0)