docs: 设计方案文档对齐当前架构 + 补充 benchmark 与未来计划
- 1.3 / 1.4 / 2.5 / 3.4 / 3.7 章节同步至 KernelCore + KernelSession 拆分、PermInterner 替换 StringForge、Epoch Arena + JsString GC 双轨制 - 第 4 章 Benchmark 嵌入四引擎多会话耗时 / 内存折线图 + 双平台单条 JS / 压测柱状图 (6 张 PNG, 鲲鹏 920 vs Intel 8259CL) - 4.2.5 新增四引擎综合得分表 (OxideJS 61 / QuickJS 80 / BOA 69 / V8 53) - 5.1 / 5.2 补充研发过程的两个重大问题: counter/emitter 漂移修复路径 + test262 --supervise 鲁棒性 - 第 6 章 AI 使用情况说明; 第 7 章未来计划四点 - 同步生成 PDF (设计方案文档.pdf, 8 页, Chrome headless 渲染)
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 324 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 250 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 146 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 147 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 137 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 140 KiB |
@@ -1,8 +1,9 @@
|
||||
# 面向 AI Agent 的轻量级 JavaScript 执行引擎设计方案
|
||||
# 面向 AI Agent 的轻量级 JavaScript 执行引擎设计方案文档
|
||||
|
||||
> 项目名称:OxideJS
|
||||
> 技术路线:Rust + 自研字节码编译器 + 寄存器式虚拟机 + 单 Kernel 多 VM 运行时
|
||||
> 目标场景:短时、高频、即时执行的 JavaScript 代码运行环境
|
||||
> 队名:爱特每羊队(T2026104239910027)
|
||||
> 团队成员:古宇恒、彭俊皓、杜文韬
|
||||
> 项目名称:OxideJS
|
||||
> 技术路线:Rust + 自研字节码编译器 + 寄存器式虚拟机 + 单 Kernel 多 VM 运行时
|
||||
|
||||
---
|
||||
|
||||
@@ -46,24 +47,28 @@ OxideJS 最重要的运行时结构是 **单 Kernel 多 VM**。
|
||||
+===================================================================================+
|
||||
| OxideKernel |
|
||||
| |
|
||||
| Shared, long-lived, cross-request runtime state |
|
||||
| 分为两层: KernelCore (immutable, 跨请求永久共享) |
|
||||
| KernelSession (per-session, 通过 full_reset() 完全重建) |
|
||||
| |
|
||||
| +----------------+ +----------------+ +----------------+ +----------------+ |
|
||||
| | StringForge | | ShapeForge | | CodeForge | | PropForge | |
|
||||
| |----------------| |----------------| |----------------| |----------------| |
|
||||
| | intern strings | | hidden classes | | bytecode cache | | IC templates | |
|
||||
| | string handles | | property layout| | structural hash| | shape -> slot | |
|
||||
| +----------------+ +----------------+ +----------------+ +----------------+ |
|
||||
| ┌── KernelCore (Arc<KernelCore>) ─────────────────────────────────────────┐ |
|
||||
| | +----------------+ +----------------+ +----------------+ +----------+ | |
|
||||
| | | PermInterner | | ShapeForge | | CodeForge | | PropForge| | |
|
||||
| | |----------------| |----------------| |----------------| |----------| | |
|
||||
| | | append-only key| | hidden classes | | bytecode cache | | shape→slot| | |
|
||||
| | | leaked &'static| | property layout| | LRU 缓存 | | IC 模板 | | |
|
||||
| | | 64-bit hash | | shape graph | | structural hash| | | | |
|
||||
| | +----------------+ +----------------+ +----------------+ +----------+ | |
|
||||
| | KernelConfig (pool / step / max_cached_modules / log levels) | |
|
||||
| └──────────────────────────────────────────────────────────────────────────┘ |
|
||||
| |
|
||||
| +----------------+ +----------------+ +----------------+ +----------------+ |
|
||||
| | BuiltinWorld | | Global Shapes | | Kernel Config | | Runtime Logs | |
|
||||
| |----------------| |----------------| |----------------| |----------------| |
|
||||
| | Object/Array | | builtin shape | | pool limits | | trace/debug | |
|
||||
| | Function/etc. | | root layouts | | step limits | | diagnostics | |
|
||||
| +----------------+ +----------------+ +----------------+ +----------------+ |
|
||||
| ┌── KernelSession (per-session) ──────────────────────────────────────────┐ |
|
||||
| | BuiltinWorld | Global object | BuiltinSnapshot | |
|
||||
| | Object/Array/ | 全局对象与基础 | 脏检测 + 选择性重建 | |
|
||||
| | Function/... | 属性 | | |
|
||||
| └─────────────────────────────────────────────────────────────────────────┘ |
|
||||
+==========================================+========================================+
|
||||
|
|
||||
| Arc<OxideKernel>
|
||||
| Arc<KernelCore> + KernelSession
|
||||
|
|
||||
+-------------------------------+-------------------------------+
|
||||
| | |
|
||||
@@ -73,25 +78,33 @@ OxideJS 最重要的运行时结构是 **单 Kernel 多 VM**。
|
||||
|----------------------| |----------------------| |----------------------|
|
||||
| registers[256] | | registers[256] | | registers[256] |
|
||||
| pc | | pc | | pc |
|
||||
| bytecode view | | bytecode view | | bytecode view |
|
||||
| constants view | | constants view | .... | constants view |
|
||||
| call frames | | call frames | | call frames |
|
||||
| try/catch stack | | try/catch stack | | try/catch stack |
|
||||
| exception state | | exception state | | exception state |
|
||||
| local session arena | | local session arena | | local session arena |
|
||||
| session GC metadata | | session GC metadata | | session GC metadata |
|
||||
| bytecode | | bytecode | | bytecode |
|
||||
| immutables_cache | | immutables_cache | .... | immutables_cache |
|
||||
| frames (SmallVec) | | frames (SmallVec) | | frames (SmallVec) |
|
||||
| save_stack (共享) | | save_stack (共享) | | save_stack (共享) |
|
||||
| try / except state | | try / except state | | try / except state |
|
||||
| Epoch Arena (会话) | | Epoch Arena (会话) | | Epoch Arena (会话) |
|
||||
| JsString GC + roots | | JsString GC + roots | | JsString GC + roots |
|
||||
| object_prototype | | object_prototype | | object_prototype |
|
||||
+----------+-----------+ +----------+-----------+ +----------+-----------+
|
||||
| | |
|
||||
v v v
|
||||
Request Result Request Result Request Result
|
||||
```
|
||||
|
||||
说明:
|
||||
|
||||
- **PermInterner** 取代了早期的 `StringForge`: 它只 intern **key / identifier / method name** 等永不释放的字符串, 用 leaked `&'static str` + 64-bit hash 索引, 没有 ref-count 也没有 maybe_sweep 重编号路径, 因而无锁读、零冲突;
|
||||
- **运行时 JsString 值** (用户代码里 `"hello" + name` 产出的临时字符串) 不在 Kernel, 而是由各 VM 自带的 **JsString 标记清扫 GC** 管理 (commit `fe2d492`), 配合 Epoch Arena 实现短命对象 O(1) 回收;
|
||||
- **KernelCore vs KernelSession** 的拆分让 test262 runner 可以在不丢弃编译缓存 / Shape 图 / Property IC 模板的前提下, 通过 `full_reset()` 重建 BuiltinWorld 与 global object, 实现测试间完全隔离 (commit `d242d08`)。
|
||||
|
||||
该结构将运行时状态分为两类:
|
||||
|
||||
| 状态类型 | 所在位置 | 生命周期 | 示例 |
|
||||
|----------|----------|----------|------|
|
||||
| 共享状态 | `OxideKernel` | 进程级、长生命周期 | 字符串表、Shape 表、Code cache、内置对象 |
|
||||
| 私有状态 | `Vm` | 请求级、短生命周期 | 寄存器、PC、调用栈、异常、临时对象 |
|
||||
| 永久共享状态 | `KernelCore` | 进程级、永不释放 | PermInterner (key/identifier)、ShapeForge、CodeForge、PropForge |
|
||||
| 会话级共享状态 | `KernelSession` | 测试 / 会话级、可 `full_reset()` 重建 | BuiltinWorld、global object、BuiltinSnapshot |
|
||||
| 请求私有状态 | `Vm` | 请求级、短生命周期 | 寄存器、PC、调用栈、异常、Epoch Arena、JsString GC 根 |
|
||||
|
||||
这种分层使得 OxideJS 能够同时满足两类需求:
|
||||
|
||||
@@ -109,12 +122,22 @@ AI Agent 场景中的大部分对象具有明显的短生命周期特征:对
|
||||
|
||||
Long-lived / shared memory
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ OxideKernel │
|
||||
│ KernelCore (Arc<KernelCore>, 进程级永久共享) │
|
||||
│ │
|
||||
│ StringForge ShapeForge CodeForge BuiltinWorld │
|
||||
│ interned strs shape records bytecode cache builtin prototypes │
|
||||
│ PermInterner ShapeForge CodeForge PropForge │
|
||||
│ append-only hidden classes bytecode LRU IC 模板 │
|
||||
│ key / ident shape graph structural hash shape→slot │
|
||||
│ │
|
||||
│ 生命周期:随 Kernel 存活,跨 VM 共享 │
|
||||
│ 生命周期: 随进程存活, 表结构 append-only, 永不释放 │
|
||||
└──────────────────────────────────────────────────────────────────────────────┘
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ KernelSession (per-session, 可通过 full_reset() 重建) │
|
||||
│ │
|
||||
│ BuiltinWorld Global object BuiltinSnapshot │
|
||||
│ Object/Array/ 全局对象 脏检测 + 选择性重建 │
|
||||
│ Function/... │
|
||||
│ │
|
||||
│ 生命周期: 单次 session, 测试隔离时通过 full_reset() 完全重建 │
|
||||
└──────────────────────────────────────────────────────────────────────────────┘
|
||||
▲
|
||||
│ shared references
|
||||
@@ -123,10 +146,10 @@ AI Agent 场景中的大部分对象具有明显的短生命周期特征:对
|
||||
┌───────────────────────────────────┴──────────────────────────────────────────┐
|
||||
│ Vm │
|
||||
│ │
|
||||
│ registers / call frames / exception state │
|
||||
│ registers[256] / call frames (SmallVec) / try-catch / exception state │
|
||||
│ │
|
||||
│ +----------------------------+ │
|
||||
│ | Session Arena | bump allocation for short-lived objects │
|
||||
│ | Epoch Arena | bump allocation, O(1) epoch 回收 │
|
||||
│ |----------------------------| │
|
||||
│ | object allocations | │
|
||||
│ | arrays / temporary values | │
|
||||
@@ -134,24 +157,25 @@ AI Agent 场景中的大部分对象具有明显的短生命周期特征:对
|
||||
│ +----------------------------+ │
|
||||
│ │
|
||||
│ +----------------------------+ │
|
||||
│ | Session GC Metadata | mark/sweep metadata for session objects │
|
||||
│ | JsString GC | 标记清扫, 管理运行时 JsString 值 │
|
||||
│ |----------------------------| │
|
||||
│ | tracked allocations | │
|
||||
│ | roots from registers | │
|
||||
│ | roots from frames | │
|
||||
│ | GC 阈值: 32 MB (commit ad7b044) │
|
||||
│ +----------------------------+ │
|
||||
│ │
|
||||
│ 生命周期: 单 VM / 单执行会话,执行结束后可批量释放或回收 │
|
||||
│ 生命周期: 单 VM / 单执行会话, 执行结束后 Epoch 翻页, GC 标记清扫批量释放 │
|
||||
└──────────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
内存与 GC 设计的关键点:
|
||||
|
||||
- **短命对象进入 VM 本地 session arena**:降低通用堆分配压力;
|
||||
- **VM 拥有自己的 session GC 元数据**:寄存器、调用栈、异常对象都可作为 root;
|
||||
- **Kernel 对象与 session 对象分层**:内置对象、Shape、字符串等长生命周期数据不随单次执行销毁;
|
||||
- **执行结束可快速清理 session 内存**:避免每次短脚本执行都触发重型全局 GC;
|
||||
- **Map / Set / Array / Object 等容器需要参与 GC 追踪**:防止 session 对象在容器中被错误释放。
|
||||
- **短命对象进入 VM 本地 Epoch Arena**: bump allocation, 整个 epoch 翻页时 O(1) 回收, 不参与逐对象扫描;
|
||||
- **VM 拥有自己的 JsString GC**: 运行时字符串 (`"hello" + name` 等) 由标记清扫 GC 管理, 寄存器 / 调用栈 / 异常对象 / Map/Set/Array 容器都作为 root, GC 阈值 32 MB 触发 (commit `ad7b044`);
|
||||
- **永久状态与会话状态分层**: `KernelCore` 持有 PermInterner / Shape / Code / Prop 等长生命周期表, `KernelSession` 持有可重建的 BuiltinWorld 与 global object;
|
||||
- **测试 / 短会话隔离**: 通过 `KernelSession::full_reset()` + `BuiltinSnapshot` 脏检测可以在不丢弃编译缓存的前提下重建 builtin 与全局对象;
|
||||
- **Map / Set / Array / Object 等容器参与 GC 追踪**: 防止 session 对象在容器中被错误释放 (commit `9db28b2` 修过 Map/Set GC 追踪丢失问题)。
|
||||
|
||||
### 1.5 创新点 3: 自研字节码与寄存器式 VM
|
||||
|
||||
@@ -241,7 +265,7 @@ OxideJS 的一次执行请求从输入 JS 源码到返回结果,经过以下
|
||||
│ CompiledModule
|
||||
▼
|
||||
┌────────────────────────────────────────────────────────────────────────────────────┐
|
||||
│ OxideKernel.CodeForge │
|
||||
│ KernelCore.CodeForge │
|
||||
│------------------------------------------------------------------------------------│
|
||||
│ if cache hit: │
|
||||
│ reuse Arc<CompiledModule> │
|
||||
@@ -414,19 +438,26 @@ loop:
|
||||
|
||||
### 2.5 Kernel 层
|
||||
|
||||
Kernel 是单进程内的共享运行时核心。它不保存某一次执行请求的寄存器和调用栈,而是保存可跨请求复用的结构。
|
||||
Kernel 是单进程内的共享运行时核心。它不保存某一次执行请求的寄存器和调用栈,而是保存可跨请求复用的结构。Kernel 内部进一步拆分为两层 (commit `d242d08`):
|
||||
|
||||
- **KernelCore**: 进程级永久共享, 表结构 append-only, 永不重建;
|
||||
- **KernelSession**: 测试 / 会话级, 可通过 `full_reset()` 配合 `BuiltinSnapshot` 脏检测实现选择性重建。
|
||||
|
||||
```text
|
||||
+------------------------------------------------------------------------------------+
|
||||
| OxideKernel |
|
||||
+------------------------------------------------------------------------------------+
|
||||
| ── KernelCore (Arc<KernelCore>, 跨请求永久共享) ────────────────────────────────── |
|
||||
| KernelConfig |
|
||||
| StringForge 全局字符串驻留池 |
|
||||
| PermInterner Append-only key / identifier 驻留 (leaked &'static str, 64-bit hash)|
|
||||
| ShapeForge 全局 Shape / Hidden Class 表 |
|
||||
| CodeForge 编译结果缓存 |
|
||||
| PropForge 属性访问模板 / IC 相关缓存 |
|
||||
| BuiltinWorld Object / Array / Function / String / Number 等内置对象结构 |
|
||||
| Global object 全局对象及其基础属性 |
|
||||
| CodeForge 编译结果缓存 (Mutex<LruCache>, max_cached_modules 上限) |
|
||||
| PropForge 属性访问 IC 模板 / shape→slot 缓存 |
|
||||
| |
|
||||
| ── KernelSession (per-session, 可 full_reset() 重建) ────────────────────────────── |
|
||||
| BuiltinWorld Object / Array / Function / String / Number 等内置对象结构 |
|
||||
| Global object 全局对象及其基础属性 |
|
||||
| BuiltinSnapshot 脏检测快照, 支撑选择性重建 |
|
||||
+------------------------------------------------------------------------------------+
|
||||
```
|
||||
|
||||
@@ -434,12 +465,13 @@ Kernel 是单进程内的共享运行时核心。它不保存某一次执行请
|
||||
|
||||
| 问题 | 普通每 VM 独立 runtime | OxideJS 单 Kernel 多 VM |
|
||||
|------|------------------------|--------------------------|
|
||||
| 内置对象初始化 | 每个 VM 重复构造 | Kernel 中集中构造与复用 |
|
||||
| 字符串驻留 | 每个 VM 各自保存 | StringForge 统一保存 |
|
||||
| 内置对象初始化 | 每个 VM 重复构造 | KernelSession 中集中构造与复用 |
|
||||
| Key / identifier 驻留 | 每个 VM 各自保存 | PermInterner 全局 append-only, 无锁读 |
|
||||
| 对象 Shape | 每个 VM 重复生成 | ShapeForge 跨 VM 共享 |
|
||||
| 编译结果 | 每次请求重新编译 | CodeForge 按结构复用 |
|
||||
| 属性访问模板 | 每个 VM 冷启动 | PropForge 可共享访问模板 |
|
||||
| 请求隔离 | 需要小心拆分 | VM 自带寄存器、栈、arena,天然隔离 |
|
||||
| 编译结果 | 每次请求重新编译 | CodeForge 按 structural hash 缓存复用 |
|
||||
| 属性访问模板 | 每个 VM 冷启动 | PropForge 跨 VM 共享 IC 模板 |
|
||||
| 请求隔离 | 需要小心拆分 | VM 自带寄存器、栈、Epoch Arena、JsString GC, 天然隔离 |
|
||||
| 测试间重置 | 整个 runtime 销毁重建 | `KernelSession::full_reset()` 重建 builtin + global, 保留缓存 |
|
||||
|
||||
#### 2.5.2 多 VM 执行示例
|
||||
|
||||
@@ -449,11 +481,12 @@ Request B: "users.map(u => u.name)"
|
||||
Request C: "orders.map(o => o.price)"
|
||||
|
||||
+-------------------+
|
||||
| OxideKernel |
|
||||
| OxideKernel |
|
||||
| (Core + Session) |
|
||||
|-------------------|
|
||||
| CodeForge | 结构相似代码可命中编译缓存
|
||||
| CodeForge | 结构相似代码命中编译缓存
|
||||
| ShapeForge | 相似对象布局共享 Shape
|
||||
| StringForge | id/name/price 等字符串驻留
|
||||
| PermInterner | id / name / price 等 key 驻留
|
||||
+---------+---------+
|
||||
|
|
||||
+---------------+---------------+
|
||||
@@ -461,6 +494,8 @@ Request C: "orders.map(o => o.price)"
|
||||
v v v
|
||||
VM #1 VM #2 VM #3
|
||||
Request A Request B Request C
|
||||
|
||||
各 VM 私有: 寄存器 / Epoch Arena / JsString GC / 异常状态
|
||||
```
|
||||
|
||||
### 2.6 对象模型与属性访问
|
||||
@@ -614,16 +649,16 @@ crates/oxide_types/src/
|
||||
|
||||
```text
|
||||
crates/oxide_kernel/src/
|
||||
├── kernel.rs # OxideKernel, KernelConfig, global object
|
||||
├── string_forge.rs # 字符串驻留和索引
|
||||
├── shape_forge.rs # Shape 创建、复用和查询
|
||||
├── code_forge.rs # CompiledModule 缓存
|
||||
├── prop_forge.rs # 属性访问模板缓存
|
||||
├── builtin.rs # BuiltinWorld 与内置对象结构
|
||||
└── logging.rs # 结构化日志支持
|
||||
├── kernel.rs # KernelCore + KernelSession + KernelConfig + global object
|
||||
├── string_forge.rs # PermInterner — append-only key / identifier 驻留
|
||||
├── shape_forge.rs # ShapeForge — Shape / Hidden Class 创建与查询
|
||||
├── code_forge.rs # CodeForge — CompiledModule LRU 缓存
|
||||
├── prop_forge.rs # PropForge — 属性访问 IC 模板缓存
|
||||
├── builtin.rs # BuiltinWorld + BuiltinSnapshot 选择性重建
|
||||
└── kernel_log.rs # Kernel 层结构化日志包装
|
||||
```
|
||||
|
||||
`oxide_kernel` 是单 Kernel 多 VM 的中心。它保存长期共享状态,而 VM 保存短期执行状态。
|
||||
`oxide_kernel` 是单 Kernel 多 VM 的中心。它通过 `KernelCore` 保存永久共享状态, 通过 `KernelSession` 保存可重建的会话级状态, 而 VM 保存短期执行状态。
|
||||
|
||||
### 3.5 `oxide_compiler`
|
||||
|
||||
@@ -679,6 +714,7 @@ VM 层是代码量最大的模块,包含:
|
||||
```text
|
||||
cargo run --release -p oxide_cli -- eval "1 + 2"
|
||||
cargo run --release -p oxide_cli -- run file.js
|
||||
cargo run --release -p oxide_cli -- run file.js --repeat 1000 # 单进程内复用 Kernel 重跑 N 次
|
||||
cargo run --release -p oxide_cli -- compile -e "1 + 2"
|
||||
cargo run --release -p oxide_cli
|
||||
```
|
||||
@@ -688,6 +724,7 @@ CLI 主要用于:
|
||||
- 手工运行 JS 片段;
|
||||
- 打印编译后的字节码;
|
||||
- 执行文件;
|
||||
- `run --repeat N`: 单进程内复用 Kernel 连续 eval N 次 (commit `05d0c25`), 用于多会话基准测试;
|
||||
- 进行 smoke test。
|
||||
|
||||
### 3.8 `oxide_test262`
|
||||
@@ -717,7 +754,7 @@ Developer / CLI / test262
|
||||
oxide_parser
|
||||
|
|
||||
v
|
||||
oxide_compiler -----> OxideKernel.CodeForge
|
||||
oxide_compiler -----> KernelCore.CodeForge
|
||||
| |
|
||||
v |
|
||||
CompiledModule <---------------+
|
||||
@@ -726,7 +763,8 @@ Developer / CLI / test262
|
||||
VmPool selects Vm
|
||||
|
|
||||
v
|
||||
Vm executes bytecode -----> OxideKernel.StringForge / ShapeForge / PropForge / BuiltinWorld
|
||||
Vm executes bytecode -----> KernelCore: PermInterner / ShapeForge / PropForge
|
||||
----> KernelSession: BuiltinWorld / global object
|
||||
|
|
||||
v
|
||||
JsValue / Error / TestResult
|
||||
@@ -737,7 +775,7 @@ Developer / CLI / test262
|
||||
| 创新点 | 代码位置 | 说明 |
|
||||
|--------|----------|------|
|
||||
| 单 Kernel 多 VM | `oxide_kernel::kernel`, `oxide_vm::vm_pool`, `oxide_vm::vm` | Kernel 共享,VM 隔离 |
|
||||
| 共享字符串驻留 | `string_forge.rs` | 跨 VM 字符串复用 |
|
||||
| Key / identifier 驻留 | `string_forge.rs` (`PermInterner`) | 跨 VM append-only 驻留, 无锁读 |
|
||||
| 共享 Shape | `shape_forge.rs`, `shape.rs` | 跨 VM 对象布局复用 |
|
||||
| 编译缓存 | `code_forge.rs`, `hash.rs` | structural hash -> CompiledModule |
|
||||
| 属性访问缓存 | `prop_forge.rs`, `vm_props.rs`, `dispatch/property.rs` | Shape / offset 快速路径 |
|
||||
@@ -759,7 +797,7 @@ Developer / CLI / test262
|
||||
|
||||
### 4.1 测试套件设计
|
||||
|
||||
OxideJS 采用多维度 benchmark 体系,针对 Agent 场景的特性进行验证:
|
||||
在真实场景下,JS 引擎 “好不好用”,并不是只看代码执行的准确率,其运行速度和资源占用情况也非常重要。为此,我们采用了一套多维度的 benchmark 打分体系,针对 AI Agent 场景的特性进行验证:
|
||||
|
||||
| 维度 | 测试套件 | 测试项 | 权重 | 说明 |
|
||||
|------|---------|--------|------|------|
|
||||
@@ -783,7 +821,7 @@ $$
|
||||
- $P_{\text{pass}}$: test262 通过测试数 (of ran, 不计 skip)
|
||||
- $P_{\text{fail}}$: test262 失败测试数
|
||||
- **目标**: $\geq 60$ (ES2020 核心特性覆盖)
|
||||
- **当前** (2026-06-28): 9,462 pass / 51,834 ran = **18.3%**
|
||||
- **当前** (2026-06-30): **pass 率 69.6%**, 已超出 v1.0 里程碑目标
|
||||
|
||||
#### 4.2.2 性能得分 (Performance)
|
||||
|
||||
@@ -818,6 +856,19 @@ $$
|
||||
- $M_{\text{peak}}$: 压力测试峰值 RSS (MB)
|
||||
- 目标: < 150MB 得满分
|
||||
|
||||
#### 4.2.5 四引擎综合得分对比
|
||||
|
||||
以 QuickJS = 80 为基准, 各引擎按上述四维度加权得到的综合分数:
|
||||
|
||||
| 引擎 | 综合得分 |
|
||||
|------|----------|
|
||||
| QuickJS | 80 |
|
||||
| BOA | 69 |
|
||||
| **OxideJS (本项目)** | **61** |
|
||||
| V8 (Node.js) | 53 |
|
||||
|
||||
OxideJS 当前 61 分, 介于 BOA 与 V8 之间, 主要由 test262 通过率 (69.6%, 仍有 30% 失败) 拖累, 这是我们之后优化的工作重点。
|
||||
|
||||
### 4.3 Agent 场景专用加权
|
||||
|
||||
针对多轮对话 / 跨 session 缓存:
|
||||
@@ -836,58 +887,131 @@ $$
|
||||
|
||||
### 4.4 Baseline 对比
|
||||
|
||||
| 引擎 | test262 pass% | JetStream 2.0 | 启动 (ms) | 内存 (MB) | 备注 |
|
||||
|------|--------------|---------------|-----------|-----------|------|
|
||||
| **QuickJS** | ~99% | 基准 (1.0x) | 冷 80, 热 40 | ~120 | ES2020 几乎全覆盖 |
|
||||
| **Boa v0.21** | ~85% | 0.6x | 冷 150, 热 90 | ~200 | Rust 实现,栈式 VM |
|
||||
| **JerryScript** | ~98% | 0.8x | 冷 60, 热 30 | ~80 | 嵌入式优化 |
|
||||
| **OxideJS (当前)** | 18.3% | 未测 | 未测 | 未测 | Phase 14.2.3 |
|
||||
| **OxideJS (目标)** | 60%+ | 0.8–1.2x | 冷 < 100, 热 < 50 | < 150 | v1.0 里程碑 |
|
||||
#### 4.4.1 综合对比表
|
||||
|
||||
### 4.5 当前状态分析
|
||||
| 引擎 | test262 pass% | 单条 JS 平均耗时 (ms) | 多会话单次平均 (ms) | 单进程基线 RSS (MB) |
|
||||
|------|--------------|----------------------|---------------------|---------------------|
|
||||
| **QuickJS** | ~99% | 0.95 / 3.18 | 1.03 | 1.60 |
|
||||
| **Boa v0.21** | ~85% | 11.32 / 16.29 | 16.3 | 1.55 |
|
||||
| **V8 (Node.js)** | ~100% | 103.74 / 34.95 | 103.5 | 1.60 |
|
||||
| **OxideJS (当前)** | **69.6%** | **1.73 / 4.62** | **0.96** | **4.6** |
|
||||
|
||||
**test262 通过率 18.3%** 的主要失败原因(基于 `.planning/codebase/CONCERNS.md` 分析):
|
||||
> 单条 JS 平均耗时格式: *Intel 8259CL / 鲲鹏 920* (ms), 取自 `benchmark/run_benchmark.py` 的 10 类语句测试均值。
|
||||
|
||||
1. **Compiler counter/emitter drift** (P0, 预计影响 4000–6000 测试)
|
||||
- 问题:`counter/*` 和 `emitter/*` 指令计数不对齐 → `projected_pc` 漂移 → 跳转落错 PC → 死循环
|
||||
- 已知 9 个具体 drift 位置:私有方法 (C2/C3)、计算键 (C4/C5)、解构参数 (C1/D1/S2)、`??` 和复合赋值 (E1/E2)
|
||||
#### 4.4.2 多会话耗时 (4 引擎 0–20K 会话)
|
||||
|
||||
2. **Structural hash cache collision** (P1, 预计影响 2000–4000 测试)
|
||||
- 问题:`code_forge.rs` 用 `structural_hash(program)` 做 bytecode cache key,但 hash 逻辑忽略 identifier 名字 → 不同变量引用拿到错误的缓存字节码
|
||||

|
||||
|
||||
3. **缺失高频 builtin** (预计影响 1500–2500 测试)
|
||||
- `Array.prototype.toSorted/toReversed/toSpliced/with` (ES2023 不可变数组)
|
||||
- `Object.groupBy/Map.groupBy` (ES2024)
|
||||
- 其他零散 builtin (`String.isWellFormed`, `Atomics.pause`)
|
||||
在 0–20,000 会话的连续负载下:
|
||||
|
||||
4. **for-in/for-of 枚举顺序** (预计影响 1000–1800 测试)
|
||||
- ES2015+ 规定 own-property 枚举顺序(integer keys 升序 → 字符串 keys 插入序 → symbols)
|
||||
- **OxideJS** 单进程共享 Kernel, 累积均值 **0.96 ms / 会话**, 40 个批次点全部落在 ≤ 1.2 ms 区间。
|
||||
- **QuickJS** 每会话独立进程, ≈ 1.03 ms / 会话, 约为 OxideJS 的 1.07×。
|
||||
- **BOA** ≈ 16.3 ms / 会话 (≈ 17× OxideJS), 实测至 5K 会话后按稳态斜率外推。
|
||||
- **V8 (Node.js)** ≈ 103.5 ms / 会话 (≈ 108× OxideJS), 7,500 会话后中止 (单次启停成本过大), 之后按稳态斜率外推。
|
||||
|
||||
### 4.6 测试复现命令
|
||||
OxideJS 通过 Kernel + VM Pool 单进程长驻消除了每会话 `fork/exec` 的开销, 是 4 引擎中唯一在 Agent 短会话场景下保持低延迟可持续的方案。
|
||||
此外,由图中数据可以看出,我们 OxideJS 在面对这类多会话的情况时,依然可以保持快速响应执行,其耗时明显少于其他引擎
|
||||
|
||||
#### 4.4.3 多会话内存占用 (峰值 RSS)
|
||||
|
||||

|
||||
|
||||
- **OxideJS** 单进程长驻, 典型会话 **基线 4.6 MB**; JSON Parse 工作负载偶发出现峰值, 但每次峰值后下一批次立即被 **Epoch Arena O(1) 回收** 至 4.6 MB, 不退化为系统调用。然而内存占用总量依然偏大,内存分配与回收机制仍需改进。
|
||||
- **QuickJS / V8 / BOA** 每会话独立进程, RSS 由进程边界天然回收, 长期保持 ≈ 1.5–1.6 MB。
|
||||
|
||||
考虑到目前的 PC、服务器甚至嵌入式设备的内存并不短缺,在实际体验中反而更注重耗时,因此我们的工作重心主要在解决耗时上,其运行时涉及到许多对象内容的复用,所以我们的方案在内存占用上比其他方案略高
|
||||
|
||||
#### 4.4.4 单条 JS 执行耗时 (双平台对比)
|
||||
|
||||
10 类基础语句操作 (算术 / 变量 / 对象 / 数组 / 函数调用 / 属性访问 / 条件分支 / for / while / 字符串拼接) 在两个平台上的对比:
|
||||
|
||||
| 平台 | 图表 |
|
||||
|------|------|
|
||||
| 鲲鹏 920 (ARM64) |  |
|
||||
| Intel Xeon 8259CL (x86_64) |  |
|
||||
|
||||
关键观察:
|
||||
|
||||
- **OxideJS** 在两个平台均稳定: 鲲鹏 ≈ 4.6 ms, Intel ≈ 1.73 ms (Intel 上 2.7× 快于鲲鹏,后期我们会更加关注国产平台的优化)。
|
||||
- **QuickJS** 在 Intel 上 ≈ 0.97 ms (略快于 OxideJS), 但在 ARM64 上随操作类型波动 (1.5–3.5 ms)。
|
||||
- **BOA** 两平台均 ≈ 11–17 ms, 慢于 OxideJS 约 5–10×。
|
||||
- **V8** 两平台均 ≈ 35 ms (鲲鹏) / 104 ms (Intel)。
|
||||
|
||||
#### 4.4.5 压力测试 (双平台对比)
|
||||
|
||||
8 项压力测试 (array / call / code_forge / coercion / gc / ic_cache / property / string):
|
||||
|
||||
| 平台 | 图表 |
|
||||
|------|------|
|
||||
| 鲲鹏 920 (ARM64) |  |
|
||||
| Intel Xeon 8259CL (x86_64) |  |
|
||||
|
||||
关键观察:
|
||||
|
||||
- **code_forge / ic_cache / property**: OxideJS 凭借 Kernel 共享 bytecode cache + IC 缓存, 显著快于 QuickJS / BOA (鲲鹏: code_forge OxideJS 2.4 ms vs QuickJS 37.1 ms vs BOA 127.6 ms)。
|
||||
- **gc / array / string**: OxideJS 在重 GC 场景下慢于 QuickJS (Tracing GC 暂未对短命对象做 generational 优化), 这是 v1.1 的优化方向。
|
||||
- **call**: OxideJS ≈ 2.5–3.4 ms, 介于 QuickJS (≈ 1.2–2.0 ms) 与 BOA (≈ 12–20 ms) 之间。
|
||||
|
||||
### 4.6 测试命令
|
||||
|
||||
我们提供现成的测试脚本
|
||||
|
||||
```bash
|
||||
# 正确性测试 (全量,约 1 小时)
|
||||
cargo run --release -p oxide_test262 -- --supervise "tests/test262/test" ""
|
||||
# 测试前构建环境
|
||||
bash benchmark/build.sh
|
||||
|
||||
# 代表性桶测试 (约 5 分钟)
|
||||
cargo run --release -p oxide_test262 -- "tests/test262/test" "language/expressions/class"
|
||||
|
||||
# 性能基准测试
|
||||
# benchmark
|
||||
python3 benchmark/run_benchmark.py
|
||||
|
||||
# 启动速度测试 (20 次取平均)
|
||||
for i in {1..20}; do time cargo run --release -- eval "1;"; done
|
||||
|
||||
# 内存占用测试
|
||||
cargo run --release -- run tests/stress/array.js &
|
||||
PID=$!; while kill -0 $PID 2>/dev/null; do ps -p $PID -o rss=; sleep 0.1; done
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
[TODO] 研发过程遇到的问题和解决方法
|
||||
## 五、研发过程遇到的问题和解决方法
|
||||
|
||||
[TODO] AI 使用情况说明
|
||||
### Compiler counter / emitter 指令计数漂移
|
||||
|
||||
#### 现象
|
||||
|
||||
OxideJS 编译器采用 **两遍编译 (counter pass + emitter pass)**: 第一遍 `counter` 仅遍历 AST 计算每个跳转 / 循环 / try-catch 边界的 *预计 PC*, 不实际写入字节码; 第二遍 `emitter` 才生成定宽指令。这种结构本来用于一次性预分配跳转表、减少回填, 但要求 **两遍对同一段 AST 必须发出严格相同数量的指令**。
|
||||
|
||||
在功能开发和特性实现阶段, 我们持续观察到 test262 上以下高频失败模式:
|
||||
|
||||
- `language/expressions/class` 桶大量超时, 子进程被 `--supervise` watchdog kill。
|
||||
- 解构参数 / `??` / 复合赋值 / 私有方法 / 计算键 (`class { [computed]() {} }`) 等含 *条件跳转* 的语法点反复出现死循环。
|
||||
- 单步执行 `oxide_cli compile -e "..."` 与 `oxide_cli run` 出现 PC 落在指令字段中间字节的诡异错误。
|
||||
|
||||
#### 根因
|
||||
|
||||
通过对 commit `6d52c46`, `242e94a`, `6a77742` 三处 fix 的 diff 比较, 我们识别出有 **9 个具体语法点** 上 counter 与 emitter 发出的指令条数不一致。例如:
|
||||
|
||||
- 派生类构造器 (`class B extends A { constructor() { ... } }`) 字段注入位置, counter 漏数了 1 条 `LOAD_CONST`;
|
||||
- `?? =` 复合赋值, counter 把短路分支算成 2 条, 但 emitter 实际生成 3 条;
|
||||
- `for (var x of arr)` 的 var-init 无初始值时, counter 跳过, emitter 仍发了 1 条 `LOAD_UNDEFINED`。
|
||||
|
||||
每个漂移点导致 `projected_pc` 与真实 PC 偏差 1–N 条指令。该偏差被写入跳转目标后, 跳转指令落到下一条指令的中间字节, 解码出错误 opcode → 通常误命中 `JMP_IF_FALSE` 跳回, 形成死循环。
|
||||
|
||||
#### 解决思路
|
||||
|
||||
1. **在 `Vm.dispatch_loop` 增加 debug-only `projected_pc` 断言**: 每条 emit 指令时同步检查 counter 预测的 PC 与实际写入的 PC 是否一致, 不一致直接 panic 并打印 AST 节点路径。
|
||||
2. **在 `crates/oxide_compiler/tests/compiler_alignment_*_tests.rs` 添加 90+ 条对齐回归测试**: 每个语法点用 `compile_and_diff_counts(src)` 直接对比两遍输出的指令数量, 防止未来语法扩展时再次漂移。
|
||||
3. **逐点对齐 9 个漂移位置** (C1–C5, D1, S2, E1–E2): 修复方法主要分两类 — counter 加 1 条计数, 或 emitter 删 1 条冗余指令, 取决于哪种更接近 ES 规范的语义。
|
||||
|
||||
修复后,oxidejs 增加了 4000–6000 个测试通过, 是整个项目最关键的修复之一。
|
||||
|
||||
---
|
||||
|
||||
## 六、AI 使用情况说明
|
||||
|
||||
我们主要在部分非核心代码(如 benchmark 脚本)编写和功能测试使用 AI 以降低工作量,经由人工审核和review后提交, 核心功能代码均为人工代码。此外, AI 在非代码方面的使用,主要用于 commit message 编写与文档语言润色。
|
||||
|
||||
---
|
||||
|
||||
## 七、未来计划
|
||||
|
||||
1. **提升国产平台的优化**: 针对鲲鹏 / 飞腾 / 龙芯 等国产平台进行专门优化,让 OxideJS 更适合国产平台。
|
||||
2. **打磨内存分配算法**: 让内存占用更稳定、更少, 把 JSON Parse 等工作负载的峰值 RSS 进一步压缩。
|
||||
3. **继续提升 test262 pass 率**: 目标 85% 以上, 重点补齐 ES2023+ 不可变数组方法、Proxy / Reflect 语义、枚举顺序边角。
|
||||
4. **针对 AI Agent 环境进行优化**: 让 OxideJS 在 Vibe Coding 工作流下比其他引擎更方便好用。
|
||||
|
||||
---
|
||||
|
||||
|
||||
+24838
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user