Skip to content

Commit dfe514e

Browse files
committed
Auto merge of #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 65519f5 + 9d3905f commit dfe514e

File tree

2 files changed

+30
-1
lines changed

2 files changed

+30
-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

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

63-
self.iter().zip(other.iter()).all(|(x, y)| x == y)
63+
let (chunks_a, residual_a) = self.as_chunks::<4>();
64+
let (chunks_b, residual_b) = other.as_chunks::<4>();
65+
66+
// check the residual first to bail out fast if there's a mismatch and comparisons
67+
// happen to be expensive
68+
let mut result = residual_a.into_iter().zip(residual_b).all(|(a, b)| a == b);
69+
70+
// iter.all short-circuits which means the backend can't unroll the loop due to early exits.
71+
// So we unroll it manually.
72+
result = result
73+
&& chunks_a
74+
.iter()
75+
.zip(chunks_b.iter())
76+
.all(|(a, b)| (a[0] == b[0]) & (a[1] == b[1]) & (a[2] == b[2]) & (a[3] == b[3]));
77+
78+
result
6479
}
6580
}
6681

0 commit comments

Comments
 (0)