Skip to content

Commit

Permalink
perf(es/lexer): Use jump table for skip_space (#7073)
Browse files Browse the repository at this point in the history
  • Loading branch information
kdy1 committed Mar 13, 2023
1 parent 9c29666 commit f854d51
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 33 deletions.
2 changes: 1 addition & 1 deletion crates/swc_ecma_parser/scripts/instrument/bench.sh
Expand Up @@ -4,4 +4,4 @@ set -eu
export RUST_LOG=off
export MIMALLOC_SHOW_STATS=1

cargo profile instruments --release -t time --features tracing/release_max_level_info --features swc_common/concurrent --features swc_common/parking_lot --bench parser -- --bench --color
cargo profile instruments --release -t time --features tracing/release_max_level_info --features swc_common/concurrent --features swc_common/parking_lot --bench parser -- --bench --color $@
1 change: 1 addition & 0 deletions crates/swc_ecma_parser/src/lexer/mod.rs
Expand Up @@ -34,6 +34,7 @@ mod table;
#[cfg(test)]
mod tests;
pub mod util;
mod whitespace;

pub(crate) type LexResult<T> = Result<T, Error>;

Expand Down
51 changes: 19 additions & 32 deletions crates/swc_ecma_parser/src/lexer/util.rs
Expand Up @@ -12,7 +12,10 @@ use swc_common::{
use swc_ecma_ast::Ident;
use tracing::warn;

use super::{comments_buffer::BufferedComment, input::Input, Char, LexResult, Lexer};
use super::{
comments_buffer::BufferedComment, input::Input, whitespace::SkipWhitespace, Char, LexResult,
Lexer,
};
use crate::{
error::{Error, SyntaxError},
lexer::comments_buffer::BufferedCommentKind,
Expand Down Expand Up @@ -184,18 +187,20 @@ impl<'a> Lexer<'a> {
/// See https://tc39.github.io/ecma262/#sec-white-space
pub(super) fn skip_space<const LEX_COMMENTS: bool>(&mut self) -> LexResult<()> {
loop {
let cur_b = self.input.cur_as_ascii();
let (offset, newline) = {
let mut skip = SkipWhitespace {
input: self.input.as_str(),
newline: false,
offset: 0,
};

if matches!(cur_b, Some(b'\n' | b'\r')) {
self.input.bump();
self.state.had_line_break = true;
continue;
}
skip.scan();

if matches!(cur_b, Some(b'\x09' | b'\x0b' | b'\x0c' | b'\x20' | b'\xa0')) {
self.input.bump();
continue;
}
(skip.offset, skip.newline)
};

self.input.bump_bytes(offset);
self.state.had_line_break |= newline;

if LEX_COMMENTS && self.input.is_byte(b'/') {
if self.peek() == Some('/') {
Expand All @@ -205,34 +210,15 @@ impl<'a> Lexer<'a> {
self.skip_block_comment()?;
continue;
}
break;
}

let c = self.cur();
let c = match c {
Some(v) => v,
None => break,
};

match c {
// white spaces
'\u{feff}' => {}
// line breaks
'\u{2028}' | '\u{2029}' => {
self.state.had_line_break = true;
}

_ if c.is_whitespace() => {}

_ => break,
}

self.bump();
break;
}

Ok(())
}

#[inline(never)]
pub(super) fn skip_line_comment(&mut self, start_skip: usize) {
let start = self.cur_pos();
self.input.bump_bytes(start_skip);
Expand Down Expand Up @@ -282,6 +268,7 @@ impl<'a> Lexer<'a> {
}

/// Expects current char to be '/' and next char to be '*'.
#[inline(never)]
pub(super) fn skip_block_comment(&mut self) -> LexResult<()> {
let start = self.cur_pos();

Expand Down
100 changes: 100 additions & 0 deletions crates/swc_ecma_parser/src/lexer/whitespace.rs
@@ -0,0 +1,100 @@
/// Returns true if it's done
pub(super) type ByteHandler = Option<for<'aa> fn(&mut SkipWhitespace<'aa>) -> usize>;

/// Lookup table for whitespace
static BYTE_HANDLERS: [ByteHandler; 256] = [
// 0 1 2 3 4 5 6 7 8 9 A B C D E F //
___, ___, ___, ___, ___, ___, ___, ___, ___, SPC, NLN, SPC, SPC, NLN, ___, ___, // 0
___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, // 1
SPC, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, // 2
___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, // 3
___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, // 4
___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, // 5
___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, // 6
___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, // 7
UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, // 8
UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, // 9
UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, // A
UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, // B
UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, // C
UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, // D
UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, // E
UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, UNI, // F
];

/// Stop
const ___: ByteHandler = None;

/// Newline
const NLN: ByteHandler = Some(|skip| {
skip.newline = true;

1
});

/// Space
const SPC: ByteHandler = Some(|_| 1);

/// Unicode
const UNI: ByteHandler = Some(|skip| {
let s = unsafe {
// Safety: `skip.offset` is always valid
skip.input.get_unchecked(skip.offset..)
};

let c = unsafe {
// Safety: Byte handlers are called only when `skip.input` is not empty
s.chars().next().unwrap_unchecked()
};

match c {
// white spaces
'\u{feff}' => {}
// line breaks
'\u{2028}' | '\u{2029}' => {
skip.newline = true;
}

_ if c.is_whitespace() => {}

_ => return 0,
}

c.len_utf8()
});

/// API is taked from oxc by Boshen (https://github.com/Boshen/oxc/pull/26)
pub(super) struct SkipWhitespace<'a> {
pub input: &'a str,

/// Total offset
pub offset: usize,

/// Found newline
pub newline: bool,
}

impl SkipWhitespace<'_> {
#[inline(always)]
pub fn scan(&mut self) {
let mut byte;
loop {
byte = match self.input.as_bytes().get(self.offset).copied() {
Some(v) => v,
None => return,
};

let handler = unsafe { *(&BYTE_HANDLERS as *const ByteHandler).offset(byte as isize) };

if let Some(handler) = handler {
let delta = handler(self);
if delta == 0 {
return;
}
self.offset += delta;
} else {
return;
}
}
}
}

1 comment on commit f854d51

@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: f854d51 Previous: 11d4874 Ratio
es/full/bugs-1 362829 ns/iter (± 19163) 311882 ns/iter (± 5390) 1.16
es/full/minify/libraries/antd 1771996901 ns/iter (± 24747418) 1676125955 ns/iter (± 28199276) 1.06
es/full/minify/libraries/d3 359329795 ns/iter (± 18531051) 310312819 ns/iter (± 6997234) 1.16
es/full/minify/libraries/echarts 1359615640 ns/iter (± 19000088) 1261496650 ns/iter (± 15514273) 1.08
es/full/minify/libraries/jquery 100872098 ns/iter (± 2860005) 92240018 ns/iter (± 1100088) 1.09
es/full/minify/libraries/lodash 112584515 ns/iter (± 3516634) 105904743 ns/iter (± 1210531) 1.06
es/full/minify/libraries/moment 56143773 ns/iter (± 1386286) 52442047 ns/iter (± 591676) 1.07
es/full/minify/libraries/react 21337388 ns/iter (± 779084) 19031883 ns/iter (± 183286) 1.12
es/full/minify/libraries/terser 278341765 ns/iter (± 7633880) 245803998 ns/iter (± 4324879) 1.13
es/full/minify/libraries/three 496238802 ns/iter (± 12899179) 444357105 ns/iter (± 6968596) 1.12
es/full/minify/libraries/typescript 3291680614 ns/iter (± 29261715) 3061958376 ns/iter (± 25372413) 1.08
es/full/minify/libraries/victory 757688713 ns/iter (± 18246283) 657696825 ns/iter (± 13818352) 1.15
es/full/minify/libraries/vue 142236724 ns/iter (± 2843522) 129925983 ns/iter (± 1311388) 1.09
es/full/codegen/es3 26191 ns/iter (± 80) 26420 ns/iter (± 282) 0.99
es/full/codegen/es5 26382 ns/iter (± 66) 26507 ns/iter (± 157) 1.00
es/full/codegen/es2015 26345 ns/iter (± 87) 26492 ns/iter (± 85) 0.99
es/full/codegen/es2016 26299 ns/iter (± 57) 26512 ns/iter (± 77) 0.99
es/full/codegen/es2017 26334 ns/iter (± 49) 26590 ns/iter (± 50) 0.99
es/full/codegen/es2018 26302 ns/iter (± 70) 26589 ns/iter (± 123) 0.99
es/full/codegen/es2019 26252 ns/iter (± 61) 26506 ns/iter (± 52) 0.99
es/full/codegen/es2020 26340 ns/iter (± 59) 26491 ns/iter (± 62) 0.99
es/full/all/es3 195824665 ns/iter (± 5889318) 180926987 ns/iter (± 2985911) 1.08
es/full/all/es5 182559376 ns/iter (± 3251090) 173212713 ns/iter (± 3258910) 1.05
es/full/all/es2015 145182796 ns/iter (± 3679409) 135932165 ns/iter (± 1085418) 1.07
es/full/all/es2016 144160105 ns/iter (± 4205450) 134014489 ns/iter (± 2431510) 1.08
es/full/all/es2017 142780354 ns/iter (± 3391322) 131675577 ns/iter (± 2043211) 1.08
es/full/all/es2018 140151140 ns/iter (± 4183230) 128537029 ns/iter (± 2284060) 1.09
es/full/all/es2019 139688717 ns/iter (± 6097224) 125026460 ns/iter (± 1660441) 1.12
es/full/all/es2020 130429226 ns/iter (± 2251981) 118460641 ns/iter (± 788665) 1.10
es/full/parser 531971 ns/iter (± 9054) 537212 ns/iter (± 8157) 0.99
es/full/base/fixer 22513 ns/iter (± 65) 22348 ns/iter (± 29) 1.01
es/full/base/resolver_and_hygiene 81709 ns/iter (± 81) 82189 ns/iter (± 176) 0.99
serialization of ast node 124 ns/iter (± 0) 125 ns/iter (± 0) 0.99
serialization of serde 127 ns/iter (± 0) 127 ns/iter (± 0) 1
css/minify/libraries/bootstrap 29236158 ns/iter (± 194850) 28468297 ns/iter (± 214616) 1.03
css/visitor/compare/clone 2119251 ns/iter (± 30582) 2095838 ns/iter (± 17677) 1.01
css/visitor/compare/visit_mut_span 2322690 ns/iter (± 5677) 2257958 ns/iter (± 50065) 1.03
css/visitor/compare/visit_mut_span_panic 2384962 ns/iter (± 27421) 2332797 ns/iter (± 5178) 1.02
css/visitor/compare/fold_span 3084756 ns/iter (± 23484) 3032901 ns/iter (± 24048) 1.02
css/visitor/compare/fold_span_panic 3230277 ns/iter (± 30361) 3218762 ns/iter (± 28690) 1.00
css/lexer/bootstrap_5_1_3 5145419 ns/iter (± 7578) 5145481 ns/iter (± 13142) 1.00
css/lexer/foundation_6_7_4 4337985 ns/iter (± 4058) 4341172 ns/iter (± 4114) 1.00
css/lexer/tailwind_3_1_1 823792 ns/iter (± 271) 823524 ns/iter (± 539) 1.00
css/parser/bootstrap_5_1_3 22405290 ns/iter (± 170932) 21960790 ns/iter (± 81135) 1.02
css/parser/foundation_6_7_4 17569093 ns/iter (± 100563) 17597065 ns/iter (± 108897) 1.00
css/parser/tailwind_3_1_1 3329762 ns/iter (± 3294) 3337868 ns/iter (± 2897) 1.00
es/codegen/colors 327700 ns/iter (± 185154) 330272 ns/iter (± 186667) 0.99
es/codegen/large 1199983 ns/iter (± 627236) 1230730 ns/iter (± 643705) 0.98
es/codegen/with-parser/colors 48273 ns/iter (± 316) 47005 ns/iter (± 287) 1.03
es/codegen/with-parser/large 523535 ns/iter (± 1399) 519859 ns/iter (± 1099) 1.01
es/minify/libraries/antd 1579091825 ns/iter (± 24467967) 1418795322 ns/iter (± 8023977) 1.11
es/minify/libraries/d3 286016567 ns/iter (± 8100275) 249947576 ns/iter (± 2587571) 1.14
es/minify/libraries/echarts 1168057942 ns/iter (± 25291827) 1088201657 ns/iter (± 21325234) 1.07
es/minify/libraries/jquery 82745047 ns/iter (± 1493053) 78586964 ns/iter (± 893411) 1.05
es/minify/libraries/lodash 103529427 ns/iter (± 2825454) 94549853 ns/iter (± 1406714) 1.09
es/minify/libraries/moment 49520639 ns/iter (± 2074280) 45408642 ns/iter (± 293413) 1.09
es/minify/libraries/react 19294170 ns/iter (± 1194606) 16915967 ns/iter (± 110931) 1.14
es/minify/libraries/terser 234434579 ns/iter (± 8960927) 208958891 ns/iter (± 2685676) 1.12
es/minify/libraries/three 404043847 ns/iter (± 12118984) 364015445 ns/iter (± 4507882) 1.11
es/minify/libraries/typescript 2773898855 ns/iter (± 25165964) 2601891341 ns/iter (± 17268816) 1.07
es/minify/libraries/victory 659887296 ns/iter (± 18313615) 543972776 ns/iter (± 7283181) 1.21
es/minify/libraries/vue 121203267 ns/iter (± 2957558) 116260963 ns/iter (± 1047385) 1.04
es/visitor/compare/clone 2380632 ns/iter (± 34832) 2363822 ns/iter (± 9662) 1.01
es/visitor/compare/visit_mut_span 2742246 ns/iter (± 14937) 2722947 ns/iter (± 6237) 1.01
es/visitor/compare/visit_mut_span_panic 2778673 ns/iter (± 23860) 2777810 ns/iter (± 5652) 1.00
es/visitor/compare/fold_span 3882371 ns/iter (± 17429) 3850058 ns/iter (± 19245) 1.01
es/visitor/compare/fold_span_panic 4049291 ns/iter (± 24483) 3994215 ns/iter (± 11402) 1.01
es/lexer/colors 15783 ns/iter (± 8) 15117 ns/iter (± 32) 1.04
es/lexer/angular 7714476 ns/iter (± 3530) 7367328 ns/iter (± 6762) 1.05
es/lexer/backbone 999478 ns/iter (± 272) 988536 ns/iter (± 1223) 1.01
es/lexer/jquery 5596550 ns/iter (± 4311) 5509121 ns/iter (± 3815) 1.02
es/lexer/jquery mobile 8648497 ns/iter (± 5151) 8494005 ns/iter (± 9114) 1.02
es/lexer/mootools 4433764 ns/iter (± 1656) 4353318 ns/iter (± 2557) 1.02
es/lexer/underscore 842963 ns/iter (± 360) 827178 ns/iter (± 249) 1.02
es/lexer/three 26217572 ns/iter (± 20998) 25772931 ns/iter (± 20752) 1.02
es/lexer/yui 4733599 ns/iter (± 1342) 4602086 ns/iter (± 1994) 1.03
es/parser/colors 29143 ns/iter (± 56) 29785 ns/iter (± 103) 0.98
es/parser/angular 16262931 ns/iter (± 168651) 15479082 ns/iter (± 191160) 1.05
es/parser/backbone 2242607 ns/iter (± 11163) 2253915 ns/iter (± 14684) 0.99
es/parser/jquery 12705016 ns/iter (± 75607) 12325004 ns/iter (± 154262) 1.03
es/parser/jquery mobile 20440847 ns/iter (± 257757) 19674821 ns/iter (± 485728) 1.04
es/parser/mootools 9444322 ns/iter (± 79295) 9301967 ns/iter (± 49418) 1.02
es/parser/underscore 1895760 ns/iter (± 8914) 1904529 ns/iter (± 10898) 1.00
es/parser/three 58635099 ns/iter (± 430471) 56320967 ns/iter (± 1369436) 1.04
es/parser/yui 9390937 ns/iter (± 72660) 9290268 ns/iter (± 53548) 1.01
es/preset-env/usage/builtin_type 141321 ns/iter (± 33797) 142862 ns/iter (± 34536) 0.99
es/preset-env/usage/property 21161 ns/iter (± 125) 20750 ns/iter (± 18) 1.02
es/resolver/typescript 118969330 ns/iter (± 2282912) 114212512 ns/iter (± 1560753) 1.04
es/fixer/typescript 90448406 ns/iter (± 1106277) 85227025 ns/iter (± 613225) 1.06
es/hygiene/typescript 195481189 ns/iter (± 2946311) 181278146 ns/iter (± 1391232) 1.08
es/resolver_with_hygiene/typescript 345864513 ns/iter (± 5306118) 328369692 ns/iter (± 2196592) 1.05
es/visitor/base-perf/module_clone 78635 ns/iter (± 915) 79258 ns/iter (± 1278) 0.99
es/visitor/base-perf/fold_empty 90714 ns/iter (± 1159) 89808 ns/iter (± 1313) 1.01
es/visitor/base-perf/fold_noop_impl_all 89942 ns/iter (± 1678) 91084 ns/iter (± 1275) 0.99
es/visitor/base-perf/fold_noop_impl_vec 91633 ns/iter (± 1323) 91568 ns/iter (± 783) 1.00
es/visitor/base-perf/boxing_boxed_clone 56 ns/iter (± 0) 56 ns/iter (± 0) 1
es/visitor/base-perf/boxing_unboxed_clone 54 ns/iter (± 0) 54 ns/iter (± 0) 1
es/visitor/base-perf/boxing_boxed 103 ns/iter (± 0) 104 ns/iter (± 0) 0.99
es/visitor/base-perf/boxing_unboxed 98 ns/iter (± 0) 98 ns/iter (± 0) 1
es/visitor/base-perf/visit_contains_this 3481 ns/iter (± 83) 3495 ns/iter (± 56) 1.00
es/base/parallel/resolver/typescript 5739869327 ns/iter (± 486937726) 5484842320 ns/iter (± 473209810) 1.05
es/base/parallel/hygiene/typescript 2209141166 ns/iter (± 28059995) 2152710308 ns/iter (± 22805801) 1.03
misc/visitors/time-complexity/time 5 101 ns/iter (± 0) 101 ns/iter (± 0) 1
misc/visitors/time-complexity/time 10 357 ns/iter (± 6) 352 ns/iter (± 0) 1.01
misc/visitors/time-complexity/time 15 705 ns/iter (± 2) 700 ns/iter (± 3) 1.01
misc/visitors/time-complexity/time 20 1315 ns/iter (± 0) 1335 ns/iter (± 8) 0.99
misc/visitors/time-complexity/time 40 6900 ns/iter (± 41) 6865 ns/iter (± 23) 1.01
misc/visitors/time-complexity/time 60 17465 ns/iter (± 48) 17392 ns/iter (± 24) 1.00
es/full-target/es2016 250498 ns/iter (± 310) 250003 ns/iter (± 628) 1.00
es/full-target/es2017 243163 ns/iter (± 295) 240888 ns/iter (± 439) 1.01
es/full-target/es2018 232275 ns/iter (± 442) 230369 ns/iter (± 858) 1.01
es2020_nullish_coalescing 91515 ns/iter (± 208) 90777 ns/iter (± 561) 1.01
es2020_optional_chaining 124310 ns/iter (± 747) 122089 ns/iter (± 789) 1.02
es2022_class_properties 147456 ns/iter (± 359) 145375 ns/iter (± 655) 1.01
es2018_object_rest_spread 95136 ns/iter (± 198) 93408 ns/iter (± 495) 1.02
es2019_optional_catch_binding 84427 ns/iter (± 198) 82784 ns/iter (± 454) 1.02
es2017_async_to_generator 85625 ns/iter (± 179) 83718 ns/iter (± 543) 1.02
es2016_exponentiation 89241 ns/iter (± 213) 87999 ns/iter (± 305) 1.01
es2015_arrow 94009 ns/iter (± 176) 92721 ns/iter (± 583) 1.01
es2015_block_scoped_fn 91527 ns/iter (± 278) 89597 ns/iter (± 584) 1.02
es2015_block_scoping 169949 ns/iter (± 624) 167169 ns/iter (± 1079) 1.02

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

Please sign in to comment.