Reduce compile time of bevy_ptr::OwnedPtr::make function (#15644)

## Methodology

A good metric that correlates with compile time is the amount of code
generated by the compiler itself; even if the end binary is exactly the
same size, having more copies of the same code can really slow down
compile time, since it has to figure out whether it needs to include
them or not.

The measurement for this used was the [`cargo-llvm-lines`
crate](https://docs.rs/crate/cargo-llvm-lines) which can measure which
functions are generating the most lines of LLVM IR, which generally
means more code compiled. The example compiled was the `breakout` game,
to choose something that touches a decent portion of the engine.

## Solution

Based upon the measurements, `bevy_ptr::OwnedPtr::make` was taking up
4061 lines of LLVM IR in the example code. So, I separated part of this
function into a less-monomorphised version to reduce the amount of
generated code. This was by far the most lines emitted by any single
function.

## Results

After this change, only 2560 lines are emitted, accounting for a 36%
decrease. I tried timing the results and it seemed like it did decrease
compile times a bit, but honestly, the data is really noisy and I can't
be bothered to compile bevy for hours on end to get enough data points.

The tweak feels like an improvement, so, I'll offer it, however small.
This commit is contained in:
Clar Fon 2024-10-28 17:15:00 -04:00 committed by GitHub
parent 1add4bf238
commit 069291d6d8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -397,13 +397,23 @@ impl<'a, T: ?Sized> From<&'a mut T> for PtrMut<'a> {
} }
impl<'a> OwningPtr<'a> { impl<'a> OwningPtr<'a> {
/// This exists mostly to reduce compile times;
/// code is only duplicated per type, rather than per function called.
///
/// # Safety
///
/// Safety constraints of [`PtrMut::promote`] must be upheld.
unsafe fn make_internal<T>(temp: &mut ManuallyDrop<T>) -> OwningPtr<'_> {
// SAFETY: The constraints of `promote` are upheld by caller.
unsafe { PtrMut::from(&mut *temp).promote() }
}
/// Consumes a value and creates an [`OwningPtr`] to it while ensuring a double drop does not happen. /// Consumes a value and creates an [`OwningPtr`] to it while ensuring a double drop does not happen.
#[inline] #[inline]
pub fn make<T, F: FnOnce(OwningPtr<'_>) -> R, R>(val: T, f: F) -> R { pub fn make<T, F: FnOnce(OwningPtr<'_>) -> R, R>(val: T, f: F) -> R {
let mut temp = ManuallyDrop::new(val);
// SAFETY: The value behind the pointer will not get dropped or observed later, // SAFETY: The value behind the pointer will not get dropped or observed later,
// so it's safe to promote it to an owning pointer. // so it's safe to promote it to an owning pointer.
f(unsafe { PtrMut::from(&mut *temp).promote() }) f(unsafe { Self::make_internal(&mut ManuallyDrop::new(val)) })
} }
} }