Skip to content

Commit 08ba7b2

Browse files
bmbergerjoshlf
andcommitted
Optimize Ref<_, T> methods when T: Sized (#2752)
Benchmarks are surprisingly showing minimal impact... With optimization: test r#ref::tests::bench_deref_mut_sized ... bench: 0.72 ns/iter (+/- 0.09) test r#ref::tests::bench_deref_sized ... bench: 0.70 ns/iter (+/- 0.09) test r#ref::tests::bench_from_bytes_sized ... bench: 5.27 ns/iter (+/- 0.69) test r#ref::tests::bench_into_mut_sized ... bench: 0.70 ns/iter (+/- 0.07) test r#ref::tests::bench_into_ref_sized ... bench: 0.94 ns/iter (+/- 0.08) Without optimization: test r#ref::tests::bench_deref_mut_sized ... bench: 0.72 ns/iter (+/- 0.23) test r#ref::tests::bench_deref_sized ... bench: 0.73 ns/iter (+/- 0.09) test r#ref::tests::bench_from_bytes_sized ... bench: 5.26 ns/iter (+/- 0.69) test r#ref::tests::bench_into_mut_sized ... bench: 0.70 ns/iter (+/- 0.08) test r#ref::tests::bench_into_ref_sized ... bench: 0.97 ns/iter (+/- 0.08) Closes #2752 Co-authored-by: Joshua Liebow-Feeser <[email protected]>
1 parent e8eb595 commit 08ba7b2

File tree

2 files changed

+234
-2
lines changed

2 files changed

+234
-2
lines changed

src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,13 +334,17 @@
334334
__ZEROCOPY_INTERNAL_USE_ONLY_NIGHTLY_FEATURES_IN_TESTS,
335335
feature(layout_for_ptr, coverage_attribute)
336336
)]
337+
#![cfg_attr(test, feature(test))]
337338

338339
// This is a hack to allow zerocopy-derive derives to work in this crate. They
339340
// assume that zerocopy is linked as an extern crate, so they access items from
340341
// it as `zerocopy::Xxx`. This makes that still work.
341342
#[cfg(any(feature = "derive", test))]
342343
extern crate self as zerocopy;
343344

345+
#[cfg(test)]
346+
extern crate test;
347+
344348
#[doc(hidden)]
345349
#[macro_use]
346350
pub mod util;

src/ref.rs

Lines changed: 230 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,9 @@ mod def {
192192

193193
#[allow(unreachable_pub)] // This is a false positive on our MSRV toolchain.
194194
pub use def::Ref;
195+
use crate::pointer::{BecauseRead, BecauseMutationCompatible, PtrInner, invariant::{
196+
Aligned, BecauseExclusive, Exclusive, Initialized, Shared, Unaligned, Valid
197+
}};
195198

196199
impl<B, T> Ref<B, T>
197200
where
@@ -616,6 +619,39 @@ where
616619
// SAFETY: We don't call any methods on `b` other than those provided by
617620
// `IntoByteSlice`.
618621
let b = unsafe { r.into_byte_slice() };
622+
let b = b.into_byte_slice();
623+
624+
if let crate::layout::SizeInfo::Sized { .. } = T::LAYOUT.size_info {
625+
let ptr = Ptr::from_ref(b);
626+
let ptr: Ptr<'_, [u8], (Shared, Unaligned, Initialized)> = ptr.transmute::<_, _, (_, (_, _))>();
627+
// SAFETY: `T::raw_from_ptr_len` promises to preserve address and provenance in the referent.
628+
let ptr: Ptr<'_, T, (Shared, Unaligned, Initialized)> = unsafe {
629+
ptr.cast_unsized(|ptr| {
630+
let ptr = T::raw_from_ptr_len(
631+
ptr.as_non_null().cast(),
632+
<T::PointerMetadata as crate::PointerMetadata>::from_elem_count(0),
633+
);
634+
// SAFETY: The safety invariants of `Ptr::new` (see definition) are
635+
// satisfied:
636+
// 0. If `ptr`'s referent is not zero sized, then `ptr` has valid
637+
// provenance for its referent, because it derived from `self`
638+
// using a series of provenance-preserving operations, and
639+
// because `self` has valid provenance for its referent. By the
640+
// same argument, `ptr`'s referent is entirely contained within
641+
// the same allocated object as `self`'s referent.
642+
// 1. If `ptr`'s referent is not zero sized, then the allocation of
643+
// `ptr` is guaranteed to live for at least `'a`, because `ptr`
644+
// is entirely contained in `self`, which lives for at least `'a`
645+
// by invariant on `Ptr`.
646+
unsafe { PtrInner::new(ptr) }
647+
})
648+
};
649+
650+
// SAFETY: By invariant on `r`, we know that alignment is valid.
651+
let ptr = unsafe { ptr.assume_alignment::<Aligned>() };
652+
let ptr = ptr.recall_validity::<Valid, _>();
653+
return ptr.as_ref();
654+
}
619655

620656
// PANICS: By post-condition on `into_byte_slice`, `b`'s size and
621657
// alignment are valid for `T`. By post-condition, `b.into_byte_slice()`
@@ -650,6 +686,58 @@ where
650686
// SAFETY: We don't call any methods on `b` other than those provided by
651687
// `IntoByteSliceMut`.
652688
let b = unsafe { r.into_byte_slice_mut() };
689+
let b = b.into_byte_slice_mut();
690+
691+
if let crate::layout::SizeInfo::Sized { .. } = T::LAYOUT.size_info {
692+
let ptr = Ptr::from_mut(b);
693+
let ptr: Ptr<'_, [u8], (Exclusive, Unaligned, Initialized)> = ptr.transmute::<
694+
[u8],
695+
Initialized,
696+
(
697+
BecauseMutationCompatible,
698+
(
699+
BecauseRead,
700+
(BecauseExclusive, BecauseExclusive),
701+
),
702+
),
703+
>();
704+
// SAFETY: `T::raw_from_ptr_len` promises to preserve address and provenance in the referent.
705+
let ptr: Ptr<'_, T, (Exclusive, Unaligned, Initialized)> = unsafe {
706+
ptr.cast_unsized::<_, _, (BecauseRead, (BecauseExclusive, BecauseExclusive))>(|ptr| {
707+
let ptr = T::raw_from_ptr_len(
708+
ptr.as_non_null().cast(),
709+
<T::PointerMetadata as crate::PointerMetadata>::from_elem_count(0),
710+
);
711+
// SAFETY: The safety invariants of `Ptr::new` (see definition) are
712+
// satisfied:
713+
// 0. If `ptr`'s referent is not zero sized, then `ptr` has valid
714+
// provenance for its referent, because it derived from `self`
715+
// using a series of provenance-preserving operations, and
716+
// because `self` has valid provenance for its referent. By the
717+
// same argument, `ptr`'s referent is entirely contained within
718+
// the same allocated object as `self`'s referent.
719+
// 1. If `ptr`'s referent is not zero sized, then the allocation of
720+
// `ptr` is guaranteed to live for at least `'a`, because `ptr`
721+
// is entirely contained in `self`, which lives for at least `'a`
722+
// by invariant on `Ptr`.
723+
unsafe { PtrInner::new(ptr) }
724+
})
725+
};
726+
727+
// SAFETY: By invariant on `r`, we know that alignment is valid.
728+
let ptr = unsafe { ptr.assume_alignment::<Aligned>() };
729+
let ptr = ptr.recall_validity::<
730+
Valid,
731+
(
732+
BecauseMutationCompatible,
733+
(
734+
BecauseRead,
735+
(BecauseExclusive, BecauseExclusive),
736+
),
737+
),
738+
>();
739+
return ptr.as_mut();
740+
}
653741

654742
// PANICS: By post-condition on `into_byte_slice_mut`, `b`'s size and
655743
// alignment are valid for `T`. By post-condition,
@@ -763,11 +851,44 @@ where
763851
// SAFETY: We don't call any methods on `b` other than those provided by
764852
// `ByteSlice`.
765853
let b = unsafe { self.as_byte_slice() };
854+
let b = b.deref();
855+
856+
if let crate::layout::SizeInfo::Sized { .. } = T::LAYOUT.size_info {
857+
let ptr = Ptr::from_ref(b);
858+
let ptr: Ptr<'_, [u8], (Shared, Unaligned, Initialized)> = ptr.transmute::<_, _, (_, (_, _))>();
859+
// SAFETY: `T::raw_from_ptr_len` promises to preserve address and provenance in the referent.
860+
let ptr: Ptr<'_, T, (Shared, Unaligned, Initialized)> = unsafe {
861+
ptr.cast_unsized(|ptr| {
862+
let ptr = T::raw_from_ptr_len(
863+
ptr.as_non_null().cast(),
864+
<T::PointerMetadata as crate::PointerMetadata>::from_elem_count(0),
865+
);
866+
// SAFETY: The safety invariants of `Ptr::new` (see definition) are
867+
// satisfied:
868+
// 0. If `ptr`'s referent is not zero sized, then `ptr` has valid
869+
// provenance for its referent, because it derived from `self`
870+
// using a series of provenance-preserving operations, and
871+
// because `self` has valid provenance for its referent. By the
872+
// same argument, `ptr`'s referent is entirely contained within
873+
// the same allocated object as `self`'s referent.
874+
// 1. If `ptr`'s referent is not zero sized, then the allocation of
875+
// `ptr` is guaranteed to live for at least `'a`, because `ptr`
876+
// is entirely contained in `self`, which lives for at least `'a`
877+
// by invariant on `Ptr`.
878+
unsafe { PtrInner::new(ptr) }
879+
})
880+
};
881+
882+
// SAFETY: By invariant on `r`, we know that alignment is valid.
883+
let ptr = unsafe { ptr.assume_alignment::<Aligned>() };
884+
let ptr = ptr.recall_validity::<Valid, _>();
885+
return ptr.as_ref();
886+
}
766887

767888
// PANICS: By postcondition on `as_byte_slice`, `b`'s size and alignment
768889
// are valid for `T`, and by invariant on `ByteSlice`, these are
769890
// preserved through `.deref()`, so this `unwrap` will not panic.
770-
let ptr = Ptr::from_ref(b.deref())
891+
let ptr = Ptr::from_ref(b)
771892
.try_cast_into_no_leftover::<T, BecauseImmutable>(None)
772893
.expect("zerocopy internal error: Deref::deref should be infallible");
773894
let ptr = ptr.recall_validity();
@@ -791,12 +912,64 @@ where
791912
// SAFETY: We don't call any methods on `b` other than those provided by
792913
// `ByteSliceMut`.
793914
let b = unsafe { self.as_byte_slice_mut() };
915+
let b = b.deref_mut();
916+
917+
if let crate::layout::SizeInfo::Sized { .. } = T::LAYOUT.size_info {
918+
let ptr = Ptr::from_mut(b);
919+
let ptr: Ptr<'_, [u8], (Exclusive, Unaligned, Initialized)> = ptr.transmute::<
920+
[u8],
921+
Initialized,
922+
(
923+
BecauseMutationCompatible,
924+
(
925+
BecauseRead,
926+
(BecauseExclusive, BecauseExclusive),
927+
),
928+
),
929+
>();
930+
// SAFETY: `T::raw_from_ptr_len` promises to preserve address and provenance in the referent.
931+
let ptr: Ptr<'_, T, (Exclusive, Unaligned, Initialized)> = unsafe {
932+
ptr.cast_unsized::<_, _, (BecauseRead, (BecauseExclusive, BecauseExclusive))>(|ptr| {
933+
let ptr = T::raw_from_ptr_len(
934+
ptr.as_non_null().cast(),
935+
<T::PointerMetadata as crate::PointerMetadata>::from_elem_count(0),
936+
);
937+
// SAFETY: The safety invariants of `Ptr::new` (see definition) are
938+
// satisfied:
939+
// 0. If `ptr`'s referent is not zero sized, then `ptr` has valid
940+
// provenance for its referent, because it derived from `self`
941+
// using a series of provenance-preserving operations, and
942+
// because `self` has valid provenance for its referent. By the
943+
// same argument, `ptr`'s referent is entirely contained within
944+
// the same allocated object as `self`'s referent.
945+
// 1. If `ptr`'s referent is not zero sized, then the allocation of
946+
// `ptr` is guaranteed to live for at least `'a`, because `ptr`
947+
// is entirely contained in `self`, which lives for at least `'a`
948+
// by invariant on `Ptr`.
949+
unsafe { PtrInner::new(ptr) }
950+
})
951+
};
952+
953+
// SAFETY: By invariant on `r`, we know that alignment is valid.
954+
let ptr = unsafe { ptr.assume_alignment::<Aligned>() };
955+
let ptr = ptr.recall_validity::<
956+
Valid,
957+
(
958+
BecauseMutationCompatible,
959+
(
960+
BecauseRead,
961+
(BecauseExclusive, BecauseExclusive),
962+
),
963+
),
964+
>();
965+
return ptr.as_mut();
966+
}
794967

795968
// PANICS: By postcondition on `as_byte_slice_mut`, `b`'s size and
796969
// alignment are valid for `T`, and by invariant on `ByteSlice`, these
797970
// are preserved through `.deref_mut()`, so this `unwrap` will not
798971
// panic.
799-
let ptr = Ptr::from_mut(b.deref_mut())
972+
let ptr = Ptr::from_mut(b)
800973
.try_cast_into_no_leftover::<T, BecauseExclusive>(None)
801974
.expect("zerocopy internal error: DerefMut::deref_mut should be infallible");
802975
let ptr = ptr.recall_validity::<_, (_, (_, (BecauseExclusive, BecauseExclusive)))>();
@@ -879,6 +1052,61 @@ mod tests {
8791052

8801053
use super::*;
8811054
use crate::util::testutil::*;
1055+
use test::{self, Bencher};
1056+
1057+
#[bench]
1058+
fn bench_from_bytes_sized(b: &mut Bencher) {
1059+
let buf = Align::<[u8; 8], AU64>::default();
1060+
// `buf.t` should be aligned to 8, so this should always succeed.
1061+
let bytes = &buf.t[..];
1062+
b.iter(|| test::black_box(Ref::<_, AU64>::from_bytes(test::black_box(bytes)).unwrap()));
1063+
}
1064+
1065+
#[bench]
1066+
fn bench_into_ref_sized(b: &mut Bencher) {
1067+
let buf = Align::<[u8; 8], AU64>::default();
1068+
let bytes = &buf.t[..];
1069+
let r = Ref::<_, AU64>::from_bytes(bytes).unwrap();
1070+
b.iter(|| {
1071+
test::black_box(Ref::into_ref(test::black_box(r)))
1072+
});
1073+
}
1074+
1075+
#[bench]
1076+
fn bench_into_mut_sized(b: &mut Bencher) {
1077+
let mut buf = Align::<[u8; 8], AU64>::default();
1078+
let _ = Ref::<_, AU64>::from_bytes(&mut buf.t[..]).unwrap();
1079+
b.iter(move || {
1080+
// SAFETY: The preceding `from_bytes` succeeded, and so we know that
1081+
// `&mut buf.t[.]
1082+
let r = unsafe { Ref::<&mut [u8], AU64>::new_unchecked(&mut buf.t[..]) };
1083+
test::black_box(Ref::into_mut(test::black_box(r)));
1084+
});
1085+
}
1086+
1087+
#[bench]
1088+
fn bench_deref_sized(b: &mut Bencher) {
1089+
let buf = Align::<[u8; 8], AU64>::default();
1090+
let bytes = &buf.t[..];
1091+
let r = Ref::<_, AU64>::from_bytes(bytes).unwrap();
1092+
b.iter(|| {
1093+
let temp = test::black_box(r);
1094+
test::black_box(temp.deref());
1095+
});
1096+
}
1097+
1098+
#[bench]
1099+
fn bench_deref_mut_sized(b: &mut Bencher) {
1100+
let mut buf = Align::<[u8; 8], AU64>::default();
1101+
let _ = Ref::<_, AU64>::from_bytes(&mut buf.t[..]).unwrap();
1102+
b.iter(|| {
1103+
// SAFETY: The preceding `from_bytes` succeeded, and so we know that
1104+
// `&mut buf.t[.]
1105+
let r = unsafe { Ref::<&mut [u8], AU64>::new_unchecked(&mut buf.t[..]) };
1106+
let mut temp = test::black_box(r);
1107+
test::black_box(temp.deref_mut());
1108+
});
1109+
}
8821110

8831111
#[test]
8841112
fn test_mut_slice_into_ref() {

0 commit comments

Comments
 (0)