Skip to content

Commit 7b241a8

Browse files
committed
perf: prepare module outgoings for code splitting
1 parent 0616a9d commit 7b241a8

File tree

4 files changed

+159
-81
lines changed

4 files changed

+159
-81
lines changed

crates/rspack_core/src/build_chunk_graph/code_splitter.rs

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use std::{
77
use indexmap::{IndexMap as RawIndexMap, IndexSet as RawIndexSet};
88
use itertools::Itertools;
99
use num_bigint::BigUint;
10+
use rayon::prelude::*;
1011
use rspack_collections::{
1112
impl_item_ukey, Database, DatabaseItem, IdentifierHasher, IdentifierIndexSet, IdentifierMap,
1213
Ukey, UkeyIndexMap, UkeyIndexSet, UkeyMap, UkeySet,
@@ -674,15 +675,27 @@ Or do you want to use the entrypoints '{name}' and '{runtime}' independently on
674675
let mut input_entrypoints_and_modules: UkeyIndexMap<ChunkGroupUkey, Vec<ModuleIdentifier>> =
675676
UkeyIndexMap::default();
676677
let mut assign_depths_map = IdentifierMap::default();
678+
let outgoings = {
679+
let mg = compilation.get_module_graph();
680+
let all_modules = mg.modules();
681+
all_modules
682+
.keys()
683+
.par_bridge()
684+
.map(|mid| {
685+
(
686+
*mid,
687+
mg.get_outgoing_connections(mid)
688+
.cloned()
689+
.collect::<Vec<_>>(),
690+
)
691+
})
692+
.collect::<IdentifierMap<_>>()
693+
};
677694

678695
let entries = compilation.entries.keys().cloned().collect::<Vec<_>>();
679696
for name in &entries {
680697
let (entry_point, modules) = self.prepare_entry_input(name, compilation)?;
681-
assign_depths(
682-
&mut assign_depths_map,
683-
&compilation.get_module_graph(),
684-
modules.iter(),
685-
);
698+
assign_depths(&mut assign_depths_map, &modules, &outgoings);
686699
input_entrypoints_and_modules.insert(entry_point, modules);
687700
}
688701

crates/rspack_core/src/build_chunk_graph/new_code_splitter.rs

Lines changed: 131 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,10 @@
1-
use std::{
2-
borrow::Cow,
3-
hash::BuildHasherDefault,
4-
iter::once,
5-
sync::{atomic::AtomicU32, Arc},
6-
};
1+
use std::{borrow::Cow, hash::BuildHasherDefault, iter::once, sync::atomic::AtomicU32};
72

83
use indexmap::IndexSet;
94
use rayon::prelude::*;
105
use rspack_collections::{
11-
DatabaseItem, IdentifierDashMap, IdentifierHasher, IdentifierIndexMap, IdentifierIndexSet,
12-
IdentifierMap, IdentifierSet, Ukey, UkeyMap,
6+
DatabaseItem, IdentifierHasher, IdentifierIndexMap, IdentifierIndexSet, IdentifierMap,
7+
IdentifierSet, Ukey, UkeyMap,
138
};
149
use rspack_error::{error, Diagnostic, Result};
1510
use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet};
@@ -28,7 +23,7 @@ use crate::{
2823

2924
type ModuleDeps = HashMap<
3025
RuntimeSpec,
31-
IdentifierDashMap<Arc<(Vec<ModuleIdentifier>, Vec<AsyncDependenciesBlockIdentifier>)>>,
26+
IdentifierMap<(Vec<ModuleIdentifier>, Vec<AsyncDependenciesBlockIdentifier>)>,
3227
>;
3328

3429
static NEXT_CACHE_UKEY: AtomicU32 = AtomicU32::new(0);
@@ -194,7 +189,6 @@ impl CreateChunkRoot {
194189

195190
fn create(&self, splitter: &CodeSplitter, compilation: &Compilation) -> Vec<ChunkDesc> {
196191
let module_graph = compilation.get_module_graph();
197-
let module_graph_cache = &compilation.module_graph_cache_artifact;
198192

199193
match self {
200194
CreateChunkRoot::Entry(entry, data, runtime) => {
@@ -235,7 +229,7 @@ impl CreateChunkRoot {
235229
)
236230
.filter_map(|dep_id| module_graph.module_identifier_by_dependency_id(dep_id))
237231
{
238-
splitter.fill_chunk_modules(*m, runtime, &module_graph, module_graph_cache, &mut ctx);
232+
splitter.fill_chunk_modules(*m, runtime, &module_graph, &mut ctx);
239233
}
240234

241235
vec![ChunkDesc::Entry(Box::new(EntryChunkDesc {
@@ -289,7 +283,7 @@ impl CreateChunkRoot {
289283
continue;
290284
};
291285

292-
splitter.fill_chunk_modules(*m, runtime, &module_graph, module_graph_cache, &mut ctx);
286+
splitter.fill_chunk_modules(*m, runtime, &module_graph, &mut ctx);
293287
}
294288

295289
if let Some(group_option) = block.get_group_options()
@@ -383,7 +377,7 @@ impl CodeSplitter {
383377

384378
fn invalidate_outgoing_cache(&mut self, module: ModuleIdentifier) {
385379
// refresh module traversal result in the last compilation
386-
for map in self.module_deps.values() {
380+
for map in self.module_deps.values_mut() {
387381
map.remove(&module);
388382
}
389383
}
@@ -428,6 +422,10 @@ impl CodeSplitter {
428422
fn analyze_module_graph(
429423
&mut self,
430424
compilation: &mut Compilation,
425+
prepared_outgoings: &IdentifierMap<(
426+
IdentifierIndexMap<Vec<ModuleGraphConnection>>,
427+
Vec<AsyncDependenciesBlockIdentifier>,
428+
)>,
431429
) -> Result<Vec<CreateChunkRoot>> {
432430
// determine runtime and chunkLoading
433431
let mut entry_runtime: std::collections::HashMap<&str, RuntimeSpec, rustc_hash::FxBuildHasher> =
@@ -500,12 +498,15 @@ impl CodeSplitter {
500498
continue;
501499
}
502500

503-
let guard =
504-
self.outgoings_modules(&module, runtime.as_ref(), &module_graph, module_graph_cache);
505-
let (modules, blocks) = guard.as_ref();
506-
let blocks = blocks.clone();
501+
let (modules, blocks) = self.outgoings_modules(
502+
&module,
503+
runtime.as_ref(),
504+
&module_graph,
505+
module_graph_cache,
506+
prepared_outgoings,
507+
);
507508
for m in modules {
508-
stack.push((*m, runtime.clone(), chunk_loading));
509+
stack.push((m, runtime.clone(), chunk_loading));
509510
}
510511

511512
for block_id in blocks {
@@ -742,40 +743,41 @@ impl CodeSplitter {
742743
.unwrap_or_else(|| panic!("should have module ordinal: {m}"))
743744
}
744745

745-
pub fn outgoings_modules(
746+
pub fn get_outgoings_modules(
746747
&self,
747748
module: &ModuleIdentifier,
748749
runtime: &RuntimeSpec,
749-
module_graph: &ModuleGraph,
750-
module_graph_cache: &ModuleGraphCacheArtifact,
751-
) -> Arc<(Vec<ModuleIdentifier>, Vec<AsyncDependenciesBlockIdentifier>)> {
750+
) -> Option<(Vec<ModuleIdentifier>, Vec<AsyncDependenciesBlockIdentifier>)> {
752751
let module_map = self.module_deps.get(runtime).expect("should have value");
752+
module_map.get(module).cloned()
753+
}
753754

754-
let guard = module_map.get(module);
755-
if let Some(ref_value) = guard {
756-
return ref_value.clone();
755+
pub fn outgoings_modules(
756+
&mut self,
757+
module: &ModuleIdentifier,
758+
runtime: &RuntimeSpec,
759+
module_graph: &ModuleGraph,
760+
module_graph_cache: &ModuleGraphCacheArtifact,
761+
prepared_outgoings: &IdentifierMap<(
762+
IdentifierIndexMap<Vec<ModuleGraphConnection>>,
763+
Vec<AsyncDependenciesBlockIdentifier>,
764+
)>,
765+
) -> (Vec<ModuleIdentifier>, Vec<AsyncDependenciesBlockIdentifier>) {
766+
if let Some(ref_value) = self
767+
.module_deps
768+
.get_mut(runtime)
769+
.expect("should have value")
770+
.get(module)
771+
{
772+
return (ref_value.0.clone(), ref_value.1.clone());
757773
}
758774

759-
let mut outgoings = IdentifierIndexMap::<Vec<&ModuleGraphConnection>>::default();
760-
let m = module_graph
761-
.module_by_identifier(module)
762-
.expect("should have module");
763-
764-
m.get_dependencies()
765-
.iter()
766-
.filter(|dep_id| {
767-
module_graph
768-
.dependency_by_id(dep_id)
769-
.expect("should have dep")
770-
.as_module_dependency()
771-
.is_none_or(|module_dep| !module_dep.weak())
772-
})
773-
.filter_map(|dep| module_graph.connection_by_dependency_id(dep))
774-
.map(|conn| (conn.module_identifier(), conn))
775-
.for_each(|(module, conn)| outgoings.entry(*module).or_default().push(conn));
775+
let (outgoings, blocks) = prepared_outgoings
776+
.get(module)
777+
.expect("should have outgoings");
776778

777779
let mut modules = IdentifierIndexSet::default();
778-
let mut blocks = m.get_blocks().to_vec();
780+
let mut blocks = blocks.clone();
779781

780782
'outer: for (m, conns) in outgoings.iter() {
781783
for conn in conns {
@@ -786,19 +788,29 @@ impl CodeSplitter {
786788
continue 'outer;
787789
}
788790
crate::ConnectionState::TransitiveOnly => {
789-
let transitive = self.outgoings_modules(m, runtime, module_graph, module_graph_cache);
790-
let (extra_modules, extra_blocks) = transitive.as_ref();
791-
modules.extend(extra_modules.iter().copied());
792-
blocks.extend(extra_blocks.iter().copied());
791+
let (extra_modules, extra_blocks) = self.outgoings_modules(
792+
m,
793+
runtime,
794+
module_graph,
795+
module_graph_cache,
796+
prepared_outgoings,
797+
);
798+
modules.extend(extra_modules);
799+
blocks.extend(extra_blocks);
793800
}
794801
crate::ConnectionState::Active(false) => {}
795802
crate::ConnectionState::CircularConnection => {}
796803
}
797804
}
798805
}
799806

800-
module_map.insert(*module, Arc::new((modules.into_iter().collect(), blocks)));
801-
module_map.get(module).expect("have value").clone()
807+
let module_map = self
808+
.module_deps
809+
.get_mut(runtime)
810+
.expect("should have value");
811+
let modules = modules.into_iter().collect::<Vec<_>>();
812+
module_map.insert(*module, (modules.clone(), blocks.clone()));
813+
(modules, blocks)
802814
}
803815

804816
// insert static dependencies into a set
@@ -807,7 +819,6 @@ impl CodeSplitter {
807819
target_module: ModuleIdentifier,
808820
runtime: &RuntimeSpec,
809821
module_graph: &ModuleGraph,
810-
module_graph_cache: &ModuleGraphCacheArtifact,
811822
ctx: &mut FillCtx,
812823
) {
813824
enum Task {
@@ -843,13 +854,16 @@ impl CodeSplitter {
843854
value
844855
});
845856

846-
let guard =
847-
self.outgoings_modules(&target_module, runtime, module_graph, module_graph_cache);
848-
let (outgoing_modules, blocks) = guard.as_ref();
849-
let mut outgoing_modules = outgoing_modules.clone();
857+
let Some((mut outgoing_modules, blocks)) =
858+
self.get_outgoings_modules(&target_module, runtime)
859+
else {
860+
// not exists when error occurs
861+
// just skip it and the errors will be reported in stats
862+
continue;
863+
};
850864

851865
if ctx.chunk_loading {
852-
ctx.out_goings.extend(blocks.clone());
866+
ctx.out_goings.extend(blocks);
853867
} else {
854868
let modules = blocks
855869
.iter()
@@ -926,8 +940,6 @@ impl CodeSplitter {
926940
}
927941

928942
let mut visited = HashSet::default();
929-
let module_graph = compilation.get_module_graph();
930-
let module_graph_cache = &compilation.module_graph_cache_artifact;
931943

932944
queue.reverse();
933945

@@ -971,8 +983,11 @@ impl CodeSplitter {
971983
if !self.module_deps.contains_key(runtime) {
972984
self.module_deps.insert(runtime.clone(), Default::default());
973985
}
974-
let guard = self.outgoings_modules(&m, runtime, &module_graph, module_graph_cache);
975-
let (modules, blocks) = guard.as_ref();
986+
let Some((modules, blocks)) = self.get_outgoings_modules(&m, runtime) else {
987+
// not exists when error occurs
988+
// just skip it and the errors will be reported in stats
989+
continue;
990+
};
976991

977992
for m in modules.iter().rev() {
978993
queue.push(Task::Enter((*m, runtime)));
@@ -981,9 +996,9 @@ impl CodeSplitter {
981996
for block_id in blocks {
982997
if let Some(chunk_group) = compilation
983998
.chunk_graph
984-
.get_block_chunk_group(block_id, &compilation.chunk_group_by_ukey)
999+
.get_block_chunk_group(&block_id, &compilation.chunk_group_by_ukey)
9851000
{
986-
queue_delay.push(Task::Group(chunk_group.ukey(), *block_id));
1001+
queue_delay.push(Task::Group(chunk_group.ukey(), block_id));
9871002
}
9881003
}
9891004
}
@@ -1033,10 +1048,47 @@ impl CodeSplitter {
10331048
ukey
10341049
}
10351050

1051+
fn prepare_outgoings(
1052+
&self,
1053+
compilation: &Compilation,
1054+
) -> IdentifierMap<(
1055+
IdentifierIndexMap<Vec<ModuleGraphConnection>>,
1056+
Vec<AsyncDependenciesBlockIdentifier>,
1057+
)> {
1058+
let module_graph = compilation.get_module_graph();
1059+
let modules = module_graph.modules().keys().copied().collect::<Vec<_>>();
1060+
modules
1061+
.into_par_iter()
1062+
.map(|mid| {
1063+
let mut outgoings = IdentifierIndexMap::<Vec<ModuleGraphConnection>>::default();
1064+
let m = module_graph
1065+
.module_by_identifier(&mid)
1066+
.expect("should have module");
1067+
let blocks = m.get_blocks().to_vec();
1068+
m.get_dependencies()
1069+
.iter()
1070+
.filter(|dep_id| {
1071+
module_graph
1072+
.dependency_by_id(dep_id)
1073+
.expect("should have dep")
1074+
.as_module_dependency()
1075+
.is_none_or(|module_dep| !module_dep.weak())
1076+
})
1077+
.filter_map(|dep| module_graph.connection_by_dependency_id(dep))
1078+
.map(|conn| (conn.module_identifier(), conn))
1079+
.for_each(|(module, conn)| outgoings.entry(*module).or_default().push(conn.clone()));
1080+
1081+
(mid, (outgoings, blocks))
1082+
})
1083+
.collect::<IdentifierMap<_>>()
1084+
}
1085+
10361086
fn create_chunks(&mut self, compilation: &mut Compilation) -> Result<()> {
10371087
let mut errors = vec![];
10381088

1039-
let mut roots = self.analyze_module_graph(compilation)?;
1089+
let outgoings = self.prepare_outgoings(compilation);
1090+
1091+
let mut roots = self.analyze_module_graph(compilation, &outgoings)?;
10401092

10411093
let enable_incremental: bool = compilation
10421094
.incremental
@@ -1102,6 +1154,22 @@ impl CodeSplitter {
11021154
let mut async_entrypoints = HashSet::default();
11031155
let mut entrypoints = HashMap::default();
11041156
let mut skipped = HashSet::<usize>::default();
1157+
let outgoings = {
1158+
let mg = compilation.get_module_graph();
1159+
let all_modules = mg.modules();
1160+
all_modules
1161+
.keys()
1162+
.par_bridge()
1163+
.map(|mid| {
1164+
(
1165+
*mid,
1166+
mg.get_outgoing_connections(mid)
1167+
.cloned()
1168+
.collect::<Vec<_>>(),
1169+
)
1170+
})
1171+
.collect::<IdentifierMap<_>>()
1172+
};
11051173

11061174
for (idx, (reuse, cache)) in finalize_result.chunks.into_iter().enumerate() {
11071175
let chunk_desc = cache.chunk_desc;
@@ -1283,11 +1351,7 @@ Or do you want to use the entrypoints '{name}' and '{entry_runtime}' independent
12831351

12841352
if initial {
12851353
let mut assign_depths_map = IdentifierMap::default();
1286-
assign_depths(
1287-
&mut assign_depths_map,
1288-
&compilation.get_module_graph(),
1289-
entry_modules.iter(),
1290-
);
1354+
assign_depths(&mut assign_depths_map, &entry_modules, &outgoings);
12911355
let mut module_graph = compilation.get_module_graph_mut();
12921356
for (m, depth) in assign_depths_map {
12931357
module_graph.set_depth_if_lower(&m, depth);

0 commit comments

Comments
 (0)