Skip to content

Commit 5b17418

Browse files
perf(core): use dense storage for dependency connections (#14148)
* perf(core): use dense storage for dependency connections Co-authored-by: hardfist <1562502418@qq.com> * fix(core): relax dense connection storage default Co-authored-by: hardfist <1562502418@qq.com> * fix(core): page dense dependency connection storage Co-authored-by: hardfist <1562502418@qq.com> * Revert "fix(core): page dense dependency connection storage" This reverts commit 6097bd2. --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com>
1 parent c5f4b70 commit 5b17418

3 files changed

Lines changed: 179 additions & 3 deletions

File tree

crates/rspack_core/src/module_graph/mod.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use rayon::prelude::*;
88
use rspack_collections::{IdentifierHasher, IdentifierMap};
99
use rspack_error::Result;
1010
use rspack_hash::RspackHashDigest;
11-
use rustc_hash::{FxHashMap as HashMap, FxHasher};
11+
use rustc_hash::FxHashMap as HashMap;
1212
use swc_core::ecma::atoms::Atom;
1313

1414
use crate::{
@@ -145,8 +145,7 @@ pub(crate) struct ModuleGraphData {
145145

146146
/// ModuleGraphConnection indexed by `DependencyId`.
147147
/// modified here https://github.com/web-infra-dev/rspack/blob/9ae2f0f3be22370197cd9ed3308982f84f2bb738/crates/rspack_plugin_javascript/src/plugin/module_concatenation_plugin.rs#L820
148-
connections:
149-
rollback::OverlayMap<DependencyId, ModuleGraphConnection, BuildHasherDefault<FxHasher>>,
148+
connections: rollback::DenseDependencyIdOverlayMap<ModuleGraphConnection>,
150149

151150
/***************** only Modified during Seal Phase ********************/
152151
// setting here https://github.com/web-infra-dev/rspack/blob/9ae2f0f3be22370197cd9ed3308982f84f2bb738/crates/rspack_plugin_javascript/src/plugin/side_effects_flag_plugin.rs#L318
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
use super::OverlayValue;
2+
use crate::DependencyId;
3+
4+
#[derive(Debug, Clone)]
5+
pub struct DenseDependencyIdOverlayMap<V> {
6+
base: Vec<Option<V>>,
7+
overlay: Option<Vec<Option<OverlayValue<V>>>>,
8+
}
9+
10+
impl<V> Default for DenseDependencyIdOverlayMap<V> {
11+
fn default() -> Self {
12+
Self {
13+
base: Vec::new(),
14+
overlay: None,
15+
}
16+
}
17+
}
18+
19+
impl<V> DenseDependencyIdOverlayMap<V> {
20+
#[inline]
21+
pub fn checkpoint(&mut self) {
22+
self.overlay.get_or_insert_with(Vec::new);
23+
}
24+
25+
#[inline]
26+
pub fn reset(&mut self) {
27+
self.overlay = None;
28+
}
29+
30+
#[inline]
31+
pub fn insert(&mut self, key: DependencyId, value: V) {
32+
let index = key.as_u32() as usize;
33+
if self.overlay.is_some() {
34+
Self::ensure_len(self.overlay(), index);
35+
self.overlay.as_mut().expect("overlay checked above")[index] =
36+
Some(OverlayValue::Value(value));
37+
} else {
38+
Self::ensure_len(&mut self.base, index);
39+
self.base[index] = Some(value);
40+
}
41+
}
42+
43+
#[inline]
44+
pub fn remove(&mut self, key: &DependencyId) {
45+
let index = key.as_u32() as usize;
46+
if self.overlay.is_some() {
47+
Self::ensure_len(self.overlay(), index);
48+
self.overlay.as_mut().expect("overlay checked above")[index] = Some(OverlayValue::Tombstone);
49+
} else if let Some(value) = self.base.get_mut(index) {
50+
*value = None;
51+
}
52+
}
53+
54+
#[inline]
55+
pub fn get(&self, key: &DependencyId) -> Option<&V> {
56+
let index = key.as_u32() as usize;
57+
if let Some(overlay) = &self.overlay
58+
&& let Some(Some(value)) = overlay.get(index)
59+
{
60+
return match value {
61+
OverlayValue::Value(value) => Some(value),
62+
OverlayValue::Tombstone => None,
63+
};
64+
}
65+
self.base.get(index).and_then(Option::as_ref)
66+
}
67+
68+
#[inline]
69+
pub fn get_mut(&mut self, key: &DependencyId) -> Option<&mut V>
70+
where
71+
V: Clone,
72+
{
73+
let index = key.as_u32() as usize;
74+
if self.overlay.is_some() {
75+
self.materialize_overlay_value(index);
76+
let overlay = self.overlay.as_mut().expect("overlay checked above");
77+
match overlay.get_mut(index).and_then(Option::as_mut) {
78+
Some(OverlayValue::Value(value)) => Some(value),
79+
_ => None,
80+
}
81+
} else {
82+
self.base.get_mut(index).and_then(Option::as_mut)
83+
}
84+
}
85+
86+
#[inline]
87+
fn materialize_overlay_value(&mut self, index: usize)
88+
where
89+
V: Clone,
90+
{
91+
let overlay = self.overlay.as_ref().expect("overlay checked above");
92+
if matches!(overlay.get(index), Some(Some(_))) {
93+
return;
94+
}
95+
96+
if let Some(value) = self.base.get(index).and_then(Option::as_ref).cloned() {
97+
Self::ensure_len(self.overlay(), index);
98+
self.overlay.as_mut().expect("overlay checked above")[index] =
99+
Some(OverlayValue::Value(value));
100+
}
101+
}
102+
103+
#[inline]
104+
fn overlay(&mut self) -> &mut Vec<Option<OverlayValue<V>>> {
105+
self.overlay.get_or_insert_with(Vec::new)
106+
}
107+
108+
#[inline]
109+
fn ensure_len<T>(values: &mut Vec<Option<T>>, index: usize) {
110+
if values.len() <= index {
111+
values.resize_with(index + 1, || None);
112+
}
113+
}
114+
}
115+
116+
#[cfg(test)]
117+
mod tests {
118+
use crate::{DependencyId, module_graph::rollback::DenseDependencyIdOverlayMap};
119+
120+
#[test]
121+
fn checkpoint_inserts_apply_only_to_overlay() {
122+
let mut map = DenseDependencyIdOverlayMap::default();
123+
let a = DependencyId::from(0);
124+
let b = DependencyId::from(1);
125+
126+
map.insert(a, 1);
127+
map.checkpoint();
128+
map.insert(b, 2);
129+
map.insert(a, 3);
130+
131+
assert_eq!(map.get(&a), Some(&3));
132+
assert_eq!(map.get(&b), Some(&2));
133+
134+
map.reset();
135+
136+
assert_eq!(map.get(&a), Some(&1));
137+
assert_eq!(map.get(&b), None);
138+
}
139+
140+
#[test]
141+
fn remove_in_overlay_masks_base() {
142+
let mut map = DenseDependencyIdOverlayMap::default();
143+
let a = DependencyId::from(0);
144+
let b = DependencyId::from(7);
145+
146+
map.insert(a, 1);
147+
map.insert(b, 2);
148+
map.checkpoint();
149+
map.remove(&a);
150+
151+
assert_eq!(map.get(&a), None);
152+
assert_eq!(map.get(&b), Some(&2));
153+
154+
map.reset();
155+
156+
assert_eq!(map.get(&a), Some(&1));
157+
assert_eq!(map.get(&b), Some(&2));
158+
}
159+
160+
#[test]
161+
fn get_mut_clones_base_into_overlay() {
162+
let mut map = DenseDependencyIdOverlayMap::default();
163+
let a = DependencyId::from(3);
164+
165+
map.insert(a, 1);
166+
map.checkpoint();
167+
*map.get_mut(&a).expect("should clone base into overlay") = 5;
168+
169+
assert_eq!(map.get(&a), Some(&5));
170+
171+
map.reset();
172+
173+
assert_eq!(map.get(&a), Some(&1));
174+
}
175+
}

crates/rspack_core/src/module_graph/rollback/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
pub mod atom;
22
pub use atom::*;
3+
pub mod dense_dependency_id_overlay_map;
4+
pub use dense_dependency_id_overlay_map::*;
35
pub mod map;
46
pub use map::*;
57
pub mod overlay_map;

0 commit comments

Comments
 (0)