From 76ea52babe4437e8a12401e664c5a9d3c7c03fdf Mon Sep 17 00:00:00 2001 From: overlookmotel <557937+overlookmotel@users.noreply.github.com> Date: Sat, 18 Jan 2025 01:23:56 +0000 Subject: [PATCH] perf(allocator): inline `Box` methods (#8572) Add `#[inline]` or `#[inline(always)]` to many of `Box`'s methods. In particular, `Box::new_in` should absolutely always be inlined, for the same reasons that `Allocator::alloc` should be. --- crates/oxc_allocator/src/boxed.rs | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/crates/oxc_allocator/src/boxed.rs b/crates/oxc_allocator/src/boxed.rs index 68a4a133485c1..003b493404d3a 100644 --- a/crates/oxc_allocator/src/boxed.rs +++ b/crates/oxc_allocator/src/boxed.rs @@ -48,6 +48,7 @@ impl Box<'_, T> { /// /// assert_eq!(i, 5); /// ``` + #[inline] pub fn unbox(self) -> T { // SAFETY: // This pointer read is safe because the reference `self.0` is @@ -70,6 +71,11 @@ impl Box<'_, T> { /// let arena = Allocator::default(); /// let in_arena: Box = Box::new_in(5, &arena); /// ``` + // + // `#[inline(always)]` because this is a hot path and `Allocator::alloc` is a very small function. + // We always want it to be inlined. + #[expect(clippy::inline_always)] + #[inline(always)] pub fn new_in(value: T, allocator: &Allocator) -> Self { const { assert!(!needs_drop::(), "Cannot create a Box where T is a Drop type"); @@ -106,7 +112,10 @@ impl Box<'_, T> { /// with no other aliases. You must not, for example, create 2 `Box`es from the same pointer. /// /// `ptr` must have been created from a `*mut T` or `&mut T` (not a `*const T` / `&T`). - #[inline] + // + // `#[inline(always)]` because this is a no-op + #[expect(clippy::inline_always)] + #[inline(always)] pub(crate) const unsafe fn from_non_null(ptr: NonNull) -> Self { const { assert!(!needs_drop::(), "Cannot create a Box where T is a Drop type"); @@ -116,8 +125,10 @@ impl Box<'_, T> { } /// Consume a [`Box`] and return a [`NonNull`] pointer to its contents. - #[inline] - #[expect(clippy::needless_pass_by_value)] + // + // `#[inline(always)]` because this is a no-op + #[expect(clippy::inline_always, clippy::needless_pass_by_value)] + #[inline(always)] pub fn into_non_null(boxed: Self) -> NonNull { boxed.0 } @@ -126,6 +137,7 @@ impl Box<'_, T> { impl ops::Deref for Box<'_, T> { type Target = T; + #[inline] fn deref(&self) -> &T { // SAFETY: self.0 is always a unique reference allocated from a Bump in Box::new_in unsafe { self.0.as_ref() } @@ -133,6 +145,7 @@ impl ops::Deref for Box<'_, T> { } impl ops::DerefMut for Box<'_, T> { + #[inline] fn deref_mut(&mut self) -> &mut T { // SAFETY: self.0 is always a unique reference allocated from a Bump in Box::new_in unsafe { self.0.as_mut() } @@ -140,18 +153,21 @@ impl ops::DerefMut for Box<'_, T> { } impl AsRef for Box<'_, T> { + #[inline] fn as_ref(&self) -> &T { self } } impl AsMut for Box<'_, T> { + #[inline] fn as_mut(&mut self) -> &mut T { self } } impl Debug for Box<'_, T> { + #[inline] fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { self.deref().fmt(f) } @@ -181,6 +197,7 @@ where } impl Hash for Box<'_, T> { + #[inline] fn hash(&self, state: &mut H) { self.deref().hash(state); }