From 9800e90a647e941167e9b53ae93929d1d32d97aa Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Fri, 11 Sep 2026 05:53:38 +0000 Subject: [PATCH] =?UTF-8?q?test:=20=E6=96=B0=E5=A2=9E=E5=AF=B9=E8=B1=A1?= =?UTF-8?q?=E5=9B=BE=E7=95=99=E5=AD=98=E8=AE=A1=E6=95=B0=E6=95=B0=E5=80=BC?= =?UTF-8?q?=E9=94=9A=EF=BC=9B=E5=9F=BA=E7=BA=BF=E5=8F=A3=E5=BE=84=E6=B3=A8?= =?UTF-8?q?=E6=98=8E=20retained=20=E8=87=B3=E5=A4=9A=E4=B8=80=E4=BB=BD?= =?UTF-8?q?=E5=BC=95=E6=93=8E=E5=86=85=E9=83=A8=E5=89=AF=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BENCHMARK_BASELINE.md | 3 +- .../tests/object_graph_retention_tests.rs | 56 +++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 crates/oxide_vm/tests/object_graph_retention_tests.rs diff --git a/BENCHMARK_BASELINE.md b/BENCHMARK_BASELINE.md index 222d7998..17a2a73b 100644 --- a/BENCHMARK_BASELINE.md +++ b/BENCHMARK_BASELINE.md @@ -89,4 +89,5 @@ unary_ops 25.55 0.000 25.555 200002 mem_string_rope 拼接 rope 链留存:重复二元拼接形成深 Cons 链(账目按逻辑长度计,数值高于实际足迹) 口径:Peak B/Ret B 为 session 堆账目(对象头 + 堆数据 + 存活串/BigInt);epoch arena 的临时驻留无字节账目(既有口径,数量见 JSON 的 epoch_objects)。Ret B 为 workload 后 -晋升 + 完整 GC 的真存活集。 +晋升 + 完整 GC 的真存活集,至多含一份引擎内部副本:逃逸屏障留 epoch 原件且顶层 +寄存器根不回写时,测量时晋升会把原件再克隆一份(object_graph 见留存锚测试)。 diff --git a/crates/oxide_vm/tests/object_graph_retention_tests.rs b/crates/oxide_vm/tests/object_graph_retention_tests.rs new file mode 100644 index 00000000..2de70f43 --- /dev/null +++ b/crates/oxide_vm/tests/object_graph_retention_tests.rs @@ -0,0 +1,56 @@ +//! mem_object_graph 留存计数回归锚:克隆式晋升双计的数值基线。 +//! +//! 15 层满二叉树(32767 节点)在「逃逸写屏障克隆 + 测量点 promote」晋升语义下 +//! 的三个确定性计数。晋升语义或陈旧顶层寄存器根处理改动后,若以下任一数值 +//! 变化,说明双计机制已变——须对照 tests/stress/mem_object_graph.js 的基线 +//! (benchmark_baseline.json)复核后再更新本锚。 + +use oxide_compiler::compiler::Compiler; +use oxide_parser::Allocator; +use oxide_vm::vm::Vm; + +/// 与 tests/stress/mem_object_graph.js 同源的 15 层满二叉树。 +const OBJECT_GRAPH: &str = r#" +function makeNode(depth, base) { + if (depth === 0) { + return { id: base, leaf: 1 }; + } + return { + id: base, + depth: depth, + left: makeNode(depth - 1, base * 2), + right: makeNode(depth - 1, base * 2 + 1), + }; +} + +var root = makeNode(14, 1); +root.left.id + root.right.right.id +"#; + +fn run_module(source: &str) -> Vm { + let allocator = Allocator::default(); + let program = oxide_parser::parse(&allocator, source).expect("parse"); + let module = Compiler::new().compile(&program).expect("compile"); + let mut vm = Vm::new(); + let result = vm.run(&module).expect("run"); + assert_eq!(result.to_string(), "9", "root.left.id(2) + root.right.right.id(7)"); + vm +} + +/// 留存锚:run 末屏障已把整树克隆进 session(全局镜像,32769 = 整树 + 2), +/// 顶层 var 寄存器槽仍持有 epoch 原件(SET 路径不回写寄存器);测量点 +/// promote_rooted 再克隆寄存器原件一份——两份互异副本在 full GC 后均根可达。 +/// epoch 侧 65534 的 2× 与节点无关:其中 32767 是每次 JS 调用无条件创建的 +/// arguments 对象(CREATE_ARGUMENTS),另 32767 才是节点字面量。 +#[test] +fn object_graph_retention_anchor() { + let mut vm = run_module(OBJECT_GRAPH); + + assert_eq!(vm.epoch_object_count(), 65534, "run 末 epoch 计数"); + assert_eq!(vm.session_object_count(), 32769, "run 末 session 屏障克隆计数"); + + vm.promote_rooted_epoch_objects(); + vm.promote_session_epoch_refs(); + vm.collect_session_gc(); + assert_eq!(vm.session_object_count(), 65536, "留存锚:克隆式晋升双计 = 2 × 32769(逻辑存活集 32769)"); +}