perf(atoms): Use thin pointer for Atom (#6135)

**Description:**

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

**Related issue:**

 - https://github.com/swc-project/swc/issues/4946.
This commit is contained in:
Donny/강동윤 2022-10-13 15:09:44 +09:00 committed by GitHub
parent a871b13906
commit 9c8ec0ea56
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 73 additions and 23 deletions

View File

@ -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();
}

View File

@ -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)]
@ -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"));
@ -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.
@ -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(),
))
}
}
@ -79,7 +81,11 @@ 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)
}
}
}
@ -87,7 +93,7 @@ macro_rules! impl_eq {
($T:ty) => {
impl PartialEq<$T> for Atom {
fn eq(&self, other: &$T) -> bool {
*self.0 == **other
&**self == &**other
}
}
};
@ -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
}
}
@ -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)
}
}
@ -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.
@ -201,7 +238,7 @@ impl serde::ser::Serialize for Atom {
where
S: Serializer,
{
serializer.serialize_str(&self.0)
serializer.serialize_str(self)
}
}
@ -247,7 +284,7 @@ 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)
}
}
@ -255,7 +292,7 @@ impl rkyv::Archive for Atom {
#[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)
}
}