From 9a62cf1610dc2072c26b519b946401fc5887936b Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Wed, 24 Jun 2026 19:01:51 +0800 Subject: [PATCH] =?UTF-8?q?perf:=20for=5Feach=5Froot=20=E8=BF=AD=E4=BB=A3?= =?UTF-8?q?=E5=99=A8=E6=9B=BF=E4=BB=A3=20gc=5Froots=EF=BC=8C=E5=A4=8D?= =?UTF-8?q?=E7=94=A8=20Vec=20DFS=20mark=20=E6=A0=88=E6=B6=88=E9=99=A4?= =?UTF-8?q?=E6=AF=8F=E8=BD=AE=20GC=20=E5=A0=86=E5=88=86=E9=85=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/oxide_vm/src/session_gc.rs | 70 +++++++++++++++---------------- crates/oxide_vm/src/vm.rs | 60 +++++++++++++++++--------- 2 files changed, 75 insertions(+), 55 deletions(-) diff --git a/crates/oxide_vm/src/session_gc.rs b/crates/oxide_vm/src/session_gc.rs index 7e945fbd..12c6be19 100644 --- a/crates/oxide_vm/src/session_gc.rs +++ b/crates/oxide_vm/src/session_gc.rs @@ -1,4 +1,4 @@ -use std::collections::{HashMap, VecDeque}; +use std::collections::HashMap; use std::mem::size_of; use std::time::Instant; @@ -23,6 +23,7 @@ pub struct SessionGc { pub last_collection_duration_us: u64, pub max_collection_duration_us: u64, pub min_collection_duration_us: u64, + pub(crate) mark_stack: Vec<*mut JsObject>, } impl SessionGc { @@ -32,11 +33,6 @@ 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) { for &ptr in &vm.session_object_ptrs { if ptr.is_null() { @@ -48,7 +44,7 @@ impl SessionGc { } } - fn object_edges(&self, obj: &JsObject) -> Vec { + fn object_edges(obj: &JsObject) -> Vec { let mut edges = Vec::new(); if let Some(props) = obj.hash_props_vec() { edges.extend(props.iter().copied().filter(|val| val.is_object())); @@ -87,32 +83,36 @@ impl SessionGc { edges } - pub(crate) fn mark(&mut self, vm: &Vm, roots: &[JsValue]) { - let mut queue = VecDeque::<*mut JsObject>::new(); + pub(crate) fn mark(&mut self, vm: &Vm) { + 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 { - if !root.is_object() { + let stack = &mut self.mark_stack; + stack.clear(); + for ptr in seeds { + if ptr.is_null() { continue; } - let ptr = root.as_js_object_ptr(); - if self.is_session_ptr(vm, ptr) { - queue.push_back(ptr); + if vm.is_session_ptr(ptr) { + stack.push(ptr); continue; } - if !ptr.is_null() { - // SAFETY: object roots are produced by VM-owned fields and builtin objects. - for edge in self.object_edges(unsafe { &*ptr }) { - if edge.is_object() { - let edge_ptr = edge.as_js_object_ptr(); - if self.is_session_ptr(vm, edge_ptr) { - queue.push_back(edge_ptr); - } + // SAFETY: object roots are produced by VM-owned fields and builtin objects. + for edge in Self::object_edges(unsafe { &*ptr }) { + if edge.is_object() { + let edge_ptr = edge.as_js_object_ptr(); + if vm.is_session_ptr(edge_ptr) { + stack.push(edge_ptr); } } } } - while let Some(ptr) = queue.pop_front() { + while let Some(ptr) = stack.pop() { if ptr.is_null() { continue; } @@ -124,14 +124,14 @@ impl SessionGc { continue; } obj.set_gc_mark(true); - let edges = self.object_edges(obj); + let edges = Self::object_edges(obj); for edge in edges { if !edge.is_object() { continue; } let child_ptr = edge.as_js_object_ptr(); - if self.is_session_ptr(vm, child_ptr) { - queue.push_back(child_ptr); + if vm.is_session_ptr(child_ptr) { + stack.push(child_ptr); } } } @@ -293,7 +293,7 @@ impl SessionGc { pub(crate) fn collect(&mut self, vm: &mut Vm) { let start = Instant::now(); - self.mark(vm, &vm.gc_roots()); + self.mark(vm); let freed_bytes = self.sweep(vm); let elapsed = start.elapsed(); @@ -341,6 +341,7 @@ impl Default for SessionGc { last_collection_duration_us: 0, max_collection_duration_us: 0, 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.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, frame_session)); assert!(has_ptr(&roots, this_session)); @@ -500,9 +502,8 @@ mod tests { let unreachable_session = vm.promote_object(unreachable); vm.regs[0] = JsValue::from_js_object(root_session); - let roots = vm.gc_roots(); let mut gc = std::mem::take(&mut vm.session_gc); - gc.mark(&vm, &roots); + gc.mark(&vm); vm.session_gc = gc; assert!(unsafe { (*root_session).is_gc_marked() }); @@ -525,9 +526,8 @@ mod tests { let root_session = vm.promote_object(root); vm.regs[0] = JsValue::from_js_object(root_session); let dead_session = vm.promote_object(dead); - let roots = vm.gc_roots(); let mut gc = std::mem::take(&mut vm.session_gc); - gc.mark(&vm, &roots); + gc.mark(&vm); let _ = gc.sweep(&mut vm); vm.session_gc = gc; @@ -548,9 +548,8 @@ mod tests { let root_session = vm.promote_object(root); vm.regs[0] = JsValue::from_js_object(root_session); let _ = vm.promote_object(child); - let roots = vm.gc_roots(); let mut gc = std::mem::take(&mut vm.session_gc); - gc.mark(&vm, &roots); + gc.mark(&vm); let _ = gc.sweep(&mut vm); vm.session_gc = gc; @@ -640,8 +639,7 @@ mod tests { assert!(map_obj.hash_props_vec().is_none()); assert!(!map_obj.native_data().is_null()); - assert!(!SessionGc::new() - .object_edges(map_obj) + assert!(!SessionGc::object_edges(map_obj) .iter() .any(|edge| std::ptr::eq(edge.as_js_object_ptr(), native_ptr))); } diff --git a/crates/oxide_vm/src/vm.rs b/crates/oxide_vm/src/vm.rs index 49130033..aa79907b 100644 --- a/crates/oxide_vm/src/vm.rs +++ b/crates/oxide_vm/src/vm.rs @@ -295,29 +295,51 @@ impl Vm { ptr } - pub(crate) fn gc_roots(&self) -> Vec { - let mut roots = Vec::new(); - roots.extend_from_slice(&self.regs); - + pub(crate) fn for_each_root(&self, mut f: impl FnMut(JsValue)) { + for value in &self.regs { + if value.is_object() || value.is_string() { + f(*value); + } + } for frame in &self.frames { - roots.extend(frame.saved_regs.iter().copied()); - roots.push(frame.saved_this); - roots.push(frame.saved_new_target); - roots.push(frame.callee); - roots.push(frame.constructed_this.unwrap_or(JsValue::undefined())); + for &v in &frame.saved_regs { + f(v); + } + f(frame.saved_this); + 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 { - 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) {