From a5a6eb53a56faa8e224f59f5cd967e5075c12edd Mon Sep 17 00:00:00 2001 From: David Sherret Date: Wed, 8 Nov 2023 19:59:20 -0500 Subject: [PATCH] perf(atoms): Introduce `AtomStoreCell` (#8232) --- crates/swc_atoms/src/lib.rs | 15 +++++++++++ crates/swc_ecma_parser/src/lexer/jsx.rs | 9 +++---- crates/swc_ecma_parser/src/lexer/mod.rs | 29 ++++++++++------------ crates/swc_ecma_parser/src/lexer/number.rs | 20 +++++++-------- crates/swc_ecma_parser/src/lexer/util.rs | 4 +-- 5 files changed, 44 insertions(+), 33 deletions(-) diff --git a/crates/swc_atoms/src/lib.rs b/crates/swc_atoms/src/lib.rs index 2b20f40353f..ac1e14a710d 100644 --- a/crates/swc_atoms/src/lib.rs +++ b/crates/swc_atoms/src/lib.rs @@ -247,3 +247,18 @@ impl AtomStore { Atom(self.0.atom(s)) } } + +/// A fast internally mutable cell for [AtomStore]. +#[derive(Default)] +pub struct AtomStoreCell(std::cell::UnsafeCell); + +impl AtomStoreCell { + #[inline] + pub fn atom<'a>(&self, s: impl Into>) -> Atom { + // SAFETY: We can skip the borrow check of RefCell because + // this API enforces a safe contract. It is slightly faster + // to use an UnsafeCell. Note the borrow here is short lived + // only to this block. + unsafe { (*self.0.get()).atom(s) } + } +} diff --git a/crates/swc_ecma_parser/src/lexer/jsx.rs b/crates/swc_ecma_parser/src/lexer/jsx.rs index ebe8fa57eb6..ebd71d6dbe4 100644 --- a/crates/swc_ecma_parser/src/lexer/jsx.rs +++ b/crates/swc_ecma_parser/src/lexer/jsx.rs @@ -47,7 +47,7 @@ impl<'a> Lexer<'a> { }); return Ok(Some(Token::JSXText { - raw: self.atoms.borrow_mut().atom(out), + raw: self.atoms.atom(out), })); } '>' => { @@ -322,10 +322,9 @@ impl<'a> Lexer<'a> { raw.push(quote); - let mut b = self.atoms.borrow_mut(); Ok(Token::Str { - value: b.atom(out), - raw: b.atom(raw), + value: self.atoms.atom(out), + raw: self.atoms.atom(raw), }) } @@ -351,7 +350,7 @@ impl<'a> Lexer<'a> { }); Ok(Token::JSXName { - name: self.atoms.borrow_mut().atom(slice), + name: self.atoms.atom(slice), }) } } diff --git a/crates/swc_ecma_parser/src/lexer/mod.rs b/crates/swc_ecma_parser/src/lexer/mod.rs index 58a2cdbfcad..cd3f03d7949 100644 --- a/crates/swc_ecma_parser/src/lexer/mod.rs +++ b/crates/swc_ecma_parser/src/lexer/mod.rs @@ -5,7 +5,7 @@ use std::{cell::RefCell, char, iter::FusedIterator, rc::Rc}; use either::Either::{Left, Right}; use smallvec::{smallvec, SmallVec}; use smartstring::SmartString; -use swc_atoms::{Atom, AtomStore}; +use swc_atoms::{Atom, AtomStoreCell}; use swc_common::{comments::Comments, input::StringInput, BytePos, Span}; use swc_ecma_ast::{op, AssignOp, EsVersion}; @@ -133,7 +133,7 @@ pub struct Lexer<'a> { buf: Rc>, - atoms: Rc>, + atoms: Rc, } impl FusedIterator for Lexer<'_> {} @@ -767,9 +767,8 @@ impl<'a> Lexer<'a> { fn read_ident_unknown(&mut self) -> LexResult { debug_assert!(self.cur().is_some()); - let (word, _) = self.read_word_as_str_with(|l, s, _, _| { - Word::Ident(IdentLike::Other(l.atoms.borrow_mut().atom(s))) - })?; + let (word, _) = self + .read_word_as_str_with(|l, s, _, _| Word::Ident(IdentLike::Other(l.atoms.atom(s))))?; Ok(Word(word)) } @@ -790,7 +789,7 @@ impl<'a> Lexer<'a> { } } - Word::Ident(IdentLike::Other(l.atoms.borrow_mut().atom(s))) + Word::Ident(IdentLike::Other(l.atoms.atom(s))) })?; // Note: ctx is store in lexer because of this error. @@ -1022,10 +1021,9 @@ impl<'a> Lexer<'a> { l.bump(); - let mut b = l.atoms.borrow_mut(); return Ok(Token::Str { - value: b.atom(&*out), - raw: b.atom(raw), + value: l.atoms.atom(&*out), + raw: l.atoms.atom(raw), }); } '\\' => { @@ -1057,10 +1055,9 @@ impl<'a> Lexer<'a> { l.emit_error(start, SyntaxError::UnterminatedStrLit); - let mut b = l.atoms.borrow_mut(); Ok(Token::Str { - value: b.atom(&*out), - raw: b.atom(raw), + value: l.atoms.atom(&*out), + raw: l.atoms.atom(raw), }) }) } @@ -1108,7 +1105,7 @@ impl<'a> Lexer<'a> { buf.push(c); } - Ok(l.atoms.borrow_mut().atom(&**buf)) + Ok(l.atoms.atom(&**buf)) })?; // input is terminated without following `/` @@ -1129,7 +1126,7 @@ impl<'a> Lexer<'a> { let flags = { match self.cur() { Some(c) if c.is_ident_start() => self - .read_word_as_str_with(|l, s, _, _| l.atoms.borrow_mut().atom(s)) + .read_word_as_str_with(|l, s, _, _| l.atoms.atom(s)) .map(Some), _ => Ok(None), } @@ -1151,7 +1148,7 @@ impl<'a> Lexer<'a> { self.input.bump(); } let s = self.input.uncons_while(|c| !c.is_line_terminator()); - Ok(Some(self.atoms.borrow_mut().atom(s))) + Ok(Some(self.atoms.atom(s))) } fn read_tmpl_token(&mut self, start_of_tpl: BytePos) -> LexResult { @@ -1176,7 +1173,7 @@ impl<'a> Lexer<'a> { // TODO: Handle error return Ok(Token::Template { cooked: cooked.map(Atom::from), - raw: self.atoms.borrow_mut().atom(&*raw), + raw: self.atoms.atom(&*raw), }); } diff --git a/crates/swc_ecma_parser/src/lexer/number.rs b/crates/swc_ecma_parser/src/lexer/number.rs index b99781cb330..b015eaa003c 100644 --- a/crates/swc_ecma_parser/src/lexer/number.rs +++ b/crates/swc_ecma_parser/src/lexer/number.rs @@ -64,7 +64,7 @@ impl<'a> Lexer<'a> { return Ok(Either::Right(( Box::new(s.into_value()), - self.atoms.borrow_mut().atom(&*raw), + self.atoms.atom(&*raw), ))); } @@ -84,9 +84,9 @@ impl<'a> Lexer<'a> { // e.g. `000` is octal if start.0 != self.last_pos().0 - 1 { // `-1` is utf 8 length of `0` - return self.make_legacy_octal(start, 0f64).map(|value| { - Either::Left((value, self.atoms.borrow_mut().atom(&*raw))) - }); + return self + .make_legacy_octal(start, 0f64) + .map(|value| Either::Left((value, self.atoms.atom(&*raw)))); } } else { // strict mode hates non-zero decimals starting with zero. @@ -113,9 +113,9 @@ impl<'a> Lexer<'a> { panic!("failed to parse {} into float using BigInt", val_str) }); - return self.make_legacy_octal(start, val).map(|value| { - Either::Left((value, self.atoms.borrow_mut().atom(&*raw))) - }); + return self + .make_legacy_octal(start, val) + .map(|value| Either::Left((value, self.atoms.atom(&*raw)))); } } } @@ -227,7 +227,7 @@ impl<'a> Lexer<'a> { self.ensure_not_ident()?; - Ok(Either::Left((val, self.atoms.borrow_mut().atom(&*raw_str)))) + Ok(Either::Left((val, self.atoms.atom(&*raw_str)))) } /// Returns `Left(value)` or `Right(BigInt)` @@ -268,13 +268,13 @@ impl<'a> Lexer<'a> { return Ok(Either::Right(( Box::new(s.into_value()), - l.atoms.borrow_mut().atom(&**buf), + l.atoms.atom(&**buf), ))); } l.ensure_not_ident()?; - Ok(Either::Left((val, l.atoms.borrow_mut().atom(&**buf)))) + Ok(Either::Left((val, l.atoms.atom(&**buf)))) }) } diff --git a/crates/swc_ecma_parser/src/lexer/util.rs b/crates/swc_ecma_parser/src/lexer/util.rs index 6e5b515d4be..31f74c94149 100644 --- a/crates/swc_ecma_parser/src/lexer/util.rs +++ b/crates/swc_ecma_parser/src/lexer/util.rs @@ -257,7 +257,7 @@ impl<'a> Lexer<'a> { let cmt = Comment { kind: CommentKind::Line, span: Span::new(start, end, SyntaxContext::empty()), - text: self.atoms.borrow_mut().atom(s), + text: self.atoms.atom(s), }; if is_for_next { @@ -343,7 +343,7 @@ impl<'a> Lexer<'a> { let cmt = Comment { kind: CommentKind::Block, span: Span::new(start, end, SyntaxContext::empty()), - text: self.atoms.borrow_mut().atom(s), + text: self.atoms.atom(s), }; let _ = self.input.peek();