From 88723dbf19a402c6395d34b71a5a8a712b4bf1bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Donny/=EA=B0=95=EB=8F=99=EC=9C=A4?= Date: Fri, 19 Jul 2024 15:16:48 +0900 Subject: [PATCH] perf(allocator): Use `std` instead of `allocator-api2` (#9281) **Description:** `std` is faster. --- Cargo.lock | 5 -- crates/swc_allocator/Cargo.toml | 6 +- crates/swc_allocator/src/alloc.rs | 49 +++++++++------- crates/swc_allocator/src/boxed/mod.rs | 17 +++--- crates/swc_allocator/src/boxed/serde.rs | 2 +- crates/swc_allocator/src/lib.rs | 8 +-- crates/swc_allocator/src/vec/mod.rs | 45 +++++++++++---- crates/swc_allocator/src/vec/serde.rs | 77 +++++++++++++++++++++++++ 8 files changed, 152 insertions(+), 57 deletions(-) create mode 100644 crates/swc_allocator/src/vec/serde.rs diff --git a/Cargo.lock b/Cargo.lock index a5abd2d7e05f..b578dcc05dff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -67,9 +67,6 @@ name = "allocator-api2" version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" -dependencies = [ - "serde", -] [[package]] name = "android-tzdata" @@ -3711,14 +3708,12 @@ dependencies = [ name = "swc_allocator" version = "0.1.7" dependencies = [ - "allocator-api2", "bumpalo", "codspeed-criterion-compat", "criterion", "ptr_meta", "rkyv", "serde", - "serde_derive", "swc_malloc", "triomphe", ] diff --git a/crates/swc_allocator/Cargo.toml b/crates/swc_allocator/Cargo.toml index cc4863af9732..26b7acc8b21d 100644 --- a/crates/swc_allocator/Cargo.toml +++ b/crates/swc_allocator/Cargo.toml @@ -15,13 +15,12 @@ version = "0.1.7" [features] default = ["scoped"] -nightly = [] +nightly = ["bumpalo/allocator_api"] rkyv = ["dep:rkyv"] scoped = ["nightly"] -serde = ["dep:serde", "dep:serde_derive", "allocator-api2/serde"] +serde = ["dep:serde"] [dependencies] -allocator-api2 = { workspace = true } bumpalo = { workspace = true, features = [ "boxed", "collections", @@ -30,7 +29,6 @@ bumpalo = { workspace = true, features = [ ptr_meta = { workspace = true } rkyv = { workspace = true, optional = true } serde = { workspace = true, optional = true } -serde_derive = { workspace = true, optional = true } triomphe = { workspace = true } diff --git a/crates/swc_allocator/src/alloc.rs b/crates/swc_allocator/src/alloc.rs index 2b8825695899..8ab57fb5ac42 100644 --- a/crates/swc_allocator/src/alloc.rs +++ b/crates/swc_allocator/src/alloc.rs @@ -1,16 +1,18 @@ -use std::{ - alloc::Layout, - cell::Cell, - mem::transmute, - ops::{Deref, DerefMut}, - ptr::NonNull, -}; - -use allocator_api2::alloc::AllocError; +#[cfg(feature = "nightly")] +use std::alloc::{AllocError, Layout}; +#[cfg(feature = "scoped")] +use std::cell::Cell; +#[cfg(feature = "scoped")] +use std::mem::transmute; +use std::ops::{Deref, DerefMut}; +#[cfg(feature = "nightly")] +use std::ptr::NonNull; + use bumpalo::Bump; use crate::FastAlloc; +#[cfg(feature = "scoped")] thread_local! { static ALLOC: Cell> = const { Cell::new(None) }; } @@ -22,11 +24,13 @@ pub struct Allocator { } pub struct AllocGuard { + #[cfg(feature = "scoped")] orig: Option<&'static Allocator>, } impl Drop for AllocGuard { fn drop(&mut self) { + #[cfg(feature = "scoped")] ALLOC.set(self.orig.take()); } } @@ -39,15 +43,21 @@ impl Allocator { /// [Allocator] must outlive [crate::boxed::Box] and [crate::vec::Vec] /// created while the guard is active. pub unsafe fn guard(&self) -> AllocGuard { + #[cfg(feature = "scoped")] let orig = ALLOC.get(); + #[cfg(feature = "scoped")] let s = unsafe { // Safery: We are using a scoped API transmute::<&Allocator, &'static Allocator>(self) }; + #[cfg(feature = "scoped")] ALLOC.set(Some(s)); - AllocGuard { orig } + AllocGuard { + #[cfg(feature = "scoped")] + orig, + } } } @@ -66,27 +76,24 @@ impl Default for FastAlloc { impl FastAlloc { /// `true` is passed to `f` if the box is allocated with a custom allocator. - fn with_allocator( - &self, - f: impl FnOnce(&dyn allocator_api2::alloc::Allocator, bool) -> T, - ) -> T { + #[cfg(feature = "nightly")] + fn with_allocator(&self, f: impl FnOnce(&dyn std::alloc::Allocator, bool) -> T) -> T { #[cfg(feature = "scoped")] if let Some(arena) = &self.alloc { - return f( - (&&arena.alloc) as &dyn allocator_api2::alloc::Allocator, - true, - ); + return f((&&arena.alloc) as &dyn std::alloc::Allocator, true); } - f(&allocator_api2::alloc::Global, false) + f(&std::alloc::Global, false) } } +#[cfg(feature = "nightly")] fn mark_ptr_as_arena_mode(ptr: NonNull<[u8]>) -> NonNull<[u8]> { ptr } -unsafe impl allocator_api2::alloc::Allocator for FastAlloc { +#[cfg(feature = "nightly")] +unsafe impl std::alloc::Allocator for FastAlloc { #[inline] fn allocate(&self, layout: Layout) -> Result, AllocError> { self.with_allocator(|a, is_arena_mode| { @@ -121,7 +128,7 @@ unsafe impl allocator_api2::alloc::Allocator for FastAlloc { return; } - allocator_api2::alloc::Global.deallocate(ptr, layout) + std::alloc::Global.deallocate(ptr, layout) } #[inline] diff --git a/crates/swc_allocator/src/boxed/mod.rs b/crates/swc_allocator/src/boxed/mod.rs index 63b3ce8b681a..223b784e3be1 100644 --- a/crates/swc_allocator/src/boxed/mod.rs +++ b/crates/swc_allocator/src/boxed/mod.rs @@ -19,7 +19,7 @@ mod serde; /// Faster alterantive for [`std::boxed::Box`]. #[repr(transparent)] #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct Box(pub(crate) allocator_api2::boxed::Box); +pub struct Box(pub(crate) std::boxed::Box); impl From for Box { #[inline(always)] @@ -28,9 +28,9 @@ impl From for Box { } } -impl From> for Box { +impl From> for Box { #[inline(always)] - fn from(v: allocator_api2::boxed::Box) -> Self { + fn from(v: std::boxed::Box) -> Self { Box(v) } } @@ -76,12 +76,12 @@ impl Box { /// ``` #[inline(always)] pub fn new_in(value: T, alloc: FastAlloc) -> Self { - Self(allocator_api2::boxed::Box::new_in(value, alloc)) + Self(std::boxed::Box::new_in(value, alloc)) } /// Moves the value out of the box. pub fn unbox(self) -> T { - allocator_api2::boxed::Box::into_inner(self.0) + std::boxed::Box::into_inner(self.0) } } @@ -128,10 +128,7 @@ impl Box { /// [memory layout]: self#memory-layout /// [`Layout`]: crate::Layout pub unsafe fn from_raw(raw: *mut T) -> Self { - Self(allocator_api2::boxed::Box::from_raw_in( - raw, - FastAlloc::default(), - )) + Self(std::boxed::Box::from_raw_in(raw, FastAlloc::default())) } /// Consumes the `Box`, returning a wrapped raw pointer. @@ -182,7 +179,7 @@ impl Box { /// /// [memory layout]: self#memory-layout pub fn into_raw(b: Self) -> *mut T { - allocator_api2::boxed::Box::into_raw(b.0) + std::boxed::Box::into_raw(b.0) } /// Converts a `Box` into a `Pin>`. If `T` does not implement diff --git a/crates/swc_allocator/src/boxed/serde.rs b/crates/swc_allocator/src/boxed/serde.rs index 17c76e81d71d..e30c1b0f55e3 100644 --- a/crates/swc_allocator/src/boxed/serde.rs +++ b/crates/swc_allocator/src/boxed/serde.rs @@ -22,6 +22,6 @@ where where D: Deserializer<'de>, { - Ok(Box(allocator_api2::boxed::Box::deserialize(deserializer)?)) + Ok(Box::new(T::deserialize(deserializer)?)) } } diff --git a/crates/swc_allocator/src/lib.rs b/crates/swc_allocator/src/lib.rs index 79ea78bfe803..2a39dd4556f3 100644 --- a/crates/swc_allocator/src/lib.rs +++ b/crates/swc_allocator/src/lib.rs @@ -29,7 +29,7 @@ #![cfg_attr(docsrs, feature(doc_cfg))] #![cfg_attr( feature = "nightly", - feature(allocator_api, fundamental, with_negative_coherence) + feature(allocator_api, fundamental, with_negative_coherence, box_into_inner) )] #![deny(missing_docs)] #![allow(clippy::derivable_impls)] @@ -66,9 +66,9 @@ pub mod maybe { /// /// # Misc /// -/// It implements [`allocator_api2::alloc::Allocator`]. So it can be used as the -/// second argument for [`allocator_api2::boxed::Box`] and -/// [`allocator_api2::vec::Vec`]. But you should prefer using +/// It implements [`std::alloc::Allocator`]. So it can be used as the +/// second argument for [`std::boxed::Box`] and +/// [`std::vec::Vec`]. But you should prefer using /// [`crate::boxed::Box`] and [`crate::vec::Vec`], which is a wrapper around the /// original types. #[derive(Clone, Copy)] diff --git a/crates/swc_allocator/src/vec/mod.rs b/crates/swc_allocator/src/vec/mod.rs index e5607b0f3d31..cd00ec212e0c 100644 --- a/crates/swc_allocator/src/vec/mod.rs +++ b/crates/swc_allocator/src/vec/mod.rs @@ -7,17 +7,16 @@ use std::{ #[cfg(feature = "rkyv")] mod rkyv; +#[cfg(feature = "serde")] +mod serde; use crate::{boxed::Box, FastAlloc}; /// Faster version of [`std::vec::Vec`]. #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(transparent)] -#[cfg_attr( - feature = "serde", - derive(serde_derive::Serialize, serde_derive::Deserialize) -)] -pub struct Vec(allocator_api2::vec::Vec); + +pub struct Vec(std::vec::Vec); impl Vec { /// Constructs a new, empty `Vec`. @@ -32,6 +31,7 @@ impl Vec { /// ``` /// /// Note: This is slower than using [Self::new_in] with cached [FastAlloc]. + #[inline(always)] pub fn new() -> Self { Self::new_in(Default::default()) } @@ -41,8 +41,9 @@ impl Vec { /// The vector will not allocate until elements are pushed onto it. /// /// See [std::vec::Vec::new_in] for more information. + #[inline(always)] pub fn new_in(alloc: FastAlloc) -> Self { - Self(allocator_api2::vec::Vec::new_in(alloc)) + Self(std::vec::Vec::new_in(alloc)) } /// Constructs a new, empty `Vec` with at least the specified capacity. @@ -98,6 +99,7 @@ impl Vec { /// /// Note: This is slower than using [Self::with_capacity_in] with cached /// [FastAlloc]. + #[inline(always)] pub fn with_capacity(capacity: usize) -> Self { Self::with_capacity_in(capacity, Default::default()) } @@ -128,8 +130,9 @@ impl Vec { /// Panics if the new capacity exceeds `isize::MAX` bytes. /// /// See [std::vec::Vec::with_capacity_in] for more information. + #[inline(always)] pub fn with_capacity_in(capacity: usize, alloc: FastAlloc) -> Self { - Self(allocator_api2::vec::Vec::with_capacity_in(capacity, alloc)) + Self(std::vec::Vec::with_capacity_in(capacity, alloc)) } /// Converts the vector into [`Box<[T]>`][owned slice]. @@ -158,6 +161,7 @@ impl Vec { /// let slice = vec.into_boxed_slice(); /// assert_eq!(slice.into_vec().capacity(), 3); /// ``` + #[inline(always)] pub fn into_boxed_slice(self) -> Box<[T]> { self.0.into_boxed_slice().into() } @@ -185,6 +189,7 @@ impl Vec { /// static_ref[0] += 1; /// assert_eq!(static_ref, &[2, 2, 3]); /// ``` + #[inline(always)] pub fn leak(self) -> &'static mut [T] { self.0.leak() } @@ -269,8 +274,9 @@ impl Vec { /// assert_eq!(rebuilt, [4, 5, 6]); /// } /// ``` + #[inline(always)] pub unsafe fn from_raw_parts(ptr: *mut T, length: usize, capacity: usize) -> Self { - Self(allocator_api2::vec::Vec::from_raw_parts_in( + Self(std::vec::Vec::from_raw_parts_in( ptr, length, capacity, @@ -280,7 +286,7 @@ impl Vec { } impl Deref for Vec { - type Target = allocator_api2::vec::Vec; + type Target = std::vec::Vec; #[inline(always)] fn deref(&self) -> &Self::Target { @@ -296,15 +302,17 @@ impl DerefMut for Vec { } impl Default for Vec { + #[inline(always)] fn default() -> Self { - Self(allocator_api2::vec::Vec::new_in(FastAlloc::default())) + Self(std::vec::Vec::new_in(FastAlloc::default())) } } impl IntoIterator for Vec { - type IntoIter = allocator_api2::vec::IntoIter; + type IntoIter = std::vec::IntoIter; type Item = T; + #[inline(always)] fn into_iter(self) -> Self::IntoIter { self.0.into_iter() } @@ -313,6 +321,7 @@ impl<'a, T> IntoIterator for &'a Vec { type IntoIter = std::slice::Iter<'a, T>; type Item = &'a T; + #[inline(always)] fn into_iter(self) -> Self::IntoIter { self.iter() } @@ -322,12 +331,14 @@ impl<'a, T> IntoIterator for &'a mut Vec { type IntoIter = std::slice::IterMut<'a, T>; type Item = &'a mut T; + #[inline(always)] fn into_iter(self) -> Self::IntoIter { self.iter_mut() } } impl FromIterator for Vec { + #[inline(always)] fn from_iter>(iter: I) -> Self { let mut vec = Vec::default(); vec.extend(iter); @@ -336,44 +347,53 @@ impl FromIterator for Vec { } impl From> for Vec { + #[inline(always)] fn from(v: Box<[T]>) -> Self { - Self(allocator_api2::vec::Vec::from(v.0)) + Self(std::vec::Vec::from(v.0)) } } impl From> for Box<[T]> { + #[inline(always)] fn from(v: Vec) -> Self { Box(v.0.into()) } } impl Extend for Vec { + #[inline(always)] fn extend>(&mut self, iter: I) { self.0.extend(iter) } } impl io::Write for Vec { + #[inline(always)] fn write(&mut self, buf: &[u8]) -> io::Result { io::Write::write(&mut self.0, buf) } + #[inline(always)] fn flush(&mut self) -> io::Result<()> { io::Write::flush(&mut self.0) } + #[inline(always)] fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { io::Write::write_all(&mut self.0, buf) } + #[inline(always)] fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result { io::Write::write_vectored(&mut self.0, bufs) } + #[inline(always)] fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> { io::Write::write_fmt(&mut self.0, fmt) } + #[inline(always)] fn by_ref(&mut self) -> &mut Self where Self: Sized, @@ -383,6 +403,7 @@ impl io::Write for Vec { } impl AsRef<[T]> for Vec { + #[inline(always)] fn as_ref(&self) -> &[T] { self.0.as_ref() } diff --git a/crates/swc_allocator/src/vec/serde.rs b/crates/swc_allocator/src/vec/serde.rs new file mode 100644 index 000000000000..e369e7f355f0 --- /dev/null +++ b/crates/swc_allocator/src/vec/serde.rs @@ -0,0 +1,77 @@ +use std::{cmp, fmt, marker::PhantomData, mem}; + +use serde::{ + de::{SeqAccess, Visitor}, + Deserialize, Deserializer, Serialize, Serializer, +}; + +use super::Vec; + +impl Serialize for Vec +where + T: Serialize, +{ + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.as_slice().serialize(serializer) + } +} + +impl<'de, T> Deserialize<'de> for Vec +where + T: Deserialize<'de>, +{ + fn deserialize(deserializer: D) -> Result, D::Error> + where + D: Deserializer<'de>, + { + let visitor = VecVisitor { + marker: PhantomData, + }; + deserializer.deserialize_seq(visitor) + } +} + +struct VecVisitor { + marker: PhantomData, +} + +impl<'de, T> Visitor<'de> for VecVisitor +where + T: Deserialize<'de>, +{ + type Value = Vec; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a sequence") + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: SeqAccess<'de>, + { + let capacity = cautious_size_hint::(seq.size_hint()); + let mut values = Vec::::with_capacity(capacity); + + while let Some(value) = seq.next_element()? { + values.push(value); + } + + Ok(values) + } +} + +pub fn cautious_size_hint(hint: Option) -> usize { + const MAX_PREALLOC_BYTES: usize = 1024 * 1024; + + if mem::size_of::() == 0 { + 0 + } else { + cmp::min( + hint.unwrap_or(0), + MAX_PREALLOC_BYTES / mem::size_of::(), + ) + } +}