Skip to content

Commit

Permalink
perf: parallelize side effects optimization (#8781)
Browse files Browse the repository at this point in the history
  • Loading branch information
ahabhgk authored Dec 20, 2024
1 parent 56ee43c commit e5a53a5
Show file tree
Hide file tree
Showing 11 changed files with 256 additions and 279 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,9 @@ impl ModuleDeps {
dep.dependency_type(),
crate::DependencyType::EsmImportSpecifier
) {
let dep_ids = dep.get_ids(module_graph);
ids.extend(dep_ids.into_iter());
// TODO: remove Dependency::get_ids once incremental build chunk graph is stable.
let dep_ids = dep._get_ids(module_graph);
ids.extend(dep_ids.iter().cloned());
}
}

Expand Down Expand Up @@ -142,13 +143,12 @@ impl HasModuleGraphChange {
mod t {
use std::borrow::Cow;

use itertools::Itertools;
use rspack_cacheable::{cacheable, cacheable_dyn, with::Skip};
use rspack_collections::Identifiable;
use rspack_error::{impl_empty_diagnosable_trait, Diagnostic, Result};
use rspack_macros::impl_source_map_config;
use rspack_sources::Source;
use rspack_util::source_map::SourceMapKind;
use rspack_util::{atom::Atom, source_map::SourceMapKind};

use crate::{
compiler::make::cutout::has_module_graph_change::ModuleDeps, AffectType, AsContextDependency,
Expand All @@ -162,12 +162,12 @@ mod t {
#[derive(Debug, Clone)]
struct TestDep {
#[cacheable(with=Skip)]
ids: Vec<&'static str>,
ids: Vec<Atom>,
id: DependencyId,
}

impl TestDep {
fn new(ids: Vec<&'static str>) -> Self {
fn new(ids: Vec<Atom>) -> Self {
Self {
ids,
id: DependencyId::new(),
Expand All @@ -187,12 +187,8 @@ mod t {
&self.id
}

fn get_ids(&self, _mg: &ModuleGraph) -> Vec<swc_core::atoms::Atom> {
self
.ids
.iter()
.map(|id| (*id).to_string().into())
.collect_vec()
fn _get_ids<'a>(&'a self, _mg: &'a ModuleGraph) -> &'a [Atom] {
&self.ids
}

fn could_affect_referencing_module(&self) -> AffectType {
Expand Down Expand Up @@ -354,7 +350,7 @@ mod t {
let mut partial = ModuleGraphPartial::default();
let mut mg = ModuleGraph::new(vec![], Some(&mut partial));

let dep1 = Box::new(TestDep::new(vec!["foo"]));
let dep1 = Box::new(TestDep::new(vec!["foo".into()]));
let dep1_id = *dep1.id();
let module_orig = Box::new(TestModule::new("app", vec![dep1_id]));
let module_orig_id = module_orig.identifier();
Expand All @@ -374,7 +370,7 @@ mod t {

assert_eq!(module_deps_1, module_deps_2);

let dep2 = Box::new(TestDep::new(vec!["bar"]));
let dep2 = Box::new(TestDep::new(vec!["bar".into()]));
let dep2_id = *dep2.id();
let module_orig: &mut TestModule = mg
.module_by_identifier_mut(&module_orig_id)
Expand Down
16 changes: 0 additions & 16 deletions crates/rspack_core/src/dependency/dependency_id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@ use std::sync::atomic::Ordering::Relaxed;

use rspack_cacheable::cacheable;
use serde::Serialize;
use swc_core::ecma::atoms::Atom;

use crate::ModuleGraph;

#[cacheable(hashable)]
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Ord, PartialOrd, Serialize)]
Expand All @@ -17,19 +14,6 @@ impl DependencyId {
pub fn new() -> Self {
Self(DEPENDENCY_ID.fetch_add(1, Relaxed))
}

pub fn set_ids(&self, ids: Vec<Atom>, mg: &mut ModuleGraph) {
mg.set_dep_meta(*self, ids);
}

/// # Panic
/// This method will panic if one of following condition is true:
/// * current dependency id is not belongs to `ESMImportSpecifierDependency` or `ESMExportImportedSpecifierDependency`
/// * current id is not in `ModuleGraph`
pub fn get_ids(&self, mg: &ModuleGraph) -> Vec<Atom> {
let dep = mg.dependency_by_id(self).expect("should have dep");
dep.get_ids(mg)
}
}

impl Default for DependencyId {
Expand Down
5 changes: 3 additions & 2 deletions crates/rspack_core/src/dependency/dependency_trait.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ use dyn_clone::{clone_trait_object, DynClone};
use rspack_cacheable::cacheable_dyn;
use rspack_collections::IdentifierSet;
use rspack_error::Diagnostic;
use rspack_util::atom::Atom;
use rspack_util::ext::AsAny;
use swc_core::ecma::atoms::Atom;

use super::dependency_template::AsDependencyTemplate;
use super::module_dependency::*;
Expand Down Expand Up @@ -89,9 +89,10 @@ pub trait Dependency:
None
}

// TODO: remove this once incremental build chunk graph is stable.
// For now only `ESMImportSpecifierDependency` and
// `ESMExportImportedSpecifierDependency` can use this method
fn get_ids(&self, _mg: &ModuleGraph) -> Vec<Atom> {
fn _get_ids<'a>(&'a self, _mg: &'a ModuleGraph) -> &'a [Atom] {
unreachable!()
}

Expand Down
14 changes: 9 additions & 5 deletions crates/rspack_core/src/dependency/runtime_template.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ pub fn export_from_import(
default_interop: bool,
request: &str,
import_var: &str,
mut export_name: Vec<Atom>,
export_name: &[Atom],
id: &DependencyId,
is_call: bool,
call_context: bool,
Expand All @@ -124,6 +124,7 @@ pub fn export_from_import(

let exports_type = get_exports_type(&compilation.get_module_graph(), id, &module.identifier());

let mut exclude_default_export_name = None;
if default_interop {
if !export_name.is_empty()
&& let Some(first_export_name) = export_name.first()
Expand Down Expand Up @@ -151,7 +152,7 @@ pub fn export_from_import(
}
}
ExportsType::DefaultOnly | ExportsType::DefaultWithNamed => {
export_name = export_name[1..].to_vec();
exclude_default_export_name = Some(export_name[1..].to_vec());
}
_ => {}
}
Expand Down Expand Up @@ -204,6 +205,9 @@ pub fn export_from_import(
}
}

let export_name = exclude_default_export_name
.as_deref()
.unwrap_or(export_name);
if !export_name.is_empty() {
let used_name: Cow<Vec<Atom>> = {
let exports_info = compilation
Expand All @@ -212,7 +216,7 @@ pub fn export_from_import(
let used = exports_info.get_used_name(
&compilation.get_module_graph(),
*runtime,
crate::UsedName::Vec(export_name.clone()),
crate::UsedName::Vec(export_name.to_vec()),
);
if let Some(used) = used {
let used = match used {
Expand All @@ -223,12 +227,12 @@ pub fn export_from_import(
} else {
return format!(
"{} undefined",
to_normal_comment(&property_access(&export_name, 0))
to_normal_comment(&property_access(export_name, 0))
);
}
};
let comment = if *used_name != export_name {
to_normal_comment(&property_access(&export_name, 0))
to_normal_comment(&property_access(export_name, 0))
} else {
String::new()
};
Expand Down
105 changes: 66 additions & 39 deletions crates/rspack_core/src/exports_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -342,14 +342,14 @@ impl ExportsInfo {
pub fn get_nested_exports_info(
&self,
mg: &ModuleGraph,
name: Option<Vec<Atom>>,
name: Option<&[Atom]>,
) -> Option<ExportsInfo> {
if let Some(name) = name
&& !name.is_empty()
{
let info = self.get_read_only_export_info(mg, &name[0]);
if let Some(exports_info) = info.exports_info(mg) {
return exports_info.get_nested_exports_info(mg, Some(name[1..].to_vec()));
return exports_info.get_nested_exports_info(mg, Some(&name[1..]));
} else {
return None;
}
Expand Down Expand Up @@ -1307,43 +1307,6 @@ impl ExportInfo {
false
}

pub fn move_target(
&self,
mg: &mut ModuleGraph,
resolve_filter: ResolveFilterFnTy,
update_original_connection: UpdateOriginalFunctionTy,
) -> Option<ResolvedExportInfoTarget> {
let target = self.get_target_with_filter(mg, resolve_filter)?;
let max_target = self.get_max_target(mg);
let original_target = max_target
.values()
.next()
.expect("should have export info target"); // refer https://github.com/webpack/webpack/blob/ac7e531436b0d47cd88451f497cdfd0dad41535d/lib/ExportsInfo.js#L1388-L1394
if original_target.dependency.as_ref() == Some(&target.dependency)
&& original_target.export == target.export
{
return None;
}
let export_info_mut = self.as_export_info_mut(mg);
export_info_mut.target.clear();
let updated_dependency_id = update_original_connection(&target, mg);

// shadowning `export_info_mut` to reduce `&mut ModuleGraph` borrow life time, since
// `update_original_connection` also needs `&mut ModuleGraph`
let export_info_mut = self.as_export_info_mut(mg);
export_info_mut.target.insert(
None,
ExportInfoTargetValue {
dependency: updated_dependency_id,
export: target.export.clone(),
priority: 0,
},
);

export_info_mut.target_is_set = true;
Some(target)
}

pub fn set_used_conditionally(
&self,
mg: &mut ModuleGraph,
Expand Down Expand Up @@ -1871,6 +1834,18 @@ impl MaybeDynamicTargetExportInfo {
}
}

pub fn get_target_with_filter(
&self,
mg: &ModuleGraph,
resolve_filter: ResolveFilterFnTy,
) -> Option<ResolvedExportInfoTarget> {
match self.get_target_impl(mg, resolve_filter, &mut Default::default()) {
Some(ResolvedExportInfoTargetWithCircular::Circular) => None,
Some(ResolvedExportInfoTargetWithCircular::Target(target)) => Some(target),
None => None,
}
}

fn get_target_impl(
&self,
mg: &ModuleGraph,
Expand All @@ -1894,6 +1869,58 @@ impl MaybeDynamicTargetExportInfo {
}
}
}

fn get_max_target<'a>(
&'a self,
mg: &'a ModuleGraph,
) -> Cow<'a, HashMap<Option<DependencyId>, ExportInfoTargetValue>> {
match self {
MaybeDynamicTargetExportInfo::Static(export_info) => export_info.get_max_target(mg),
MaybeDynamicTargetExportInfo::Dynamic { data, .. } => data.get_max_target(),
}
}
}

impl MaybeDynamicTargetExportInfo {
pub fn can_move_target(
&self,
mg: &ModuleGraph,
resolve_filter: ResolveFilterFnTy,
) -> Option<ResolvedExportInfoTarget> {
let target = self.get_target_with_filter(mg, resolve_filter)?;
let max_target = self.get_max_target(mg);
let original_target = max_target
.values()
.next()
.expect("should have export info target"); // refer https://github.com/webpack/webpack/blob/ac7e531436b0d47cd88451f497cdfd0dad41535d/lib/ExportsInfo.js#L1388-L1394
if original_target.dependency.as_ref() == Some(&target.dependency)
&& original_target.export == target.export
{
return None;
}
Some(target)
}
}

impl ExportInfo {
pub fn do_move_target(
&self,
mg: &mut ModuleGraph,
dependency: DependencyId,
target_export: Option<Vec<Atom>>,
) {
let export_info_mut = self.as_export_info_mut(mg);
export_info_mut.target.clear();
export_info_mut.target.insert(
None,
ExportInfoTargetValue {
dependency: Some(dependency),
export: target_export,
priority: 0,
},
);
export_info_mut.target_is_set = true;
}
}

pub type ResolveFilterFnTy<'a> = Rc<dyn Fn(&ResolvedExportInfoTarget, &ModuleGraph) -> bool + 'a>;
Expand Down
21 changes: 11 additions & 10 deletions crates/rspack_core/src/module_graph/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -928,25 +928,27 @@ impl<'a> ModuleGraph<'a> {
self.loop_partials(|p| p.dep_meta_map.get(id))
}

pub fn set_dep_meta(&mut self, dep_id: DependencyId, ids: Vec<Atom>) {
pub fn set_dependency_extra_meta(&mut self, dep_id: DependencyId, extra: DependencyExtraMeta) {
let Some(active_partial) = &mut self.active else {
panic!("should have active partial");
};
active_partial
.dep_meta_map
.insert(dep_id, DependencyExtraMeta { ids });
active_partial.dep_meta_map.insert(dep_id, extra);
}

pub fn can_update_module(&self, dep_id: &DependencyId, module_id: &ModuleIdentifier) -> bool {
let connection = self
.connection_by_dependency_id(dep_id)
.expect("should have connection");
connection.module_identifier() != module_id
}

pub fn update_module(&mut self, dep_id: &DependencyId, module_id: &ModuleIdentifier) -> bool {
pub fn do_update_module(&mut self, dep_id: &DependencyId, module_id: &ModuleIdentifier) {
let connection = self
.connection_by_dependency_id_mut(dep_id)
.expect("should have connection");
let old_module_identifier = *connection.module_identifier();
if &old_module_identifier == module_id {
return false;
}

connection.set_module_identifier(*module_id);

// remove dep_id from old module mgm incoming connection
let old_mgm = self
.module_graph_module_by_identifier_mut(&old_module_identifier)
Expand All @@ -958,7 +960,6 @@ impl<'a> ModuleGraph<'a> {
.module_graph_module_by_identifier_mut(module_id)
.expect("should exist mgm");
new_mgm.add_incoming_connection(*dep_id);
true
}

pub fn get_exports_info(&self, module_identifier: &ModuleIdentifier) -> ExportsInfo {
Expand Down
Loading

0 comments on commit e5a53a5

Please sign in to comment.