Skip to content

Commit

Permalink
perf(bundler): Improve performance (#2394)
Browse files Browse the repository at this point in the history
swc_ecma_utils:
 - Make `collect_decls` parallel. (with threshold of 128)

swc_bundler:
 - Make inlining of synthesized variables parallel.
  • Loading branch information
kdy1 authored Oct 11, 2021
1 parent 98cc79a commit b5f8321
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 15 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions bundler/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ dashmap = {version = "4.0.2", optional = true}
indexmap = "1.6"
is-macro = "0.1"
once_cell = "1"
parking_lot = "0.11"
petgraph = "0.5"
radix_fmt = "1"
rayon = {version = "1", optional = true}
Expand Down
31 changes: 21 additions & 10 deletions bundler/src/inline.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{id::Id, modules::Modules};
use crate::{id::Id, modules::Modules, util::Readonly};
use swc_common::{collections::AHashMap, SyntaxContext, DUMMY_SP};
use swc_ecma_ast::*;
use swc_ecma_visit::{
Expand All @@ -14,26 +14,37 @@ pub(crate) struct InlineData {
pub(crate) fn inline(injected_ctxt: SyntaxContext, module: &mut Modules) {
tracing::debug!("Inlining injected variables");

let mut v = Inliner {
injected_ctxt,
data: Default::default(),
};
let mut data = Default::default();

module.visit_with(&mut v);
module.visit_mut_with(&mut v);
{
let mut analyzer = Analyzer {
injected_ctxt,
data: &mut data,
};

module.visit_with(&mut analyzer);
}

let mut v = Inliner { data: data.into() };
module.par_visit_mut_with(&mut v);
module.retain_mut(|_, s| match s {
ModuleItem::Stmt(Stmt::Empty(..)) => false,
_ => true,
});
}

#[derive(Debug)]
#[cfg_attr(feature = "concurrent", derive(Clone))]
struct Inliner {
data: Readonly<InlineData>,
}

struct Analyzer<'a> {
injected_ctxt: SyntaxContext,
data: InlineData,
data: &'a mut InlineData,
}

impl Inliner {
impl Analyzer<'_> {
fn store(&mut self, from: Id, to: Id) {
if let Some(prev) = self.data.ids.insert(from.clone(), to.clone()) {
unreachable!(
Expand All @@ -45,7 +56,7 @@ impl Inliner {
}
}

impl Visit for Inliner {
impl Visit for Analyzer<'_> {
noop_visit_type!();

/// Noop
Expand Down
38 changes: 38 additions & 0 deletions bundler/src/modules/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,44 @@ impl Modules {
.push(item);
}

#[cfg(not(feature = "concurrent"))]
pub(crate) fn par_visit_mut_with<V>(&mut self, v: &mut V)
where
V: VisitMut,
{
self.visit_mut_with(v)
}

#[cfg(feature = "concurrent")]
pub(crate) fn par_visit_mut_with<V>(&mut self, v: &mut V)
where
V: Clone + VisitMut + Send + Sync,
{
use rayon::prelude::*;

let pre = &mut self.prepended_stmts;
let modules = &mut self.modules;
let app = &mut self.appended_stmts;

rayon::scope(|s| {
s.spawn(|_| {
pre.par_iter_mut()
.for_each(|(_, stmts)| stmts.visit_mut_with(&mut v.clone()));
});

s.spawn(|_| {
modules
.par_iter_mut()
.for_each(|(_, stmts)| stmts.visit_mut_with(&mut v.clone()));
});

s.spawn(|_| {
app.par_iter_mut()
.for_each(|(_, stmts)| stmts.visit_mut_with(&mut v.clone()));
});
});
}

pub fn visit_mut_with<V>(&mut self, v: &mut V)
where
V: VisitMut,
Expand Down
6 changes: 6 additions & 0 deletions bundler/src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ use swc_ecma_visit::{noop_visit_mut_type, VisitMut};

pub(crate) mod fast_graph;

#[cfg(feature = "concurrent")]
pub(crate) type Readonly<T> = std::sync::Arc<T>;

#[cfg(not(feature = "concurrent"))]
pub(crate) type Readonly<T> = T;

const TRACK: bool = false;

pub(crate) trait VarDeclaratorExt: Into<VarDeclarator> {
Expand Down
38 changes: 33 additions & 5 deletions ecmascript/utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2084,7 +2084,7 @@ impl VisitMut for IdentReplacer<'_> {

pub struct BindingCollector<I>
where
I: IdentLike + Eq + Hash,
I: IdentLike + Eq + Hash + Send + Sync,
{
only: Option<SyntaxContext>,
bindings: AHashSet<I>,
Expand All @@ -2093,7 +2093,7 @@ where

impl<I> BindingCollector<I>
where
I: IdentLike + Eq + Hash,
I: IdentLike + Eq + Hash + Send + Sync,
{
fn add(&mut self, i: &Ident) {
if let Some(only) = self.only {
Expand All @@ -2108,10 +2108,38 @@ where

impl<I> Visit for BindingCollector<I>
where
I: IdentLike + Eq + Hash,
I: IdentLike + Eq + Hash + Send + Sync,
{
noop_visit_type!();

fn visit_module_items(&mut self, nodes: &[ModuleItem], _: &dyn Node) {
#[cfg(feature = "concurrent")]
if nodes.len() > 128 {
use rayon::prelude::*;
let set = nodes
.par_iter()
.map(|node| {
let mut v = BindingCollector {
only: self.only,
bindings: Default::default(),
is_pat_decl: self.is_pat_decl,
};
node.visit_with(&Invalid { span: DUMMY_SP }, &mut v);
v.bindings
})
.reduce(AHashSet::default, |mut a, b| {
a.extend(b);
a
});
self.bindings.extend(set);
return;
}

for node in nodes {
node.visit_children_with(self)
}
}

fn visit_class_decl(&mut self, node: &ClassDecl, _: &dyn Node) {
node.visit_children_with(self);

Expand Down Expand Up @@ -2182,7 +2210,7 @@ where
/// Collects binding identifiers.
pub fn collect_decls<I, N>(n: &N) -> AHashSet<I>
where
I: IdentLike + Eq + Hash,
I: IdentLike + Eq + Hash + Send + Sync,
N: VisitWith<BindingCollector<I>>,
{
let mut v = BindingCollector {
Expand All @@ -2198,7 +2226,7 @@ where
/// identical to `ctxt`.
pub fn collect_decls_with_ctxt<I, N>(n: &N, ctxt: SyntaxContext) -> AHashSet<I>
where
I: IdentLike + Eq + Hash,
I: IdentLike + Eq + Hash + Send + Sync,
N: VisitWith<BindingCollector<I>>,
{
let mut v = BindingCollector {
Expand Down

0 comments on commit b5f8321

Please sign in to comment.