fix(bundler): Improve performance (#1599)

swc_bundler:
 - Skip sorting of statements if a module does not import anything.
This commit is contained in:
강동윤 2021-04-22 19:43:35 +09:00 committed by GitHub
parent 8222cc075d
commit 9a07869c21
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 77 additions and 60 deletions

View File

@ -9,7 +9,7 @@ include = ["Cargo.toml", "build.rs", "src/**/*.rs", "src/**/*.js"]
license = "Apache-2.0/MIT"
name = "swc_bundler"
repository = "https://github.com/swc-project/swc.git"
version = "0.32.4"
version = "0.32.5"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
@ -21,6 +21,7 @@ ahash = "0.7"
anyhow = "1"
crc = "1.8"
dashmap = {version = "3", optional = true}
fxhash = "0.2"
indexmap = "1.6"
is-macro = "0.1"
log = "0.4"

View File

@ -15,10 +15,9 @@ use crate::{
util::{self, CloneMap, ExprExt, IntoParallelIterator, MapWithMut, VarDeclaratorExt},
Bundler, Hook, ModuleRecord,
};
use ahash::AHashMap;
use ahash::AHashSet;
use ahash::RandomState;
use anyhow::{Context, Error};
use fxhash::FxHashMap;
use fxhash::FxHashSet;
use petgraph::graphmap::DiGraphMap;
#[cfg(feature = "concurrent")]
use rayon::iter::ParallelIterator;
@ -33,7 +32,7 @@ pub(super) struct Ctx {
pub graph: DiGraphMap<ModuleId, ()>,
pub merged: CHashSet<ModuleId>,
pub transitive_remap: CloneMap<SyntaxContext, SyntaxContext>,
pub export_stars_in_wrapped: Lock<AHashMap<ModuleId, Vec<SyntaxContext>>>,
pub export_stars_in_wrapped: Lock<FxHashMap<ModuleId, Vec<SyntaxContext>>>,
}
impl<L, R> Bundler<'_, L, R>
@ -541,7 +540,7 @@ where
fn add_var(
injected_ctxt: SyntaxContext,
vars: &mut Vec<(ModuleId, ModuleItem)>,
declared: &mut AHashSet<Id>,
declared: &mut FxHashSet<Id>,
map: &CloneMap<SyntaxContext, SyntaxContext>,
module_id: ModuleId,
id: Id,
@ -579,7 +578,7 @@ where
// If an user import and export from D, the transitive syntax context map
// contains a entry from D to foo because it's reexported and
// the variable (reexported from D) exist because it's imported.
let mut declared_ids = AHashSet::<_, RandomState>::default();
let mut declared_ids = FxHashSet::<_>::default();
for (_, stmt) in entry.iter() {
match stmt {
@ -653,7 +652,7 @@ where
{
let mut map = ctx.export_stars_in_wrapped.lock();
let mut additional_props = AHashMap::<_, Vec<_>>::new();
let mut additional_props = FxHashMap::<_, Vec<_>>::default();
// Handle `export *` for wrapped modules.
for (module_id, ctxts) in map.drain() {
for (_, stmt) in entry.iter() {
@ -858,6 +857,8 @@ where
return;
}
let mut extra = vec![];
module.map_any_items(|_, items| {
let mut new = Vec::with_capacity(items.len() * 11 / 10);
@ -1039,7 +1040,7 @@ where
orig: local,
exported: Some(exported),
});
new.push(ModuleItem::ModuleDecl(ModuleDecl::ExportNamed(
extra.push(ModuleItem::ModuleDecl(ModuleDecl::ExportNamed(
NamedExport {
span: export.span.with_ctxt(injected_ctxt),
specifiers: vec![specifier],
@ -1086,7 +1087,7 @@ where
exported: Some(exported),
});
log::trace!("Exporting `default` with `export default expr`");
new.push(ModuleItem::ModuleDecl(ModuleDecl::ExportNamed(
extra.push(ModuleItem::ModuleDecl(ModuleDecl::ExportNamed(
NamedExport {
span: export.span.with_ctxt(injected_ctxt),
specifiers: vec![specifier],
@ -1158,7 +1159,7 @@ where
type_only: false,
asserts: None,
}));
new.push(export);
extra.push(export);
continue;
}
@ -1190,7 +1191,7 @@ where
exported: Some(exported),
});
new.push(ModuleItem::ModuleDecl(ModuleDecl::ExportNamed(
extra.push(ModuleItem::ModuleDecl(ModuleDecl::ExportNamed(
NamedExport {
span: export.span.with_ctxt(injected_ctxt),
specifiers: vec![specifier],
@ -1254,6 +1255,10 @@ where
new
});
for item in extra {
module.append(info.id, item);
}
// print_hygiene(
// &format!("prepared: {}", info.fm.name),
// &self.cm,

View File

@ -4,8 +4,8 @@ use crate::{
util::IntoParallelIterator, Bundle,
};
use ahash::AHashMap;
use ahash::AHashSet;
use anyhow::{Context, Error};
use fxhash::FxHashSet;
#[cfg(feature = "rayon")]
use rayon::iter::ParallelIterator;
@ -26,9 +26,9 @@ struct InternalEntry {
#[derive(Debug, Default)]
struct State {
synchronously_included: AHashSet<ModuleId>,
dynamic_entries: AHashSet<ModuleId>,
common_libs: AHashSet<ModuleId>,
synchronously_included: FxHashSet<ModuleId>,
dynamic_entries: FxHashSet<ModuleId>,
common_libs: FxHashSet<ModuleId>,
}
impl<L, R> Bundler<'_, L, R>

View File

@ -5,8 +5,9 @@ use crate::{
BundleKind, Bundler, Load, ModuleId, Resolve,
};
use ahash::AHashMap;
use ahash::AHashSet;
use anyhow::{bail, Error};
use fxhash::FxHashMap;
use fxhash::FxHashSet;
use petgraph::{
algo::all_simple_paths,
visit::Bfs,
@ -30,7 +31,7 @@ struct PlanBuilder {
/// `(a, b)`, `(a, c)`,`(b, c)` will be inserted.
///
/// `bool` is `true` if it's connected with exports.
all_deps: AHashMap<(ModuleId, ModuleId), bool>,
all_deps: FxHashMap<(ModuleId, ModuleId), bool>,
/// Graph to compute direct dependencies (direct means it will be merged
/// directly)
@ -38,14 +39,14 @@ struct PlanBuilder {
circular: Circulars,
kinds: AHashMap<ModuleId, BundleKind>,
kinds: FxHashMap<ModuleId, BundleKind>,
}
#[derive(Debug, Default)]
struct Circulars(Vec<AHashSet<ModuleId>>);
struct Circulars(Vec<FxHashSet<ModuleId>>);
impl Circulars {
pub fn get(&self, id: ModuleId) -> Option<&AHashSet<ModuleId>> {
pub fn get(&self, id: ModuleId) -> Option<&FxHashSet<ModuleId>> {
let pos = self.0.iter().position(|set| set.contains(&id))?;
Some(&self.0[pos])
@ -53,7 +54,7 @@ impl Circulars {
}
impl Deref for Circulars {
type Target = Vec<AHashSet<ModuleId>>;
type Target = Vec<FxHashSet<ModuleId>>;
fn deref(&self) -> &Self::Target {
&self.0
@ -76,7 +77,7 @@ impl PlanBuilder {
}
}
let mut set = AHashSet::default();
let mut set = FxHashSet::default();
set.insert(src);
set.insert(imported);
self.circular.push(set);
@ -98,11 +99,11 @@ pub(super) struct Plan {
pub entries: Vec<ModuleId>,
/// key is entry
pub normal: AHashMap<ModuleId, NormalPlan>,
pub normal: FxHashMap<ModuleId, NormalPlan>,
/// key is entry
pub circular: AHashMap<ModuleId, CircularPlan>,
pub circular: FxHashMap<ModuleId, CircularPlan>,
pub bundle_kinds: AHashMap<ModuleId, BundleKind>,
pub bundle_kinds: FxHashMap<ModuleId, BundleKind>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@ -165,7 +166,7 @@ where
self.add_to_graph(&mut builder, module.id, &mut vec![], true);
}
let mut metadata = AHashMap::<ModuleId, Metadata>::default();
let mut metadata = FxHashMap::<ModuleId, Metadata>::default();
// Draw dependency graph to calculte
for (id, _) in &builder.kinds {
@ -205,7 +206,7 @@ where
Ok((self.build_plan(&metadata, builder), graph))
}
fn build_plan(&self, _metadata: &AHashMap<ModuleId, Metadata>, builder: PlanBuilder) -> Plan {
fn build_plan(&self, _metadata: &FxHashMap<ModuleId, Metadata>, builder: PlanBuilder) -> Plan {
let mut plans = Plan::default();
for (id, kind) in builder.kinds.iter() {
@ -218,7 +219,7 @@ where
let root_entry = *root_entry;
let mut bfs = Bfs::new(&builder.direct_deps, root_entry);
let mut done = AHashSet::new();
let mut done = FxHashSet::default();
while let Some(entry) = bfs.next(&builder.direct_deps) {
let mut deps: Vec<_> = builder

View File

@ -1,8 +1,8 @@
use super::Bundler;
use crate::{load::Load, resolve::Resolve};
use ahash::AHashMap;
use ahash::AHashSet;
use anyhow::{Context, Error};
use fxhash::FxHashMap;
use fxhash::FxHashSet;
use retain_mut::RetainMut;
use swc_atoms::{js_word, JsWord};
use swc_common::{sync::Lrc, FileName, Mark, Spanned, SyntaxContext, DUMMY_SP};
@ -94,7 +94,7 @@ pub(super) struct RawImports {
/// function bar() {}
/// foo[bar()]
/// ```
pub forced_ns: AHashSet<JsWord>,
pub forced_ns: FxHashSet<JsWord>,
}
/// This type implements two operation (analysis, deglobbing) to reduce binary
@ -114,13 +114,13 @@ where
/// HashMap from the local identifier of a namespace import to used
/// properties.
usages: AHashMap<Id, Vec<Id>>,
usages: FxHashMap<Id, Vec<Id>>,
/// While deglobbing, we also marks imported identifiers.
imported_idents: AHashMap<Id, SyntaxContext>,
imported_idents: FxHashMap<Id, SyntaxContext>,
deglob_phase: bool,
idents_to_deglob: AHashSet<Id>,
idents_to_deglob: FxHashSet<Id>,
/// `true` while folding objects of a member expression.
///

View File

@ -1,6 +1,6 @@
use crate::id::Id;
use crate::util::MapWithMut;
use ahash::AHashMap;
use fxhash::FxHashMap;
use swc_atoms::js_word;
use swc_ecma_ast::*;
use swc_ecma_utils::private_ident;
@ -10,7 +10,7 @@ use swc_ecma_visit::VisitMutWith;
#[derive(Default)]
pub struct KeywordRenamer {
renamed: AHashMap<Id, Ident>,
renamed: FxHashMap<Id, Ident>,
}
impl KeywordRenamer {

View File

@ -1,5 +1,5 @@
use crate::ModuleId;
use ahash::AHashMap;
use fxhash::FxHashMap;
use retain_mut::RetainMut;
use std::mem::take;
use swc_common::SourceMap;
@ -24,8 +24,8 @@ pub struct Modules {
// We will change this into `Vec<Module>`.
modules: Vec<(ModuleId, Module)>,
prepended_stmts: AHashMap<ModuleId, Vec<ModuleItem>>,
appended_stmts: AHashMap<ModuleId, Vec<ModuleItem>>,
prepended_stmts: FxHashMap<ModuleId, Vec<ModuleItem>>,
appended_stmts: FxHashMap<ModuleId, Vec<ModuleItem>>,
}
impl Modules {

View File

@ -2,8 +2,8 @@ use super::stmt::sort_stmts;
use crate::dep_graph::ModuleGraph;
use crate::modules::Modules;
use crate::ModuleId;
use ahash::AHashSet;
use ahash::RandomState;
use fxhash::FxHashSet;
use indexmap::IndexSet;
use petgraph::algo::all_simple_paths;
use petgraph::EdgeDirection::Outgoing;
@ -96,6 +96,16 @@ fn toposort_real_modules<'a>(
continue;
}
// Skip sorting statements if there is no import.
if ids.len() == 1 {
if graph.neighbors_directed(ids[0], Outgoing).count() == 0 {
chunks.push(Chunk {
stmts: stmts.into_iter().next().unwrap(),
});
continue;
}
}
let stmts = sort_stmts(injected_ctxt, stmts, cm);
// print_hygiene(
@ -117,7 +127,7 @@ fn toposort_real_modules<'a>(
/// Get all modules in a cycle.
fn all_modules_in_circle(
id: ModuleId,
done: &AHashSet<ModuleId>,
done: &FxHashSet<ModuleId>,
already_in_index: &mut IndexSet<ModuleId, RandomState>,
graph: &ModuleGraph,
) -> IndexSet<ModuleId, RandomState> {
@ -159,7 +169,7 @@ fn toposort_real_module_ids<'a>(
mut queue: VecDeque<ModuleId>,
graph: &'a ModuleGraph,
) -> impl 'a + Iterator<Item = Vec<ModuleId>> {
let mut done = AHashSet::<ModuleId>::default();
let mut done = FxHashSet::<ModuleId>::default();
from_fn(move || {
while let Some(id) = queue.pop_front() {

View File

@ -1,5 +1,5 @@
use crate::util::fast_graph::FastDiGraphMap;
use ahash::AHashSet;
use fxhash::FxHashSet;
use petgraph::EdgeDirection;
use petgraph::EdgeDirection::Incoming;
use petgraph::EdgeDirection::Outgoing;
@ -22,7 +22,7 @@ pub(super) struct StmtDepGraph {
inner: FastDiGraphMap<usize, Required>,
/// Read-optimized hashset which contains all direct dependencies and
/// transitive dependencies.
paths: Vec<AHashSet<usize>>,
paths: Vec<FxHashSet<usize>>,
}
impl StmtDepGraph {
@ -44,8 +44,8 @@ impl StmtDepGraph {
self.insert_transitives(a, b);
}
fn calc_transitives(&self, id: usize, dir: EdgeDirection) -> AHashSet<usize> {
let mut set = AHashSet::default();
fn calc_transitives(&self, id: usize, dir: EdgeDirection) -> FxHashSet<usize> {
let mut set = FxHashSet::default();
let mut queue = VecDeque::default();
queue.push_front(id);

View File

@ -2,8 +2,8 @@ use super::graph::Required;
use crate::id::Id;
use crate::modules::sort::graph::StmtDepGraph;
use crate::util::MapWithMut;
use ahash::AHashMap;
use ahash::AHashSet;
use fxhash::FxHashMap;
use fxhash::FxHashSet;
use indexmap::IndexSet;
use petgraph::EdgeDirection::Incoming as Dependants;
use petgraph::EdgeDirection::Outgoing as Dependancies;
@ -103,8 +103,8 @@ fn iter<'a>(
// dbg!(&free);
// dbg!(&module_starts);
let mut moves = AHashSet::new();
let mut done = AHashSet::new();
let mut moves = FxHashSet::default();
let mut done = FxHashSet::default();
let mut stack = VecDeque::new();
stack.extend(module_starts.iter().copied());
@ -343,7 +343,7 @@ fn iter<'a>(
struct FieldInitFinter {
in_object_assign: bool,
in_rhs: bool,
accessed: AHashSet<Id>,
accessed: FxHashSet<Id>,
}
impl FieldInitFinter {
@ -643,8 +643,8 @@ fn calc_deps(new: &[ModuleItem]) -> StmtDepGraph {
log::debug!("Analyzing dependencies between statements");
let mut graph = StmtDepGraph::default();
let mut declared_by = AHashMap::<Id, Vec<usize>>::default();
let mut uninitialized_ids = AHashMap::<Id, usize>::new();
let mut declared_by = FxHashMap::<Id, Vec<usize>>::default();
let mut uninitialized_ids = FxHashMap::<Id, usize>::default();
for (idx, item) in new.iter().enumerate() {
graph.add_node(idx);

View File

@ -1,9 +1,9 @@
use anyhow::{bail, Context, Error};
use reqwest::Url;
use sha1::{Digest, Sha1};
use std::env::current_dir;
use std::io::Write;
use std::{
self, env,
fs::{create_dir_all, read_to_string, write},
path::{Path, PathBuf},
};
@ -39,7 +39,7 @@ fn calc_cache_path(cache_dir: &Path, url: &Url) -> PathBuf {
/// Load url. This method does caching.
fn load_url(url: Url) -> Result<String, Error> {
let cache_dir = PathBuf::from(
env::var("CARGO_MANIFEST_DIR")
current_dir()
.expect("the test requires an environment variable named `CARGO_MANIFEST_DIR`"),
)
.join("tests")

View File

@ -1608,6 +1608,8 @@ function joinGlobs(globs, { extended =false , globstar =false } = {
}
const mod3 = function() {
return {
SEP: SEP,
SEP_PATTERN: SEP_PATTERN,
win32: mod1,
posix: mod2,
basename: basename2,
@ -1625,8 +1627,6 @@ const mod3 = function() {
sep: sep2,
toFileUrl: toFileUrl2,
toNamespacedPath: toNamespacedPath2,
SEP: SEP,
SEP_PATTERN: SEP_PATTERN,
globToRegExp,
isGlob,
normalizeGlob,

View File

@ -1618,6 +1618,8 @@ function joinGlobs(globs, { extended =false , globstar =false } = {
}
const mod3 = function() {
return {
SEP: SEP,
SEP_PATTERN: SEP_PATTERN,
win32: win32,
posix: posix,
basename: basename2,
@ -1635,8 +1637,6 @@ const mod3 = function() {
sep: sep2,
toFileUrl: toFileUrl2,
toNamespacedPath: toNamespacedPath2,
SEP: SEP,
SEP_PATTERN: SEP_PATTERN,
globToRegExp,
isGlob,
normalizeGlob,