mirror of
https://github.com/swc-project/swc.git
synced 2024-10-04 04:07:18 +03:00
perf(css/parser): Improve performance of lexer (#4921)
This commit is contained in:
parent
e25b6ed63c
commit
327969d0d9
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -3225,6 +3225,7 @@ name = "swc_css_parser"
|
||||
version = "0.101.4"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"criterion",
|
||||
"lexical",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@ -3232,6 +3233,7 @@ dependencies = [
|
||||
"swc_common",
|
||||
"swc_css_ast",
|
||||
"swc_css_visit",
|
||||
"swc_node_base",
|
||||
"testing",
|
||||
]
|
||||
|
||||
|
@ -23,7 +23,21 @@ swc_common = { version = "0.18.0", path = "../swc_common" }
|
||||
swc_css_ast = { version = "0.93.0", path = "../swc_css_ast" }
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = "0.3"
|
||||
serde = "1.0.127"
|
||||
serde_json = "1.0.66"
|
||||
swc_css_visit = { version = "0.92.0", path = "../swc_css_visit" }
|
||||
swc_node_base = { version = "0.5.0", path = "../swc_node_base" }
|
||||
testing = { version = "0.20.0", path = "../testing" }
|
||||
|
||||
[[bench]]
|
||||
harness = false
|
||||
name = "compare"
|
||||
|
||||
[[bench]]
|
||||
harness = false
|
||||
name = "lexer"
|
||||
|
||||
[[bench]]
|
||||
harness = false
|
||||
name = "parser"
|
||||
|
102
crates/swc_css_parser/benches/compare.rs
Normal file
102
crates/swc_css_parser/benches/compare.rs
Normal file
@ -0,0 +1,102 @@
|
||||
extern crate swc_node_base;
|
||||
|
||||
use criterion::{black_box, criterion_group, criterion_main, Bencher, Criterion};
|
||||
use swc_common::{input::StringInput, FileName, Span, SyntaxContext, DUMMY_SP};
|
||||
use swc_css_ast::Stylesheet;
|
||||
use swc_css_parser::{lexer::Lexer, parser::Parser};
|
||||
use swc_css_visit::{Fold, FoldWith, VisitMut, VisitMutWith};
|
||||
|
||||
static SOURCE: &str = include_str!("files/bootstrap_5_1_3.css");
|
||||
|
||||
fn run<F>(b: &mut Bencher, mut op: F)
|
||||
where
|
||||
F: FnMut(Stylesheet) -> Stylesheet,
|
||||
{
|
||||
let _ = ::testing::run_test(false, |cm, _| {
|
||||
let fm = cm.new_source_file(FileName::Anon, SOURCE.into());
|
||||
|
||||
let lexer = Lexer::new(StringInput::from(&*fm), Default::default());
|
||||
let mut parser = Parser::new(lexer, Default::default());
|
||||
let stylesheet: Stylesheet = parser.parse_all().unwrap();
|
||||
|
||||
b.iter(|| {
|
||||
let stylesheet = stylesheet.clone();
|
||||
let stylesheet = op(stylesheet);
|
||||
|
||||
black_box(stylesheet)
|
||||
});
|
||||
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
||||
fn bench_cases(c: &mut Criterion) {
|
||||
c.bench_function("css/visitor/compare/clone", |b| run(b, |m: Stylesheet| m));
|
||||
|
||||
c.bench_function("css/visitor/compare/visit_mut_span", |b| {
|
||||
struct RespanVisitMut;
|
||||
|
||||
impl VisitMut for RespanVisitMut {
|
||||
fn visit_mut_span(&mut self, span: &mut Span) {
|
||||
*span = DUMMY_SP;
|
||||
}
|
||||
}
|
||||
|
||||
run(b, |mut m| {
|
||||
m.visit_mut_with(&mut RespanVisitMut);
|
||||
|
||||
m
|
||||
});
|
||||
});
|
||||
|
||||
c.bench_function("css/visitor/compare/visit_mut_span_panic", |b| {
|
||||
struct RespanVisitMut;
|
||||
|
||||
impl VisitMut for RespanVisitMut {
|
||||
fn visit_mut_span(&mut self, span: &mut Span) {
|
||||
if span.ctxt != SyntaxContext::empty() {
|
||||
panic!()
|
||||
}
|
||||
|
||||
*span = DUMMY_SP;
|
||||
}
|
||||
}
|
||||
|
||||
run(b, |mut m| {
|
||||
m.visit_mut_with(&mut RespanVisitMut);
|
||||
|
||||
m
|
||||
});
|
||||
});
|
||||
|
||||
c.bench_function("css/visitor/compare/fold_span", |b| {
|
||||
struct RespanFold;
|
||||
|
||||
impl Fold for RespanFold {
|
||||
fn fold_span(&mut self, _: Span) -> Span {
|
||||
DUMMY_SP
|
||||
}
|
||||
}
|
||||
|
||||
run(b, |m| m.fold_with(&mut RespanFold));
|
||||
});
|
||||
|
||||
c.bench_function("css/visitor/compare/fold_span_panic", |b| {
|
||||
struct RespanFold;
|
||||
|
||||
impl Fold for RespanFold {
|
||||
fn fold_span(&mut self, s: Span) -> Span {
|
||||
if s.ctxt != SyntaxContext::empty() {
|
||||
panic!()
|
||||
}
|
||||
|
||||
DUMMY_SP
|
||||
}
|
||||
}
|
||||
|
||||
run(b, |m| m.fold_with(&mut RespanFold));
|
||||
});
|
||||
}
|
||||
|
||||
criterion_group!(benches, bench_cases);
|
||||
criterion_main!(benches);
|
11266
crates/swc_css_parser/benches/files/bootstrap_5_1_3.css
vendored
Normal file
11266
crates/swc_css_parser/benches/files/bootstrap_5_1_3.css
vendored
Normal file
File diff suppressed because it is too large
Load Diff
5669
crates/swc_css_parser/benches/files/foundation_6_7_4.css
Normal file
5669
crates/swc_css_parser/benches/files/foundation_6_7_4.css
Normal file
File diff suppressed because it is too large
Load Diff
1607
crates/swc_css_parser/benches/files/tailwind_3_1_1.css
Normal file
1607
crates/swc_css_parser/benches/files/tailwind_3_1_1.css
Normal file
File diff suppressed because it is too large
Load Diff
38
crates/swc_css_parser/benches/lexer.rs
Normal file
38
crates/swc_css_parser/benches/lexer.rs
Normal file
@ -0,0 +1,38 @@
|
||||
extern crate swc_node_base;
|
||||
|
||||
use criterion::{black_box, criterion_group, criterion_main, Bencher, Criterion};
|
||||
use swc_common::{input::StringInput, FileName};
|
||||
use swc_css_parser::lexer::Lexer;
|
||||
|
||||
fn bench_stylesheet(b: &mut Bencher, src: &'static str) {
|
||||
let _ = ::testing::run_test(false, |cm, _| {
|
||||
let fm = cm.new_source_file(FileName::Anon, src.into());
|
||||
|
||||
b.iter(|| {
|
||||
let lexer = Lexer::new(StringInput::from(&*fm), Default::default());
|
||||
|
||||
for t in lexer {
|
||||
black_box(t);
|
||||
}
|
||||
});
|
||||
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
||||
fn bench_files(c: &mut Criterion) {
|
||||
c.bench_function("css/lexer/bootstrap_5_1_3", |b| {
|
||||
bench_stylesheet(b, include_str!("./files/bootstrap_5_1_3.css"))
|
||||
});
|
||||
|
||||
c.bench_function("css/lexer/foundation_6_7_4", |b| {
|
||||
bench_stylesheet(b, include_str!("./files/foundation_6_7_4.css"))
|
||||
});
|
||||
|
||||
c.bench_function("css/lexer/tailwind_3_1_1", |b| {
|
||||
bench_stylesheet(b, include_str!("./files/tailwind_3_1_1.css"))
|
||||
});
|
||||
}
|
||||
|
||||
criterion_group!(benches, bench_files);
|
||||
criterion_main!(benches);
|
39
crates/swc_css_parser/benches/parser.rs
Normal file
39
crates/swc_css_parser/benches/parser.rs
Normal file
@ -0,0 +1,39 @@
|
||||
extern crate swc_node_base;
|
||||
|
||||
use criterion::{black_box, criterion_group, criterion_main, Bencher, Criterion};
|
||||
use swc_common::{input::StringInput, FileName};
|
||||
use swc_css_parser::{lexer::Lexer, parser::Parser};
|
||||
|
||||
fn bench_stylesheet(b: &mut Bencher, src: &'static str) {
|
||||
let _ = ::testing::run_test(false, |cm, _| {
|
||||
let fm = cm.new_source_file(FileName::Anon, src.into());
|
||||
|
||||
b.iter(|| {
|
||||
let _ = black_box({
|
||||
let lexer = Lexer::new(StringInput::from(&*fm), Default::default());
|
||||
let mut parser = Parser::new(lexer, Default::default());
|
||||
|
||||
parser.parse_all()
|
||||
});
|
||||
});
|
||||
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
||||
fn bench_files(c: &mut Criterion) {
|
||||
c.bench_function("css/parser/bootstrap_5_1_3", |b| {
|
||||
bench_stylesheet(b, include_str!("./files/bootstrap_5_1_3.css"))
|
||||
});
|
||||
|
||||
c.bench_function("css/parser/foundation_6_7_4", |b| {
|
||||
bench_stylesheet(b, include_str!("./files/foundation_6_7_4.css"))
|
||||
});
|
||||
|
||||
c.bench_function("css/parser/tailwind_3_1_1", |b| {
|
||||
bench_stylesheet(b, include_str!("./files/tailwind_3_1_1.css"))
|
||||
});
|
||||
}
|
||||
|
||||
criterion_group!(benches, bench_files);
|
||||
criterion_main!(benches);
|
File diff suppressed because it is too large
Load Diff
@ -6,7 +6,7 @@ use swc_css_ast::{Token, TokenAndSpan, Tokens};
|
||||
use super::PResult;
|
||||
use crate::error::{Error, ErrorKind};
|
||||
|
||||
pub trait ParserInput {
|
||||
pub trait ParserInput: Iterator<Item = TokenAndSpan> {
|
||||
type State: Debug;
|
||||
|
||||
fn next(&mut self) -> PResult<TokenAndSpan>;
|
||||
@ -73,7 +73,7 @@ where
|
||||
self.cur()?;
|
||||
|
||||
if self.peeked.is_none() {
|
||||
self.peeked = Some(self.input.next()?);
|
||||
self.peeked = Some(ParserInput::next(&mut self.input)?);
|
||||
}
|
||||
|
||||
Ok(self.peeked.as_ref().map(|v| &v.token))
|
||||
@ -109,7 +109,7 @@ where
|
||||
}
|
||||
|
||||
if self.cur.is_none() {
|
||||
let res = self.input.next();
|
||||
let res = ParserInput::next(&mut self.input);
|
||||
|
||||
if let Err(err) = &res {
|
||||
if let ErrorKind::Eof = err.kind() {
|
||||
@ -159,6 +159,7 @@ pub struct TokensState {
|
||||
idx: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TokensInput<'a> {
|
||||
tokens: &'a Tokens,
|
||||
idx: usize,
|
||||
@ -184,7 +185,7 @@ impl<'a> TokensInput<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl ParserInput for TokensInput<'_> {
|
||||
impl<'a> ParserInput for TokensInput<'a> {
|
||||
type State = TokensState;
|
||||
|
||||
fn next(&mut self) -> PResult<TokenAndSpan> {
|
||||
@ -206,3 +207,20 @@ impl ParserInput for TokensInput<'_> {
|
||||
self.idx = state.idx;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for TokensInput<'a> {
|
||||
type Item = TokenAndSpan;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let token_and_span = ParserInput::next(self);
|
||||
|
||||
match token_and_span {
|
||||
Ok(token_and_span) => {
|
||||
return Some(token_and_span);
|
||||
}
|
||||
Err(..) => {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ fn tokens_input(input: PathBuf) {
|
||||
|
||||
let mut tokens = vec![];
|
||||
|
||||
while let Ok(t) = lexer.next() {
|
||||
while let Ok(t) = ParserInput::next(&mut lexer) {
|
||||
tokens.push(t);
|
||||
}
|
||||
Tokens {
|
||||
@ -85,7 +85,8 @@ fn test_pass(input: PathBuf, config: ParserConfig) {
|
||||
};
|
||||
|
||||
loop {
|
||||
let res = lexer.next();
|
||||
let res = ParserInput::next(&mut lexer);
|
||||
|
||||
match res {
|
||||
Ok(t) => {
|
||||
tokens.tokens.push(t);
|
||||
@ -196,7 +197,8 @@ fn recovery(input: PathBuf) {
|
||||
};
|
||||
|
||||
loop {
|
||||
let res = lexer.next();
|
||||
let res = ParserInput::next(&mut lexer);
|
||||
|
||||
match res {
|
||||
Ok(t) => {
|
||||
tokens.tokens.push(t);
|
||||
|
Loading…
Reference in New Issue
Block a user