quickjs-oxide:AI 主导、零 unsafe 的 Rust 版 QuickJS,真能追上 C 吗?
AI 重写观察实测 quickjs-oxide:AI 如何参与重写 QuickJS,我们复现了哪些 Test262 与性能数据,内存与兼容性还差在哪里。
QuickJS 是 Fabrice Bellard 用 C 写的小型可嵌入 JavaScript 引擎。quickjs-oxide 用 Rust 把它重写了一遍,没有一行 unsafe。团队称大部分代码由 AI 完成,速度与最新版 QuickJS 相当。这个项目想回答的问题是:先由 AI 主导重写,再由人来做性能优化,内存安全的 Rust 能不能追上精心优化的 C 解释器?
文中的数据有两个来源:一是项目方的说法,二是我们在 2026 年 10 月 22 日自己构建、运行项目得到的结果(测试环境见文首)。下文分别标注为项目声明和本站实测。
quickjs-oxide 是什么
项目自述为“对 QuickJS 的独立 Rust 重写,目标是语义层面的功能对等”,对标的是 Bellard 发布的 QuickJS 2026-06-04,而不是社区分支 quickjs-ng。项目采用 MIT 许可证,提供 qjs 命令行工具、发布在 crates.io 上的 Rust 嵌入 API,以及驱动浏览器演示的 WebAssembly 版本。
据 README,所有 crate 都设置了 unsafe_code = "forbid",垃圾回收器、正则引擎和 Unicode 表也不例外。引擎只依赖 num-bigint 和 num-traits 两个第三方 crate,原生宿主另用 tz-rs 处理时区。现在的内核把每个函数编译成寄存器字节码,用上了内联缓存、NaN-boxing 值表示和标记-清除回收器。
AI 是怎样参与的
开发过程的说法主要来自 PocketJS(@pocket_js)10 月 20 日 23:16(UTC+8)发的 X 帖子。帖子称引擎“由 AI 用 Rust 从零实现”,整个过程分三个阶段:
- 先用一个月把 Test262 通过率做上去;
- 再用一个月“清理代码、去掉 AI slop”,在栈式虚拟机加引用计数的架构下把性能做到有竞争力;
- 趁 RustChinaConf 那个周末,“仅用两天”把内核改写成寄存器虚拟机。
提交历史和这条时间线对得上,但看不出 AI 到底写了多少代码。仓库在 2026 年 7 月 11 日至 10 月 22 日(UTC+8)间共有 921 个提交:
- Yifeng “Evan” Wang(GitHub doodlewind)提交约 660 个,几乎都在 7、8 月;
- 第二位贡献者 GitHub 用户 Eric-Song-Nop 提交了其余 258 个中的大部分,集中在 9、10 月;
- 引入寄存器“新内核”的 PR 于 10 月 19 日合并,v0.3.0 也在同日发布。
Evan(@ewind_dev)在 X 上回顾第一阶段时说:“开坑前几周无脑 /goal 刷 Test262 完成度导致性能极差,这 slop 被 @ericoolen_eric 救回来了,性能相差上百倍……”
用的是哪些模型? 发布帖没说,仓库里也没有专门的复盘文章。提交里的署名行(trailer)能看出一部分:48 个提交署名了 AI 合著者,其中 44 个为 “Claude Opus 5.5”,2 个为 “Claude Fable 5”,2 个为 “Claude Code”,全部出现在 9 月之后。7—8 月的提交没有 AI 署名,所以第一阶段用的是什么 agent,公开资料里没有交代;各阶段的具体工时和 token 成本也没有披露。仓库里的 AGENTS.md 是一份写给 agent 的严格基准测试规程:测量必须串行、以退休指令数设门槛,测量期间“不得有并行 agent”。
兼容性:我们复现了 Test262 数字
项目声明(docs/test262.md):在 QuickJS 2026-06-04 自身测试配置所运行的 83,562 个 Test262 变体中,通过 81,441 个(97.462%);如果按全部 102,037 个变体算,通过率为 79.815%。
本站实测: 我们用 6 个 worker 跑了项目的完整 Test262 脚本。脚本会下载固定版本的 Test262,最后输出 pass=81441 fail=2121 unsupported=208 skipped=18475(2,121 个未通过中包含 208 个尚未执行的变体),脚本也确认结果与项目冻结的结果向量一致。
看这个数字时别忘了,分母用的是 QuickJS 自己的跳过清单。另外约有 1,870 个变体是故意不通过的,因为团队决定不实现五项特性(见偏差台账):with 语句直接报 SyntaxError,直接 eval 按间接 eval 执行,一个运行时只有一个 realm,引擎也从不读取 Symbol.species 和 Symbol.unscopables。我们实际试了 with:同一行 with 脚本,QuickJS 能运行,quickjs-oxide 报错。
性能:速度声明经得起复测,内存说法对不上
项目声明(README):以 QuickJS 2026-06-04 的运行时间为基准,quickjs-oxide 在 V8 v7 上为 0.94 倍,Octane 2.0 上为 1.15 倍,Are We Fast Yet(AWFY)上为 0.99 倍。这些是在一台 Linux 机器上对 37 个固定工作量程序取的几何平均。这个基准并不好追:据 Bellard 的说明,这一版 QuickJS 的 bench-v8(即 V8 v7)得分比上一版快 42%。
本站实测: 我们用默认 Makefile 从 Bellard 的源码包构建 QuickJS 2026-06-04(SHA-256 与项目固定值一致),并以 release 模式构建 quickjs-oxide,耗时 37 秒。然后用项目自带的 bench.py,从固定版本的 V8 v7、Octane 和 AWFY 源码生成同样的 37 个程序,按 ABBA 顺序各跑 4 次,并绑定到单个 CPU。相对 QuickJS 的几何平均耗时如下:
| 测试集 | 项目声明 | 本站实测 |
|---|---|---|
| V8 v7(8 个) | 0.94× | 0.91× |
| Octane 2.0(15 个) | 1.15× | 1.13× |
| AWFY(14 个) | 0.99× | 0.96× |
| 全部 37 个 | 1.04× | 1.01× |
平均值背后的差异很大。quickjs-oxide 在 Earley-Boyer、RayTrace 和 AWFY Storage 上明显更快(0.56–0.75 倍),在 Octane 的 GameBoy(2.26 倍)、CodeLoad(1.77 倍)、zlib(1.59 倍)和 Mandreel(1.45 倍)上明显更慢;在项目自带的 BigInt 微基准上,耗时是 QuickJS 的 4–15 倍。由于测试跑在共享云虚拟机上,单项比值只能作为参考。
内存: X 发布帖称内存只比 QuickJS 多“约 30%”,但 v0.3.1 发布说明给出的峰值 RSS 几何平均是 QuickJS 的 1.59 倍。我们用 /usr/bin/time 在这 37 个程序上测得的是 1.57 倍;空脚本的峰值约为 3.4 MiB,QuickJS 约为 2.6 MiB。至于“30%”指的是哪类负载,我们没有找到说明。
局限与风险
- 还不能直接替换
qjs。std和os模块、REPL、Worker、qjsc以及 C API 都尚未支持(见兼容性文档)。依赖 QuickJS C 嵌入接口的应用得改用 Rust API。 - 没有
unsafe不等于没有 bug。 安全 Rust 排除了引擎自身代码中的几类内存破坏问题,但逻辑错误、panic、拒绝服务,以及 Rust 工具链或标准库自身的问题仍可能存在。我们没有找到独立的安全审计。 - 暂不支持 32 位平台。 PocketJS 在 X 上回复称,armeabi-v7a、x86 等 32 位目标“在路线图上”,但目前尚不支持。
- 只有解释器。 它没有 JIT,团队称这“最适合嵌入式设备”。
- 代码还很年轻。 首个版本 2026 年 10 月 11 日才发布,0.x 阶段的嵌入 API 可能还会变。
X 上的反应
有人叫好,也有人质疑:
- Malte Ubl(@cramforce)问,这是否意味着解释器本身就可以信任,不必再套一层 WebAssembly 沙箱。PocketJS 回答,他们的目标是让解释器“在构造上就是内存安全的”。
- @k3rn_net 质疑这算不算成就:“只要有足够的 token 可烧,谁都能做到。”
- @EgeMustafaCelik 认为,过了 Test262 之后再花整整一个月清理代码,这个“诚实的部分”恰恰是多数 AI 写的项目会跳过的。
- @anoymous_elangm 提醒,Test262 衡量的是兼容性,耗时只反映特定负载。
- Ericoolen(@ericoolen_eric)推荐了 matklad 关于毫秒级基准测试的文章。他说如果早点读到这篇,把性能测试控制在 300 毫秒左右、不拖慢 agent 的迭代,项目早期能少走些弯路。
截至 2026 年 10 月 22 日,我们在 Hacker News 上没有找到相关讨论帖。
它说明了 AI 重写的什么
quickjs-oxide 与 tsrs 的路子相同:固定一个上游版本,把它的测试套件当作规格,让 agent 围绕它反复迭代。它的经历还补充了两点:
- 测试通过只完成了一半。第一阶段做出来的引擎兼容性没问题,但很慢,最后是人拿着性能分析工具重新设计的。
- 项目在测量纪律上下了很大功夫:冻结的 Test262 哈希、指令数门槛,还有写给 agent 的规则。我们能轻松复现它的数据,靠的就是这些。
本站目前的判断:Test262 和速度声明在我们的测试中站得住,X 上的内存说法对不上。如果你在 Rust 里嵌入 JS,想要一个不依赖 C 的 QuickJS 级引擎,可以试试;但如果应用依赖 QuickJS 的 std/os 模块或 C API,它目前还替代不了 QuickJS。