mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-24 02:31:13 +08:00
perf: 数组元素独立存储区,push 改为摊销 O(1) 尾插
This commit is contained in:
@@ -198,6 +198,42 @@ mod tests {
|
||||
assert!(!is_epoch_object(&vm, meta.set));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn session_arena_promotes_array_elements_and_element_meta() {
|
||||
let mut vm = Vm::new();
|
||||
let elem_child = plain_object(&mut vm);
|
||||
let meta_getter = plain_object(&mut vm);
|
||||
let array_proto = vm.session.builtin_world().array_proto.as_ptr() as *mut JsObject;
|
||||
let arr = vm.epoch.alloc(JsObject::new_array(
|
||||
EMPTY_SHAPE_ID,
|
||||
JsValue::from_js_object(array_proto),
|
||||
2,
|
||||
vm.epoch.bump(),
|
||||
));
|
||||
unsafe {
|
||||
(*arr).set_prop_at(0, JsValue::from_js_object(elem_child));
|
||||
(*arr).set_accessor_meta(
|
||||
1,
|
||||
JsValue::from_js_object(meta_getter),
|
||||
JsValue::undefined(),
|
||||
PropAttributes::DEFAULT_DATA,
|
||||
);
|
||||
(*arr).set_prop_shape(0, JsValue::int(99));
|
||||
}
|
||||
|
||||
let promoted = vm.promote_object(arr);
|
||||
let promoted_arr = unsafe { &*promoted };
|
||||
|
||||
assert!(promoted_arr.is_session_epoch());
|
||||
// 数组元素(含访问器 get)与命名属性随 clone + rewrite 迁移到 session。
|
||||
assert!(!is_epoch_object(&vm, promoted_arr.get_prop_at(0)));
|
||||
let meta = promoted_arr.prop_meta_at(1).expect("element accessor meta");
|
||||
assert!(!is_epoch_object(&vm, meta.get));
|
||||
assert_eq!(promoted_arr.get_prop_shape(0), JsValue::int(99));
|
||||
// 元素计数与长度不因 promotion 改变。
|
||||
assert_eq!(promoted_arr.prop_count(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn session_arena_barrier_promotes_global_root_write() {
|
||||
let mut vm = Vm::new();
|
||||
|
||||
@@ -55,6 +55,19 @@ impl SessionGc {
|
||||
|
||||
fn object_edges(obj: &JsObject) -> Vec<JsValue> {
|
||||
let mut edges = Vec::new();
|
||||
if let Some(elements) = obj.array_elements_vec() {
|
||||
edges.extend(elements.iter().copied().filter(|val| val.is_object()));
|
||||
}
|
||||
if let Some(meta) = obj.array_elements_meta_vec() {
|
||||
for entry in meta.iter().flatten() {
|
||||
if entry.get.is_object() {
|
||||
edges.push(entry.get);
|
||||
}
|
||||
if entry.set.is_object() {
|
||||
edges.push(entry.set);
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(props) = obj.hash_props_vec() {
|
||||
edges.extend(props.iter().copied().filter(|val| val.is_object()));
|
||||
}
|
||||
@@ -119,6 +132,13 @@ impl SessionGc {
|
||||
/// 无害地记录;sweep 只遍历 `session_string_ptrs`,`live` 中的非 session 指针
|
||||
/// 永远不会被查询。
|
||||
fn record_object_string_edges(live: &mut HashSet<*mut JsString, FxBuildHasher>, obj: &JsObject) {
|
||||
if let Some(elements) = obj.array_elements_vec() {
|
||||
for value in elements.iter() {
|
||||
if value.is_string() {
|
||||
live.insert(value.as_string_ptr_mut());
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(props) = obj.hash_props_vec() {
|
||||
for value in props.iter() {
|
||||
if value.is_string() {
|
||||
@@ -266,6 +286,21 @@ impl SessionGc {
|
||||
}
|
||||
let mut freed_bytes = 0u64;
|
||||
|
||||
let elems_ptr = obj.array_elements_raw() as *mut Vec<JsValue>;
|
||||
if !elems_ptr.is_null() {
|
||||
let vec = Box::from_raw(elems_ptr);
|
||||
freed_bytes += size_of::<Vec<JsValue>>() as u64 + (vec.capacity() * size_of::<JsValue>()) as u64;
|
||||
std::mem::drop(vec);
|
||||
}
|
||||
|
||||
let elems_meta_ptr = obj.array_elements_meta_raw() as *mut Vec<Option<PropMetaEntry>>;
|
||||
if !elems_meta_ptr.is_null() {
|
||||
let vec = Box::from_raw(elems_meta_ptr);
|
||||
freed_bytes += size_of::<Vec<Option<PropMetaEntry>>>() as u64
|
||||
+ (vec.capacity() * size_of::<Option<PropMetaEntry>>()) as u64;
|
||||
std::mem::drop(vec);
|
||||
}
|
||||
|
||||
let hash_ptr = obj.hash_props_raw() as *mut Vec<JsValue>;
|
||||
if !hash_ptr.is_null() {
|
||||
let vec = Box::from_raw(hash_ptr);
|
||||
@@ -763,6 +798,39 @@ mod tests {
|
||||
.any(|ptr| unsafe { (*(*ptr)).is_gc_marked() }));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sweep_preserves_array_elements_and_collects_dead_element_object() {
|
||||
let mut vm = Vm::new();
|
||||
let array_proto = vm.session.builtin_world().array_proto.as_ptr() as *mut JsObject;
|
||||
let arr = vm.epoch.alloc(JsObject::new_array(
|
||||
oxide_kernel::shape_forge::EMPTY_SHAPE_ID,
|
||||
JsValue::from_js_object(array_proto),
|
||||
2,
|
||||
vm.epoch.bump(),
|
||||
));
|
||||
let live_elem = plain_object(&mut vm);
|
||||
let dead_elem = plain_object(&mut vm);
|
||||
unsafe {
|
||||
(*arr).set_prop_at(0, JsValue::from_js_object(live_elem));
|
||||
(*arr).set_prop_at(1, JsValue::from_js_object(dead_elem));
|
||||
}
|
||||
// 仅元素 0 的对象作为 GC 根:元素 1 的对象应被回收。
|
||||
let arr_session = vm.promote_object(arr);
|
||||
vm.regs[0] = JsValue::from_js_object(arr_session);
|
||||
vm.regs[1] = JsValue::from_js_object(live_elem);
|
||||
|
||||
let mut gc = std::mem::take(&mut vm.gc_state.session_gc);
|
||||
gc.mark(&vm);
|
||||
let _ = gc.sweep(&mut vm);
|
||||
vm.gc_state.session_gc = gc;
|
||||
|
||||
// 存活对象经克隆 + rewrite:数组元素 0 指向晋升后的 live_elem。
|
||||
let live_session = unsafe { (*arr_session).get_prop_at(0).as_js_object_ptr() };
|
||||
assert_eq!(unsafe { (*live_session).prop_count() }, 0);
|
||||
assert_eq!(unsafe { (*arr_session).prop_count() }, 2);
|
||||
assert_eq!(unsafe { (*arr_session).get_prop_at(0).as_js_object_ptr() }, live_session);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn forwarding_is_cleared_after_sweep() {
|
||||
let mut vm = Vm::new();
|
||||
|
||||
Reference in New Issue
Block a user