Skip to content

Commit 9543119

Browse files
committed
perf(es/preset_env): Use phf for corejs3 entry
1 parent 2ba605b commit 9543119

File tree

10 files changed

+3311
-50
lines changed

10 files changed

+3311
-50
lines changed

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/swc_ecma_preset_env/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ serde = { workspace = true, features = ["derive"], optional = true }
2626
serde_json = { workspace = true }
2727
st-map = { workspace = true }
2828

29+
precomputed-map = { workspace = true }
30+
xxhash-rust = { workspace = true, features = [ "xxh3" ] }
31+
2932
preset_env_base = { version = "3.0.2", path = "../preset_env_base" }
3033
rustc-hash = { workspace = true }
3134
string_enum = { version = "1.0.1", path = "../string_enum" }

crates/swc_ecma_preset_env/benches/polyfills.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,16 @@ fn bench_cases(c: &mut Criterion) {
8181

8282
run(b, SOURCE, Default::default())
8383
});
84+
85+
c.bench_function("es/preset-env/entry/import", |b| {
86+
const SOURCE: &str = r#"
87+
import {} from "aaa.js";
88+
"#;
89+
90+
let mut config = Config::default();
91+
config.mode = Some(swc_ecma_preset_env::Mode::Entry);
92+
run(b, SOURCE, config)
93+
});
8494
}
8595

8696
criterion_group!(benches, bench_cases);

crates/swc_ecma_preset_env/src/corejs3/entry.rs

Lines changed: 33 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,47 @@
11
use std::sync::Arc;
22

33
use indexmap::IndexSet;
4-
use once_cell::sync::Lazy;
54
use preset_env_base::{
65
version::{should_enable, Version},
76
Versions,
87
};
9-
use rustc_hash::{FxBuildHasher, FxHashMap};
8+
use rustc_hash::FxBuildHasher;
109
use swc_atoms::atom;
1110
use swc_common::DUMMY_SP;
1211
use swc_ecma_ast::*;
1312
use swc_ecma_visit::VisitMut;
13+
use std::ops::Range;
14+
use crate::util::SwcXxh3;
1415

1516
use super::{compat::DATA as CORE_JS_COMPAT_DATA, data::MODULES_BY_VERSION};
1617

17-
static ENTRIES: Lazy<FxHashMap<String, Vec<&'static str>>> = Lazy::new(|| {
18-
serde_json::from_str::<FxHashMap<String, Vec<String>>>(include_str!(
19-
"../../data/core-js-compat/entries.json"
20-
))
21-
.expect("failed to parse entries.json from core js 3")
22-
.into_iter()
23-
.map(|(k, v)| {
24-
(
25-
k,
26-
v.into_iter()
27-
.map(|s: String| &*Box::leak(s.into_boxed_str()))
28-
.collect::<Vec<_>>(),
29-
)
30-
})
31-
.collect()
32-
});
18+
include!("../generated/corejs3_entries.rs");
19+
20+
pub struct FeatureSet(Range<u32>);
21+
22+
pub fn entries_get(name: &str) -> Option<FeatureSet> {{
23+
let index = ENTRY_INDEX.get(name)?;
24+
ENTRY_VALUES_LIST
25+
.get(index)
26+
.cloned()
27+
.map(FeatureSet)
28+
}}
29+
30+
impl FeatureSet {
31+
pub fn iter(&self) -> impl ExactSizeIterator<Item = &'static str> {
32+
use precomputed_map::store::AccessSeq;
33+
34+
self.0
35+
.clone()
36+
.map(|idx| ENTRY_VALUES_STRING_ID.index(idx as usize))
37+
.map(|id| {
38+
let offset = id & ((1 << 24) - 1);
39+
let len = id >> 24;
40+
41+
&ENTRY_VALUES_STRING_STORE[(offset as usize)..][..(len as usize)]
42+
})
43+
}
44+
}
3345

3446
#[derive(Debug)]
3547
pub struct Entry {
@@ -68,9 +80,9 @@ impl Entry {
6880
return true;
6981
}
7082

71-
if let Some(features) = ENTRIES.get(src) {
83+
if let Some(features) = entries_get(src) {
7284
self.imports.extend(features.iter().filter(|f| {
73-
let feature = CORE_JS_COMPAT_DATA.get(&***f);
85+
let feature = CORE_JS_COMPAT_DATA.get(*f);
7486

7587
if !*is_any_target {
7688
if let Some(feature) = feature {
@@ -80,7 +92,7 @@ impl Entry {
8092
}
8193
}
8294

83-
if let Some(version) = MODULES_BY_VERSION.get(**f) {
95+
if let Some(version) = MODULES_BY_VERSION.get(f) {
8496
return version <= corejs_version;
8597
}
8698

crates/swc_ecma_preset_env/src/generated/corejs3_entries.rs

Lines changed: 3186 additions & 0 deletions
Large diffs are not rendered by default.

crates/swc_ecma_preset_env/src/generated/corejs3_entries.str

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
es.symboles.symbol.descriptiones.symbol.async-iteratores.symbol.has-instancees.symbol.is-concat-spreadablees.symbol.iteratores.symbol.matches.symbol.match-alles.symbol.replacees.symbol.searches.symbol.specieses.symbol.splites.symbol.to-primitivees.symbol.to-string-tages.symbol.unscopableses.error.causees.error.to-stringes.aggregate-errores.aggregate-error.causees.array.ates.array.concates.array.copy-withines.array.everyes.array.filles.array.filteres.array.findes.array.find-indexes.array.find-lastes.array.find-last-indexes.array.flates.array.flat-mapes.array.for-eaches.array.fromes.array.includeses.array.index-ofes.array.is-arrayes.array.iteratores.array.joines.array.last-index-ofes.array.mapes.array.ofes.array.pushes.array.reducees.array.reduce-rightes.array.reversees.array.slicees.array.somees.array.sortes.array.specieses.array.splicees.array.to-reversedes.array.to-sortedes.array.to-splicedes.array.unscopables.flates.array.unscopables.flat-mapes.array.unshiftes.array.withes.array-buffer.constructores.array-buffer.is-viewes.array-buffer.slicees.data-viewes.array-buffer.detachedes.array-buffer.transferes.array-buffer.transfer-to-fixed-lengthes.date.get-yeares.date.nowes.date.set-yeares.date.to-gmt-stringes.date.to-iso-stringes.date.to-jsones.date.to-primitivees.date.to-stringes.escapees.function.bindes.function.has-instancees.function.namees.global-thises.json.stringifyes.json.to-string-tages.mapes.map.group-byes.math.acoshes.math.asinhes.math.atanhes.math.cbrtes.math.clz32es.math.coshes.math.expm1es.math.froundes.math.hypotes.math.imules.math.log10es.math.log1pes.math.log2es.math.signes.math.sinhes.math.tanhes.math.to-string-tages.math.trunces.number.constructores.number.epsilones.number.is-finitees.number.is-integeres.number.is-nanes.number.is-safe-integeres.number.max-safe-integeres.number.min-safe-integeres.number.parse-floates.number.parse-intes.number.to-exponentiales.number.to-fixedes.number.to-precisiones.object.assignes.object.createes.object.define-getteres.object.define-propertieses.object.define-propertyes.object.define-setteres.object.entrieses.object.freezees.object.from-entrieses.object.get-own-property-descriptores.object.get-own-property-descriptorses.object.get-own-property-nameses.object.get-prototype-ofes.object.group-byes.object.has-ownes.object.ises.object.is-extensiblees.object.is-frozenes.object.is-sealedes.object.keyses.object.lookup-getteres.object.lookup-setteres.object.prevent-extensionses.object.protoes.object.seales.object.set-prototype-ofes.object.to-stringes.object.valueses.parse-floates.parse-intes.promisees.promise.all-settledes.promise.anyes.promise.finallyes.promise.with-resolverses.reflect.applyes.reflect.constructes.reflect.define-propertyes.reflect.delete-propertyes.reflect.getes.reflect.get-own-property-descriptores.reflect.get-prototype-ofes.reflect.hases.reflect.is-extensiblees.reflect.own-keyses.reflect.prevent-extensionses.reflect.setes.reflect.set-prototype-ofes.reflect.to-string-tages.regexp.constructores.regexp.dot-alles.regexp.execes.regexp.flagses.regexp.stickyes.regexp.testes.regexp.to-stringes.setes.set.difference.v2es.set.intersection.v2es.set.is-disjoint-from.v2es.set.is-subset-of.v2es.set.is-superset-of.v2es.set.symmetric-difference.v2es.set.union.v2es.string.at-alternativees.string.code-point-ates.string.ends-withes.string.from-code-pointes.string.includeses.string.is-well-formedes.string.iteratores.string.matches.string.match-alles.string.pad-endes.string.pad-startes.string.rawes.string.repeates.string.replacees.string.replace-alles.string.searches.string.splites.string.starts-withes.string.substres.string.to-well-formedes.string.trimes.string.trim-endes.string.trim-startes.string.anchores.string.biges.string.blinkes.string.boldes.string.fixedes.string.fontcolores.string.fontsizees.string.italicses.string.linkes.string.smalles.string.strikees.string.subes.string.supes.typed-array.float32-arrayes.typed-array.float64-arrayes.typed-array.int8-arrayes.typed-array.int16-arrayes.typed-array.int32-arrayes.typed-array.uint8-arrayes.typed-array.uint8-clamped-arrayes.typed-array.uint16-arrayes.typed-array.uint32-arrayes.typed-array.ates.typed-array.copy-withines.typed-array.everyes.typed-array.filles.typed-array.filteres.typed-array.findes.typed-array.find-indexes.typed-array.find-lastes.typed-array.find-last-indexes.typed-array.for-eaches.typed-array.fromes.typed-array.includeses.typed-array.index-ofes.typed-array.iteratores.typed-array.joines.typed-array.last-index-ofes.typed-array.mapes.typed-array.ofes.typed-array.reducees.typed-array.reduce-rightes.typed-array.reversees.typed-array.setes.typed-array.slicees.typed-array.somees.typed-array.sortes.typed-array.subarrayes.typed-array.to-locale-stringes.typed-array.to-reversedes.typed-array.to-sortedes.typed-array.to-stringes.typed-array.withes.unescapees.weak-mapes.weak-setesnext.aggregate-erroresnext.suppressed-error.constructoresnext.array.from-asyncesnext.array.atesnext.array.filter-outesnext.array.filter-rejectesnext.array.find-lastesnext.array.find-last-indexesnext.array.groupesnext.array.group-byesnext.array.group-by-to-mapesnext.array.group-to-mapesnext.array.is-template-objectesnext.array.last-indexesnext.array.last-itemesnext.array.to-reversedesnext.array.to-sortedesnext.array.to-splicedesnext.array.unique-byesnext.array.withesnext.array-buffer.detachedesnext.array-buffer.transferesnext.array-buffer.transfer-to-fixed-lengthesnext.async-disposable-stack.constructoresnext.async-iterator.constructoresnext.async-iterator.as-indexed-pairsesnext.async-iterator.async-disposeesnext.async-iterator.dropesnext.async-iterator.everyesnext.async-iterator.filteresnext.async-iterator.findesnext.async-iterator.flat-mapesnext.async-iterator.for-eachesnext.async-iterator.fromesnext.async-iterator.indexedesnext.async-iterator.mapesnext.async-iterator.reduceesnext.async-iterator.someesnext.async-iterator.takeesnext.async-iterator.to-arrayesnext.bigint.rangeesnext.composite-keyesnext.composite-symbolesnext.data-view.get-float16esnext.data-view.get-uint8-clampedesnext.data-view.set-float16esnext.data-view.set-uint8-clampedesnext.disposable-stack.constructoresnext.function.demethodizeesnext.function.is-callableesnext.function.is-constructoresnext.function.metadataesnext.function.un-thisesnext.global-thisesnext.iterator.constructoresnext.iterator.as-indexed-pairsesnext.iterator.disposeesnext.iterator.dropesnext.iterator.everyesnext.iterator.filteresnext.iterator.findesnext.iterator.flat-mapesnext.iterator.for-eachesnext.iterator.fromesnext.iterator.indexedesnext.iterator.mapesnext.iterator.rangeesnext.iterator.reduceesnext.iterator.someesnext.iterator.takeesnext.iterator.to-arrayesnext.iterator.to-asyncesnext.json.is-raw-jsonesnext.json.parseesnext.json.raw-jsonesnext.map.delete-allesnext.map.emplaceesnext.map.everyesnext.map.filteresnext.map.findesnext.map.find-keyesnext.map.fromesnext.map.group-byesnext.map.includesesnext.map.key-byesnext.map.key-ofesnext.map.map-keysesnext.map.map-valuesesnext.map.mergeesnext.map.ofesnext.map.reduceesnext.map.someesnext.map.updateesnext.map.update-or-insertesnext.map.upsertesnext.math.clampesnext.math.deg-per-radesnext.math.degreesesnext.math.fscaleesnext.math.f16roundesnext.math.iaddhesnext.math.imulhesnext.math.isubhesnext.math.rad-per-degesnext.math.radiansesnext.math.scaleesnext.math.seeded-prngesnext.math.signbitesnext.math.sum-preciseesnext.math.umulhesnext.number.from-stringesnext.number.rangeesnext.object.has-ownesnext.object.iterate-entriesesnext.object.iterate-keysesnext.object.iterate-valuesesnext.object.group-byesnext.observableesnext.promise.all-settledesnext.promise.anyesnext.promise.tryesnext.promise.with-resolversesnext.reflect.define-metadataesnext.reflect.delete-metadataesnext.reflect.get-metadataesnext.reflect.get-metadata-keysesnext.reflect.get-own-metadataesnext.reflect.get-own-metadata-keysesnext.reflect.has-metadataesnext.reflect.has-own-metadataesnext.reflect.metadataesnext.regexp.escapeesnext.set.add-allesnext.set.delete-allesnext.set.difference.v2esnext.set.differenceesnext.set.everyesnext.set.filteresnext.set.findesnext.set.fromesnext.set.intersection.v2esnext.set.intersectionesnext.set.is-disjoint-from.v2esnext.set.is-disjoint-fromesnext.set.is-subset-of.v2esnext.set.is-subset-ofesnext.set.is-superset-of.v2esnext.set.is-superset-ofesnext.set.joinesnext.set.mapesnext.set.ofesnext.set.reduceesnext.set.someesnext.set.symmetric-difference.v2esnext.set.symmetric-differenceesnext.set.union.v2esnext.set.unionesnext.string.atesnext.string.cookedesnext.string.code-pointsesnext.string.dedentesnext.string.is-well-formedesnext.string.match-allesnext.string.replace-allesnext.string.to-well-formedesnext.symbol.async-disposeesnext.symbol.custom-matcheresnext.symbol.disposeesnext.symbol.is-registered-symbolesnext.symbol.is-registeredesnext.symbol.is-well-known-symbolesnext.symbol.is-well-knownesnext.symbol.matcheresnext.symbol.metadataesnext.symbol.metadata-keyesnext.symbol.observableesnext.symbol.pattern-matchesnext.symbol.replace-allesnext.typed-array.from-asyncesnext.typed-array.atesnext.typed-array.filter-outesnext.typed-array.filter-rejectesnext.typed-array.find-lastesnext.typed-array.find-last-indexesnext.typed-array.group-byesnext.typed-array.to-reversedesnext.typed-array.to-sortedesnext.typed-array.to-splicedesnext.typed-array.unique-byesnext.typed-array.withesnext.uint8-array.from-base64esnext.uint8-array.from-hexesnext.uint8-array.set-from-base64esnext.uint8-array.set-from-hexesnext.uint8-array.to-base64esnext.uint8-array.to-hexesnext.weak-map.delete-allesnext.weak-map.fromesnext.weak-map.ofesnext.weak-map.emplaceesnext.weak-map.upsertesnext.weak-set.add-allesnext.weak-set.delete-allesnext.weak-set.fromesnext.weak-set.ofweb.atobweb.btoaweb.dom-collections.for-eachweb.dom-collections.iteratorweb.dom-exception.constructorweb.dom-exception.stackweb.dom-exception.to-string-tagweb.immediateweb.queue-microtaskweb.selfweb.structured-cloneweb.timersweb.urlweb.url.can-parseweb.url.parseweb.url.to-jsonweb.url-search-paramsweb.url-search-params.deleteweb.url-search-params.hasweb.url-search-params.sizees.aggregate-error.constructores.data-view.constructores.map.constructores.object.get-own-property-symbolses.promise.alles.promise.catches.promise.constructores.promise.racees.promise.rejectes.promise.resolvees.set.constructores.string.trim-leftes.string.trim-rightes.symbol.constructores.symbol.fores.symbol.key-fores.weak-map.constructores.weak-set.constructoresnext.observable.constructoresnext.observable.fromesnext.observable.ofesnext.string.at-alternativeweb.clear-immediateweb.set-immediateweb.set-intervalweb.set-timeoutweb.url-search-params.constructorweb.url.constructor
Binary file not shown.

crates/swc_ecma_preset_env/src/util.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -687,3 +687,17 @@ impl<T> DataMapExt<T> for DataMap<T> {
687687
self
688688
}
689689
}
690+
691+
pub struct PooledStr(u32);
692+
693+
pub struct SwcXxh3;
694+
695+
impl precomputed_map::phf::HashOne for SwcXxh3 {
696+
fn hash_one<T: std::hash::Hash>(k: u64, v: T) -> u64 {
697+
use std::hash::Hasher;
698+
699+
let mut hasher = xxhash_rust::xxh3::Xxh3::with_seed(k);
700+
v.hash(&mut hasher);
701+
hasher.finish()
702+
}
703+
}

xtask/src/es/codegen.rs

Lines changed: 61 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::fs;
22
use std::io::Write;
33
use std::path::Path;
4+
use std::collections::HashMap;
45
use clap::Args;
56
use anyhow::Context;
67

@@ -12,29 +13,34 @@ impl CodegenCmd {
1213
let dir = Path::new(env!("CARGO_MANIFEST_DIR"));
1314
let dir = dir.parent().unwrap();
1415

15-
es_minifier_js_environment_props(dir)
16+
es_preset_env_corejs3_entry(dir)
1617
}
1718
}
1819

19-
fn es_minifier_js_environment_props(dir: &Path) -> anyhow::Result<()> {
20-
let crate_dir = dir.join("crates/swc_ecma_minifier/");
20+
fn es_preset_env_corejs3_entry(dir: &Path) -> anyhow::Result<()> {
21+
use std::collections::BTreeMap;
2122

22-
let domprops = fs::read_to_string(crate_dir.join("src/lists/domprops.json"))?;
23-
let jsprops = fs::read_to_string(crate_dir.join("src/lists/jsprops.json"))?;
23+
let crate_dir = dir.join("crates/swc_ecma_preset_env/");
24+
25+
let entry_data = fs::read_to_string(crate_dir.join("data/core-js-compat/entries.json"))?;
26+
let entry_data: BTreeMap<&str, Vec<&str>> = serde_json::from_str(&entry_data)
27+
.context("failed to parse entries.json from core js 3")?;
28+
let (keys, values): (Vec<_>, Vec<_>) = entry_data.into_iter().unzip();
2429

25-
let props = {
26-
let mut domprops: Vec<&str> = serde_json::from_str(&domprops)
27-
.context("failed to parse domprops.json for property mangler")?;
28-
let mut jsprops: Vec<&str> = serde_json::from_str(&jsprops)
29-
.context("Failed to parse jsprops.json for property mangler")?;
30-
domprops.append(&mut jsprops);
31-
domprops.sort_unstable();
32-
domprops.dedup();
33-
domprops
34-
};
30+
let mut strpool = StrPool::default();
31+
let mut values_strid = Vec::new();
32+
let mut values_index = Vec::new();
33+
for list in values {
34+
let start: u32 = values_strid.len().try_into().unwrap();
35+
for s in list {
36+
values_strid.push(strpool.insert(s));
37+
}
38+
let end: u32 = values_strid.len().try_into().unwrap();
39+
values_index.push(start..end);
40+
}
3541

36-
let mapout = precomputed_map::builder::MapBuilder::new(&props)
37-
.set_ord(&|&x, &y| x.cmp(&y))
42+
let mapout = precomputed_map::builder::MapBuilder::new(&keys)
43+
.set_seed(16416001479773392852)
3844
.set_hash(&|seed, &v| {
3945
use std::hash::{ Hash, Hasher };
4046

@@ -43,33 +49,59 @@ fn es_minifier_js_environment_props(dir: &Path) -> anyhow::Result<()> {
4349
hasher.finish()
4450
})
4551
.set_next_seed(|seed, c| {
46-
dbg!(c);
4752
xxhash_rust::xxh3::xxh3_64_with_seed(&c.to_le_bytes(), seed)
4853
})
4954
.build()?;
55+
println!("seed: {:?}", mapout.seed());
56+
5057
let mut builder = precomputed_map::builder::CodeBuilder::new(
51-
"props".into(),
58+
"corejs3_entries".into(),
5259
"SwcXxh3".into(),
5360
crate_dir.join("src/generated")
5461
);
5562

5663
let _ = fs::create_dir(crate_dir.join("src/generated"));
5764

58-
let k = builder.create_str_seq("PROPS_KEYS".into(), mapout.reorder(&props))?;
59-
mapout.create_map("PROPS".into(), k, &mut builder)?;
65+
let k = builder.create_str_seq("ENTRY_KEYS".into(), mapout.reorder(&keys))?;
66+
builder.create_u32_seq("ENTRY_VALUES_STRING_ID".into(), values_strid.iter().copied())?;
67+
mapout.create_map("ENTRY_INDEX".into(), k, &mut builder)?;
6068

61-
let mut codeout = fs::File::create(crate_dir.join("src/generated/props.rs"))?;
69+
let mut codeout = fs::File::create(crate_dir.join("src/generated/corejs3_entries.rs"))?;
6270
builder.write_to(&mut codeout)?;
6371

72+
fs::write(crate_dir.join("src/generated/corejs3_entries.strpool"), strpool.pool.as_bytes())?;
73+
6474
writeln!(codeout,
65-
r#"
66-
use crate::util::SwcXxh3;
67-
68-
pub fn is_exist(name: &str) -> bool {{
69-
PROPS.get(name).is_some()
70-
}}
71-
"#
75+
"static ENTRY_VALUES_STRING_STORE: &str = include_str!(\"corejs3_entries.strpool\");
76+
static ENTRY_VALUES_LIST: &[Range<u32>] = &["
7277
)?;
78+
for range in mapout.reorder(&values_index) {
79+
writeln!(codeout, "{}..{},", range.start, range.end)?;
80+
}
81+
writeln!(codeout, "];")?;
7382

7483
Ok(())
7584
}
85+
86+
#[derive(Default)]
87+
struct StrPool<'s> {
88+
pool: String,
89+
map: HashMap<&'s str, u32>,
90+
}
91+
92+
impl<'s> StrPool<'s> {
93+
pub fn insert(&mut self, s: &'s str) -> u32 {
94+
*self.map.entry(s).or_insert_with(|| {
95+
let offset = self.pool.len();
96+
self.pool.push_str(&s);
97+
let len: u8 = (self.pool.len() - offset).try_into().unwrap();
98+
let offset: u32 = offset.try_into().unwrap();
99+
100+
if offset > (1 << 24) {
101+
panic!("string too large");
102+
}
103+
104+
offset | (u32::from(len) << 24)
105+
})
106+
}
107+
}

0 commit comments

Comments
 (0)