Skip to content

Commit

Permalink
perf(atoms): Use thin pointer for Atom (#6135)
Browse files Browse the repository at this point in the history
**Description:**

This PR changes the size of `Atom` type to `usize` from 2 * usize`.

**Related issue:**

 - #4946.
  • Loading branch information
kdy1 committed Oct 13, 2022
1 parent a871b13 commit 9c8ec0e
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 23 deletions.
13 changes: 13 additions & 0 deletions crates/swc_atoms/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,19 @@ fn main() {
fn gen(mac_name: &str, type_name: &str, atoms: &[&str]) {
string_cache_codegen::AtomType::new(type_name, &format!("{}!", mac_name))
.atoms(atoms)
.with_atom_doc(
"
[JsWord] is an interned string.
This type should be used instead of [String] for values, because lots of
values are duplicated. For example, if an identifier is named `myVariable`,
there will be lots of identifier usages with the value `myVariable`.
This type
- makes equality comparison faster.
- reduces memory usage.
",
)
.write_to_file(&Path::new(&env::var("OUT_DIR").unwrap()).join(format!("{}.rs", mac_name)))
.unwrap();
}
83 changes: 60 additions & 23 deletions crates/swc_atoms/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,4 @@
//! [JsWord] is an interned string.
//!
//! This type should be used instead of [String] for values, because lots of
//! values are duplicated. For example, if an identifier is named `myVariable`,
//! there will be lots of identifier usages with the value `myVariable`.
//!
//! This type
//! - makes equality comparison faster.
//! - reduces memory usage.
//! See [JsWord] and [Atom]

#![allow(clippy::unreadable_literal)]

Expand All @@ -26,7 +18,7 @@ use std::{
use rkyv_latest as rkyv;
use rustc_hash::FxHashSet;
use serde::Serializer;
use triomphe::Arc;
use triomphe::{Arc, HeaderWithLength, ThinArc};

include!(concat!(env!("OUT_DIR"), "/js_word.rs"));

Expand All @@ -51,8 +43,12 @@ include!(concat!(env!("OUT_DIR"), "/js_word.rs"));
/// - Long texts, which is **not likely to be duplicated**. This does not mean
/// "longer than xx" as this is a type.
/// - Raw values.
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Atom(Arc<str>);
#[derive(Clone)]
pub struct Atom(ThinArc<HeaderWithLength<()>, u8>);

fn _assert_size() {
let _static_assert_size_eq = std::mem::transmute::<Atom, usize>;
}

impl Atom {
/// Creates a bad [Atom] from a string.
Expand All @@ -69,8 +65,14 @@ impl Atom {
pub fn new<S>(s: S) -> Self
where
Arc<str>: From<S>,
S: AsRef<str>,
{
Self(s.into())
let len = s.as_ref().as_bytes().len();

Self(ThinArc::from_header_and_slice(
HeaderWithLength::new((), len),
s.as_ref().as_bytes(),
))
}
}

Expand All @@ -79,15 +81,19 @@ impl Deref for Atom {

#[inline]
fn deref(&self) -> &Self::Target {
&self.0
unsafe {
// Safety: We only consturct this type from valid str

std::str::from_utf8_unchecked(&self.0.slice)
}
}
}

macro_rules! impl_eq {
($T:ty) => {
impl PartialEq<$T> for Atom {
fn eq(&self, other: &$T) -> bool {
*self.0 == **other
&**self == &**other
}
}
};
Expand Down Expand Up @@ -115,7 +121,7 @@ macro_rules! impl_from_deref {

impl PartialEq<str> for Atom {
fn eq(&self, other: &str) -> bool {
&*self.0 == other
&**self == other
}
}

Expand All @@ -141,25 +147,25 @@ impl From<JsWord> for Atom {

impl AsRef<str> for Atom {
fn as_ref(&self) -> &str {
&self.0
self
}
}

impl Borrow<str> for Atom {
fn borrow(&self) -> &str {
&self.0
self
}
}

impl fmt::Debug for Atom {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&*self.0, f)
fmt::Debug::fmt(&**self, f)
}
}

impl Display for Atom {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
Display::fmt(&*self.0, f)
Display::fmt(&**self, f)
}
}

Expand All @@ -169,6 +175,37 @@ impl Default for Atom {
}
}

impl PartialOrd for Atom {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
(**self).partial_cmp(&**other)
}
}

impl Ord for Atom {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
(**self).cmp(&**other)
}
}

impl PartialEq for Atom {
fn eq(&self, other: &Self) -> bool {
// Fast path
if self.0.as_ptr() == other.0.as_ptr() {
return true;
}

(**self).eq(&**other)
}
}

impl Eq for Atom {}

impl Hash for Atom {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
(**self).hash(state)
}
}

/// Generator for an interned strings.
///
/// A lexer is expected to store this in it.
Expand Down Expand Up @@ -201,7 +238,7 @@ impl serde::ser::Serialize for Atom {
where
S: Serializer,
{
serializer.serialize_str(&self.0)
serializer.serialize_str(self)
}
}

Expand Down Expand Up @@ -247,15 +284,15 @@ impl rkyv::Archive for Atom {

#[allow(clippy::unit_arg)]
unsafe fn resolve(&self, pos: usize, resolver: Self::Resolver, out: *mut Self::Archived) {
rkyv::string::ArchivedString::resolve_from_str(&self.0, pos, resolver, out)
rkyv::string::ArchivedString::resolve_from_str(self, pos, resolver, out)
}
}

/// NOT A PUBLIC API
#[cfg(feature = "__rkyv")]
impl<S: rkyv::ser::Serializer + ?Sized> rkyv::Serialize<S> for Atom {
fn serialize(&self, serializer: &mut S) -> Result<Self::Resolver, S::Error> {
String::serialize(&self.0.to_string(), serializer)
String::serialize(&self.to_string(), serializer)
}
}

Expand Down

1 comment on commit 9c8ec0e

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Benchmark

Benchmark suite Current: 9c8ec0e Previous: e63fa28 Ratio
es/full/minify/libraries/antd 1603675356 ns/iter (± 14029069) 1633583514 ns/iter (± 39059441) 0.98
es/full/minify/libraries/d3 325495910 ns/iter (± 8234157) 339443300 ns/iter (± 42600561) 0.96
es/full/minify/libraries/echarts 1298690855 ns/iter (± 27480035) 1317630478 ns/iter (± 38840663) 0.99
es/full/minify/libraries/jquery 79800532 ns/iter (± 2076712) 84310227 ns/iter (± 4663046) 0.95
es/full/minify/libraries/lodash 89565275 ns/iter (± 1428862) 92062748 ns/iter (± 1223163) 0.97
es/full/minify/libraries/moment 47469398 ns/iter (± 2821829) 51296456 ns/iter (± 3007688) 0.93
es/full/minify/libraries/react 16350411 ns/iter (± 107538) 17493414 ns/iter (± 398124) 0.93
es/full/minify/libraries/terser 253837472 ns/iter (± 2246175) 282003818 ns/iter (± 7081517) 0.90
es/full/minify/libraries/three 461201896 ns/iter (± 10312271) 485292978 ns/iter (± 15132985) 0.95
es/full/minify/libraries/typescript 2852472218 ns/iter (± 45911300) 2915922405 ns/iter (± 120090034) 0.98
es/full/minify/libraries/victory 704988731 ns/iter (± 25327643) 705697613 ns/iter (± 24656620) 1.00
es/full/minify/libraries/vue 116828764 ns/iter (± 3485932) 129204183 ns/iter (± 12591683) 0.90
es/full/codegen/es3 33019 ns/iter (± 622) 34824 ns/iter (± 2142) 0.95
es/full/codegen/es5 33073 ns/iter (± 418) 34559 ns/iter (± 3130) 0.96
es/full/codegen/es2015 33040 ns/iter (± 1072) 34501 ns/iter (± 2317) 0.96
es/full/codegen/es2016 33071 ns/iter (± 755) 34591 ns/iter (± 6930) 0.96
es/full/codegen/es2017 32970 ns/iter (± 913) 34612 ns/iter (± 1260) 0.95
es/full/codegen/es2018 33107 ns/iter (± 585) 34297 ns/iter (± 732) 0.97
es/full/codegen/es2019 33020 ns/iter (± 619) 34376 ns/iter (± 774) 0.96
es/full/codegen/es2020 32994 ns/iter (± 1153) 34326 ns/iter (± 944) 0.96
es/full/all/es3 192922840 ns/iter (± 4121497) 199992443 ns/iter (± 18660461) 0.96
es/full/all/es5 182195810 ns/iter (± 4165009) 213713822 ns/iter (± 1585855486) 0.85
es/full/all/es2015 147868051 ns/iter (± 5891954) 177055980 ns/iter (± 17754844) 0.84
es/full/all/es2016 146467759 ns/iter (± 3578955) 166730775 ns/iter (± 12485925) 0.88
es/full/all/es2017 145714243 ns/iter (± 3344438) 161358260 ns/iter (± 12321776) 0.90
es/full/all/es2018 144148375 ns/iter (± 5350289) 186822047 ns/iter (± 20880094) 0.77
es/full/all/es2019 142940160 ns/iter (± 2910512) 180255072 ns/iter (± 18948120) 0.79
es/full/all/es2020 138653000 ns/iter (± 2231903) 178924893 ns/iter (± 22060914) 0.77
es/full/parser 734444 ns/iter (± 26934) 832943 ns/iter (± 226080) 0.88
es/full/base/fixer 26205 ns/iter (± 430) 27714 ns/iter (± 2484) 0.95
es/full/base/resolver_and_hygiene 94013 ns/iter (± 3293) 99198 ns/iter (± 8817) 0.95
serialization of ast node 218 ns/iter (± 3) 218 ns/iter (± 10) 1
serialization of serde 219 ns/iter (± 5) 224 ns/iter (± 9) 0.98

This comment was automatically generated by workflow using github-action-benchmark.

Please sign in to comment.