perf: for_each_root 迭代器替代 gc_roots,复用 Vec DFS mark 栈消除每轮 GC 堆分配

This commit is contained in:
2026-06-24 19:01:51 +08:00
parent bb4ce39e50
commit 9a62cf1610
2 changed files with 75 additions and 55 deletions
+34 -36
View File
@@ -1,4 +1,4 @@
use std::collections::{HashMap, VecDeque}; use std::collections::HashMap;
use std::mem::size_of; use std::mem::size_of;
use std::time::Instant; use std::time::Instant;
@@ -23,6 +23,7 @@ pub struct SessionGc {
pub last_collection_duration_us: u64, pub last_collection_duration_us: u64,
pub max_collection_duration_us: u64, pub max_collection_duration_us: u64,
pub min_collection_duration_us: u64, pub min_collection_duration_us: u64,
pub(crate) mark_stack: Vec<*mut JsObject>,
} }
impl SessionGc { impl SessionGc {
@@ -32,11 +33,6 @@ impl SessionGc {
} }
impl SessionGc { impl SessionGc {
#[inline]
pub(crate) fn is_session_ptr(&self, vm: &Vm, obj_ptr: *mut JsObject) -> bool {
vm.is_session_ptr(obj_ptr)
}
pub(crate) fn clear_all_marks(&mut self, vm: &mut Vm) { pub(crate) fn clear_all_marks(&mut self, vm: &mut Vm) {
for &ptr in &vm.session_object_ptrs { for &ptr in &vm.session_object_ptrs {
if ptr.is_null() { if ptr.is_null() {
@@ -48,7 +44,7 @@ impl SessionGc {
} }
} }
fn object_edges(&self, obj: &JsObject) -> Vec<JsValue> { fn object_edges(obj: &JsObject) -> Vec<JsValue> {
let mut edges = Vec::new(); let mut edges = Vec::new();
if let Some(props) = obj.hash_props_vec() { if let Some(props) = obj.hash_props_vec() {
edges.extend(props.iter().copied().filter(|val| val.is_object())); edges.extend(props.iter().copied().filter(|val| val.is_object()));
@@ -87,32 +83,36 @@ impl SessionGc {
edges edges
} }
pub(crate) fn mark(&mut self, vm: &Vm, roots: &[JsValue]) { pub(crate) fn mark(&mut self, vm: &Vm) {
let mut queue = VecDeque::<*mut JsObject>::new(); let mut seeds = Vec::new();
vm.for_each_root(|root| {
if root.is_object() {
seeds.push(root.as_js_object_ptr());
}
});
for root in roots { let stack = &mut self.mark_stack;
if !root.is_object() { stack.clear();
for ptr in seeds {
if ptr.is_null() {
continue; continue;
} }
let ptr = root.as_js_object_ptr(); if vm.is_session_ptr(ptr) {
if self.is_session_ptr(vm, ptr) { stack.push(ptr);
queue.push_back(ptr);
continue; continue;
} }
if !ptr.is_null() { // SAFETY: object roots are produced by VM-owned fields and builtin objects.
// SAFETY: object roots are produced by VM-owned fields and builtin objects. for edge in Self::object_edges(unsafe { &*ptr }) {
for edge in self.object_edges(unsafe { &*ptr }) { if edge.is_object() {
if edge.is_object() { let edge_ptr = edge.as_js_object_ptr();
let edge_ptr = edge.as_js_object_ptr(); if vm.is_session_ptr(edge_ptr) {
if self.is_session_ptr(vm, edge_ptr) { stack.push(edge_ptr);
queue.push_back(edge_ptr);
}
} }
} }
} }
} }
while let Some(ptr) = queue.pop_front() { while let Some(ptr) = stack.pop() {
if ptr.is_null() { if ptr.is_null() {
continue; continue;
} }
@@ -124,14 +124,14 @@ impl SessionGc {
continue; continue;
} }
obj.set_gc_mark(true); obj.set_gc_mark(true);
let edges = self.object_edges(obj); let edges = Self::object_edges(obj);
for edge in edges { for edge in edges {
if !edge.is_object() { if !edge.is_object() {
continue; continue;
} }
let child_ptr = edge.as_js_object_ptr(); let child_ptr = edge.as_js_object_ptr();
if self.is_session_ptr(vm, child_ptr) { if vm.is_session_ptr(child_ptr) {
queue.push_back(child_ptr); stack.push(child_ptr);
} }
} }
} }
@@ -293,7 +293,7 @@ impl SessionGc {
pub(crate) fn collect(&mut self, vm: &mut Vm) { pub(crate) fn collect(&mut self, vm: &mut Vm) {
let start = Instant::now(); let start = Instant::now();
self.mark(vm, &vm.gc_roots()); self.mark(vm);
let freed_bytes = self.sweep(vm); let freed_bytes = self.sweep(vm);
let elapsed = start.elapsed(); let elapsed = start.elapsed();
@@ -341,6 +341,7 @@ impl Default for SessionGc {
last_collection_duration_us: 0, last_collection_duration_us: 0,
max_collection_duration_us: 0, max_collection_duration_us: 0,
min_collection_duration_us: u64::MAX, min_collection_duration_us: u64::MAX,
mark_stack: Vec::new(),
} }
} }
} }
@@ -473,7 +474,8 @@ mod tests {
vm.constants.push(JsValue::from_js_object(frame_session)); vm.constants.push(JsValue::from_js_object(frame_session));
vm.sub_module_constants = vec![vec![JsValue::from_js_object(child_session)]]; vm.sub_module_constants = vec![vec![JsValue::from_js_object(child_session)]];
let roots = vm.gc_roots(); let mut roots = Vec::new();
vm.for_each_root(|v| roots.push(v));
assert!(has_ptr(&roots, root_session)); assert!(has_ptr(&roots, root_session));
assert!(has_ptr(&roots, frame_session)); assert!(has_ptr(&roots, frame_session));
assert!(has_ptr(&roots, this_session)); assert!(has_ptr(&roots, this_session));
@@ -500,9 +502,8 @@ mod tests {
let unreachable_session = vm.promote_object(unreachable); let unreachable_session = vm.promote_object(unreachable);
vm.regs[0] = JsValue::from_js_object(root_session); vm.regs[0] = JsValue::from_js_object(root_session);
let roots = vm.gc_roots();
let mut gc = std::mem::take(&mut vm.session_gc); let mut gc = std::mem::take(&mut vm.session_gc);
gc.mark(&vm, &roots); gc.mark(&vm);
vm.session_gc = gc; vm.session_gc = gc;
assert!(unsafe { (*root_session).is_gc_marked() }); assert!(unsafe { (*root_session).is_gc_marked() });
@@ -525,9 +526,8 @@ mod tests {
let root_session = vm.promote_object(root); let root_session = vm.promote_object(root);
vm.regs[0] = JsValue::from_js_object(root_session); vm.regs[0] = JsValue::from_js_object(root_session);
let dead_session = vm.promote_object(dead); let dead_session = vm.promote_object(dead);
let roots = vm.gc_roots();
let mut gc = std::mem::take(&mut vm.session_gc); let mut gc = std::mem::take(&mut vm.session_gc);
gc.mark(&vm, &roots); gc.mark(&vm);
let _ = gc.sweep(&mut vm); let _ = gc.sweep(&mut vm);
vm.session_gc = gc; vm.session_gc = gc;
@@ -548,9 +548,8 @@ mod tests {
let root_session = vm.promote_object(root); let root_session = vm.promote_object(root);
vm.regs[0] = JsValue::from_js_object(root_session); vm.regs[0] = JsValue::from_js_object(root_session);
let _ = vm.promote_object(child); let _ = vm.promote_object(child);
let roots = vm.gc_roots();
let mut gc = std::mem::take(&mut vm.session_gc); let mut gc = std::mem::take(&mut vm.session_gc);
gc.mark(&vm, &roots); gc.mark(&vm);
let _ = gc.sweep(&mut vm); let _ = gc.sweep(&mut vm);
vm.session_gc = gc; vm.session_gc = gc;
@@ -640,8 +639,7 @@ mod tests {
assert!(map_obj.hash_props_vec().is_none()); assert!(map_obj.hash_props_vec().is_none());
assert!(!map_obj.native_data().is_null()); assert!(!map_obj.native_data().is_null());
assert!(!SessionGc::new() assert!(!SessionGc::object_edges(map_obj)
.object_edges(map_obj)
.iter() .iter()
.any(|edge| std::ptr::eq(edge.as_js_object_ptr(), native_ptr))); .any(|edge| std::ptr::eq(edge.as_js_object_ptr(), native_ptr)));
} }
+41 -19
View File
@@ -295,29 +295,51 @@ impl Vm {
ptr ptr
} }
pub(crate) fn gc_roots(&self) -> Vec<JsValue> { pub(crate) fn for_each_root(&self, mut f: impl FnMut(JsValue)) {
let mut roots = Vec::new(); for value in &self.regs {
roots.extend_from_slice(&self.regs); if value.is_object() || value.is_string() {
f(*value);
}
}
for frame in &self.frames { for frame in &self.frames {
roots.extend(frame.saved_regs.iter().copied()); for &v in &frame.saved_regs {
roots.push(frame.saved_this); f(v);
roots.push(frame.saved_new_target); }
roots.push(frame.callee); f(frame.saved_this);
roots.push(frame.constructed_this.unwrap_or(JsValue::undefined())); f(frame.saved_new_target);
f(frame.callee);
f(frame.constructed_this.unwrap_or(JsValue::undefined()));
}
f(JsValue::from_js_object(self.session.global_object().as_ptr() as *mut JsObject));
f(self.exception_value.unwrap_or(JsValue::undefined()));
f(self.pending_exception.unwrap_or(JsValue::undefined()));
for &v in &self.for_of_iters {
f(v);
}
f(self.last_for_of_result);
for &v in &self.constants {
f(v);
} }
roots.push(JsValue::from_js_object(self.session.global_object().as_ptr() as *mut JsObject));
roots.push(self.exception_value.unwrap_or(JsValue::undefined()));
roots.push(self.pending_exception.unwrap_or(JsValue::undefined()));
roots.extend(self.for_of_iters.iter().copied());
roots.push(self.last_for_of_result);
roots.extend(self.constants.iter().copied());
for sub_module in &self.sub_module_constants { for sub_module in &self.sub_module_constants {
roots.extend(sub_module.iter().copied()); for &v in sub_module {
f(v);
}
}
for saved_constants in &self.saved_constants_stack {
for &v in saved_constants {
f(v);
}
}
for iter in &self.for_in_iters {
if iter.is_null() {
continue;
}
unsafe {
for &v in (*(*iter)).keys.iter() {
f(v);
}
}
} }
roots
} }
pub(crate) fn maybe_collect_session_gc(&mut self) { pub(crate) fn maybe_collect_session_gc(&mut self) {