diff --git a/library/core/benches/slice.rs b/library/core/benches/slice.rs index 3bfb35e684ea1..1ec51653d92ef 100644 --- a/library/core/benches/slice.rs +++ b/library/core/benches/slice.rs @@ -171,3 +171,17 @@ fn fold_to_last(b: &mut Bencher) { let slice: &[i32] = &[0; 1024]; b.iter(|| black_box(slice).iter().fold(None, |_, r| Some(NonNull::from(r)))); } + +#[bench] +fn slice_cmp_generic(b: &mut Bencher) { + #[derive(PartialEq, Clone, Copy)] + struct Foo(u32, u32); + + let left = [Foo(128, 128); 128]; + let right = [Foo(128, 128); 128]; + + b.iter(|| { + let (left, right) = (black_box(&left), black_box(&right)); + left.as_slice() == right.as_slice() + }); +} diff --git a/library/core/src/slice/cmp.rs b/library/core/src/slice/cmp.rs index 075347b80d031..3e8d8d616d561 100644 --- a/library/core/src/slice/cmp.rs +++ b/library/core/src/slice/cmp.rs @@ -60,7 +60,51 @@ where return false; } - self.iter().zip(other.iter()).all(|(x, y)| x == y) + if self.len() == 0 { + return true; + } + + // ZSTs have no identity and slices don't guarantee which addresses-to-ZSTs they produce + // so we only need to compare them once to determine the behavior of the PartialEq impl + if const { mem::size_of::() == 0 && mem::size_of::() == 0 } { + // zero-length slices are always equal + // SAFETY: A and B are ZSTs so it's ok to conjure them out of thin air + return unsafe { mem::zeroed::() == mem::zeroed::() }; + } + + const UNROLL: usize = 4; + let mut i = 0; + let mut is_eq = true; + + let a = self.as_ptr(); + let b = other.as_ptr(); + let len = self.len(); + + // compare items 1 by 1 in case comparisons are expensive. at least one item, then + // until the remainder is a multiple of UNROLL + loop { + // SAFETY: slices are of the same length and loop conditions ensure indexes are in bounds + unsafe { + is_eq = is_eq & PartialEq::eq(&*a.add(i), &*b.add(i)); + i = i.unchecked_add(1); + } + + if !is_eq || i == len || (len - i) % UNROLL == 0 { + break; + } + } + while is_eq && i + UNROLL <= len { + // SAFETY: slices are of the same length and loop conditions ensure indexes are in bounds + unsafe { + is_eq = is_eq & PartialEq::eq(&*a.add(i), &*b.add(i)); + is_eq = is_eq & PartialEq::eq(&*a.add(i + 1), &*b.add(i + 1)); + is_eq = is_eq & PartialEq::eq(&*a.add(i + 2), &*b.add(i + 2)); + is_eq = is_eq & PartialEq::eq(&*a.add(i + 3), &*b.add(i + 3)); + i = i.unchecked_add(UNROLL); + } + } + + is_eq } }