Skip to content

Commit 97ca9a9

Browse files
committed
perf(turbopack): Merge nodes with same starting point
1 parent 8a652ba commit 97ca9a9

File tree

2 files changed

+77
-6
lines changed

2 files changed

+77
-6
lines changed

turbopack/crates/turbopack-ecmascript/src/tree_shake/graph.rs

+4-5
Original file line numberDiff line numberDiff line change
@@ -713,11 +713,10 @@ impl DepGraph {
713713
graph_ix: &self.g.graph_ix,
714714
data,
715715
};
716-
loop {
717-
if !optimizer.merge_single_incoming_nodes(&mut condensed) {
718-
break;
719-
}
720-
}
716+
717+
while optimizer.merge_single_incoming_nodes(&mut condensed)
718+
|| optimizer.merge_nodes_with_same_starting_point(&mut condensed)
719+
{}
721720

722721
let mut new_graph = InternedGraph::default();
723722

turbopack/crates/turbopack-ecmascript/src/tree_shake/optimizations.rs

+73-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use std::ops::Index;
22

33
use petgraph::{visit::EdgeRef, Direction, Graph};
4-
use rustc_hash::FxHashMap;
4+
use rustc_hash::{FxHashMap, FxHashSet};
55
use turbo_tasks::FxIndexSet;
66

77
use crate::tree_shake::graph::{Dependency, ItemData, ItemId, ItemIdGroupKind, ItemIdItemKind};
@@ -124,4 +124,76 @@ impl GraphOptimizer<'_> {
124124

125125
did_work
126126
}
127+
128+
pub(super) fn merge_nodes_with_same_starting_point<N>(
129+
&self,
130+
g: &mut Graph<Vec<N>, Dependency>,
131+
) -> bool
132+
where
133+
N: Copy,
134+
Self: Index<N, Output = ItemId>,
135+
{
136+
let mut did_work = false;
137+
let mut nodes_to_merge = Vec::new();
138+
139+
for node in g.node_indices() {
140+
let items = g.node_weight(node).expect("Node should exist");
141+
for item in items {
142+
let item_id = &self[*item];
143+
if matches!(item_id, ItemId::Group(..)) {
144+
let incoming_edges: Vec<_> =
145+
g.edges_directed(node, Direction::Incoming).collect();
146+
if incoming_edges.len() == 1 && incoming_edges[0].source() == node {
147+
nodes_to_merge.push(node);
148+
break;
149+
}
150+
}
151+
}
152+
}
153+
154+
for node in nodes_to_merge {
155+
let mut dependencies = g
156+
.edges_directed(node, Direction::Outgoing)
157+
.map(|e| (e.target(), *e.weight()))
158+
.collect::<Vec<_>>();
159+
160+
// Handle transitive dependencies
161+
let mut visited = FxHashSet::default();
162+
let mut stack = dependencies.clone();
163+
while let Some((dependency, _weight)) = stack.pop() {
164+
if visited.insert(dependency) {
165+
let transitive_deps = g
166+
.edges_directed(dependency, Direction::Outgoing)
167+
.map(|e| (e.target(), *e.weight()))
168+
.collect::<Vec<_>>();
169+
stack.extend(transitive_deps.clone());
170+
dependencies.extend(transitive_deps);
171+
}
172+
}
173+
174+
for (dependency, weight) in dependencies {
175+
let edge = g
176+
.find_edge(node, dependency)
177+
.and_then(|e| g.edge_weight_mut(e));
178+
match edge {
179+
Some(v) => {
180+
if matches!(v, Dependency::Weak) {
181+
*v = weight;
182+
}
183+
}
184+
None => {
185+
g.add_edge(node, dependency, weight);
186+
}
187+
}
188+
}
189+
190+
let items = g.node_weight(node).expect("Node should exist").clone();
191+
g.node_weight_mut(node).unwrap().extend(items);
192+
193+
g.remove_node(node).expect("Node should exist");
194+
did_work = true;
195+
}
196+
197+
did_work
198+
}
127199
}

0 commit comments

Comments
 (0)