Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

perf(incremental): better parallel for side effects plugin rebuild #8848

Merged
merged 2 commits into from
Dec 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@ pub struct SideEffectsDoOptimizeMoveTarget {
pub target_export: Option<Vec<Atom>>,
}

pub type SideEffectsOptimizeArtifact = UkeyMap<DependencyId, SideEffectsDoOptimize>;
pub type SideEffectsOptimizeArtifact = UkeyMap<DependencyId, Option<SideEffectsDoOptimize>>;
117 changes: 61 additions & 56 deletions crates/rspack_plugin_javascript/src/plugin/side_effects_flag_plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,8 @@ use std::rc::Rc;
use std::sync::LazyLock;

use rayon::prelude::*;
use rspack_collections::IdentifierIndexSet;
use rspack_collections::IdentifierMap;
use rspack_collections::IdentifierSet;
use rspack_collections::UkeyMap;
use rspack_core::incremental::IncrementalPasses;
use rspack_core::incremental::Mutation;
use rspack_core::DependencyExtraMeta;
Expand Down Expand Up @@ -667,7 +665,7 @@ async fn nmf_module(
#[plugin_hook(CompilationOptimizeDependencies for SideEffectsFlagPlugin)]
fn optimize_dependencies(&self, compilation: &mut Compilation) -> Result<Option<bool>> {
let logger = compilation.get_logger("rspack.SideEffectsFlagPlugin");
let start = logger.time("update dependencies");
let start = logger.time("update connections");

let mut side_effects_optimize_artifact =
std::mem::take(&mut compilation.side_effects_optimize_artifact);
Expand All @@ -684,6 +682,7 @@ fn optimize_dependencies(&self, compilation: &mut Compilation) -> Result<Option<
})
.collect();

let inner_start = logger.time("prepare connections");
let modules: IdentifierSet = if let Some(mutations) = compilation
.incremental
.mutations_read(IncrementalPasses::SIDE_EFFECTS)
Expand All @@ -695,46 +694,45 @@ fn optimize_dependencies(&self, compilation: &mut Compilation) -> Result<Option<
.is_some()
});

let mut modules: IdentifierSet = mutations.iter().fold(
fn affected_incoming_modules(
module: &ModuleIdentifier,
module_graph: &ModuleGraph,
modules: &mut IdentifierSet,
) {
for connection in module_graph.get_incoming_connections(module) {
let Some(original_module) = connection.original_module_identifier else {
continue;
};
if modules.contains(&original_module) {
continue;
}
let Some(dep) = module_graph.dependency_by_id(&connection.dependency_id) else {
continue;
};
if dep.is::<ESMExportImportedSpecifierDependency>() && modules.insert(original_module) {
affected_incoming_modules(&original_module, module_graph, modules);
}
}
}

let modules: IdentifierSet = mutations.iter().fold(
IdentifierSet::default(),
|mut modules, mutation| match mutation {
Mutation::ModuleBuild { module } => {
if modules.insert(*module) {
affected_incoming_modules(module, &module_graph, &mut modules);
}
modules.extend(
module_graph
.get_outgoing_connections(module)
.map(|connection| *connection.module_identifier()),
);
modules
}
Mutation::ModuleRemove { module } => {
for connection in module_graph.get_incoming_connections(module) {
side_effects_optimize_artifact.remove(&connection.dependency_id);
}
modules
}
_ => modules,
},
);

let mut stack = IdentifierIndexSet::from_iter(modules.iter().copied());
while let Some(module) = stack.pop() {
for connection in module_graph.get_incoming_connections(&module) {
let Some(original_module) = connection.original_module_identifier else {
continue;
};
if modules.contains(&original_module) {
continue;
}
let Some(dep) = module_graph.dependency_by_id(&connection.dependency_id) else {
continue;
};
if dep.is::<ESMExportImportedSpecifierDependency>() {
modules.insert(original_module);
stack.insert(original_module);
}
}
}

let logger = compilation.get_logger("rspack.incremental.sideEffects");
logger.log(format!(
"{} modules are affected, {} in total",
Expand All @@ -746,29 +744,40 @@ fn optimize_dependencies(&self, compilation: &mut Compilation) -> Result<Option<
} else {
module_graph.modules().keys().copied().collect()
};
logger.time_end(inner_start);

let do_optimizes: SideEffectsOptimizeArtifact = modules
let inner_start = logger.time("find optimizable connections");
let artifact: SideEffectsOptimizeArtifact = modules
.par_iter()
.filter(|module| side_effects_state_map[module] == ConnectionState::Bool(false))
.flat_map_iter(|module| {
.flat_map(|module| {
module_graph
.get_incoming_connections(module)
.filter_map(|connection| {
can_optimize_connection(connection, &side_effects_state_map, &module_graph)
})
.collect::<Vec<_>>()
})
.map(|connection| {
(
connection.dependency_id,
can_optimize_connection(connection, &side_effects_state_map, &module_graph),
)
})
.collect();
logger.time_end(inner_start);

let mut do_optimizes: UkeyMap<DependencyId, SideEffectsDoOptimize> = if compilation
let mut do_optimizes: Vec<(DependencyId, SideEffectsDoOptimize)> = if compilation
.incremental
.can_read_mutations(IncrementalPasses::SIDE_EFFECTS)
{
side_effects_optimize_artifact.extend(do_optimizes);
side_effects_optimize_artifact.extend(artifact);
side_effects_optimize_artifact.clone()
} else {
do_optimizes
};
artifact
}
.into_iter()
.filter_map(|(connection, do_optimize)| do_optimize.map(|i| (connection, i)))
.collect();

let inner_start = logger.time("do optimize connections");
let mut do_optimized_count = 0;
while !do_optimizes.is_empty() {
do_optimized_count += do_optimizes.len();
Expand All @@ -788,14 +797,16 @@ fn optimize_dependencies(&self, compilation: &mut Compilation) -> Result<Option<
.filter_map(|(connection, _)| module_graph.connection_by_dependency_id(&connection))
.filter_map(|connection| {
can_optimize_connection(connection, &side_effects_state_map, &module_graph)
.map(|i| (connection.dependency_id, i))
})
.collect();
}
logger.time_end(inner_start);

compilation.side_effects_optimize_artifact = side_effects_optimize_artifact;

logger.log(format!("optimized {do_optimized_count} dependencies"));
logger.time_end(start);
logger.log(format!("optimized {do_optimized_count} connections"));
Ok(None)
}

Expand Down Expand Up @@ -827,7 +838,7 @@ fn can_optimize_connection(
connection: &ModuleGraphConnection,
side_effects_state_map: &IdentifierMap<ConnectionState>,
module_graph: &ModuleGraph,
) -> Option<(DependencyId, SideEffectsDoOptimize)> {
) -> Option<SideEffectsDoOptimize> {
let original_module = connection.original_module_identifier?;
let dependency_id = connection.dependency_id;
let dep = module_graph.dependency_by_id(&dependency_id)?;
Expand Down Expand Up @@ -866,14 +877,11 @@ fn can_optimize_connection(
MaybeDynamicTargetExportInfo::Dynamic { .. } => None,
};

return Some((
dependency_id,
SideEffectsDoOptimize {
ids: processed_ids,
target_module: target.module,
need_move_target,
},
));
return Some(SideEffectsDoOptimize {
ids: processed_ids,
target_module: target.module,
need_move_target,
});
}

if let Some(dep) = dep.downcast_ref::<ESMImportSpecifierDependency>()
Expand Down Expand Up @@ -902,14 +910,11 @@ fn can_optimize_connection(
})
.unwrap_or_else(|| ids[1..].to_vec());

return Some((
dependency_id,
SideEffectsDoOptimize {
ids: processed_ids,
target_module: target.module,
need_move_target: None,
},
));
return Some(SideEffectsDoOptimize {
ids: processed_ids,
target_module: target.module,
need_move_target: None,
});
}

None
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,11 @@ module.exports = {
<t> remove empty chunks: X ms
LOG from rspack.SideEffectsFlagPlugin
optimized 0 dependencies
<t> update dependencies: X ms
<t> prepare connections: X ms
<t> find optimizable connections: X ms
<t> do optimize connections: X ms
<t> update connections: X ms
optimized 0 connections
LOG from rspack.SplitChunksPlugin
<t> prepare module group map: X ms
Expand Down
1 change: 1 addition & 0 deletions website/docs/en/config/experiments.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,7 @@ type Incremental = {
inferAsyncModules?: boolean;
providedExports?: boolean;
dependenciesDiagnostics?: boolean;
sideEffects?: boolean;
buildChunkGraph?: boolean;
moduleIds?: boolean;
chunkIds?: boolean;
Expand Down
1 change: 1 addition & 0 deletions website/docs/zh/config/experiments.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,7 @@ type Incremental = {
inferAsyncModules?: boolean;
providedExports?: boolean;
dependenciesDiagnostics?: boolean;
sideEffects?: boolean;
buildChunkGraph?: boolean;
moduleIds?: boolean;
chunkIds?: boolean;
Expand Down
Loading