跳转到正文

基准测试

在受调度开销限制的 JavaScript 中,Phasis 的速度大约比 V8 慢两个数量级。这一上限源于托管在 PHP 中的解释器与本机 JIT 编译器之间固有的开销差异。对嵌入者来说,问题不是“它与 V8 相比如何”,而是“它对我的用途来说够快吗”。对于大多数嵌入式工作负载,答案是肯定的。

何时性能上限无关紧要

  • 模板渲染——典型页面的 SSR 风格渲染远低于 10 ms。
  • 验证规则——用户提供的 JS 表单验证可在微秒内完成。
  • 数据转换——对数百个项目执行 array.map(transform) 可在几十毫秒内完成。
  • 沙箱运行器——短小的用户脚本即使受到宿主 1 秒的时间限制,仍有充足余量。

何时性能上限会造成影响

  • 计算密集型内层循环——图像处理、大型数组归约、模拟。将计算移至本机 PHP 库,并将其作为 宿主函数公开。
  • 实时动画——任何驱动 60 fps 的内容。此类 JS 应放在客户端(浏览器、本机应用)中,而不是嵌入式引擎里。
  • 用 JS 重写计算密集型算法——加密、压缩、解析。PHP 有速度快 1000× 的本机实现;请使用这些实现。

正确的理解方式是:Phasis 是一层胶水层,用于在 PHP 形态的应用中嵌入 JS 形态的逻辑。PHP 宿主负责计算;JS 层负责配置、分支和小规模转换。

引擎相较于朴素树遍历器的速度优势来自哪里

  • 字节码 VM — 大多数热点分发都经过 VM::execute 的操作码 switch,而不是 AST。
  • 自定义调用栈 — 每次 JS 调用都会将状态保存在 VM::execute 中,并把调用方的帧存入池中;每次 JS 调用不会新建 PHP 帧。参见 自定义调用栈(字节码 VM)。
  • 内联缓存 — 每个 PC 都有单态缓存,使原型方法在实例上的读取(c.method() 形态)跳过原型链遍历。
  • 帧快照生成器 — 简单的生成器函数体通过将分发状态捕获到堆对象中来挂起,无需跨越 PHP Fiber。
  • JsToPhp 转译器 — 数值运算和局部变量密集型函数体(包括 obj.method()、new Ctor() 和 this.member 读取)会编译成 PHP 闭包,再由 PHP 8.5 的跟踪 JIT 降为本机机器码。
  • JSON.stringify 缓存 — JSON.stringify(o) 会在输入对象上缓存输出,并通过传递式版本失效机制加速深克隆惯用写法 JSON.parse(JSON.stringify(o))。

测量你自己的工作负载

命令行
php bench/run.php

这会运行 bench/microbench.js 随附的微基准测试套件——其中包含少量合成工作负载(循环算术、递归函数、对象创建、原型方法调用、数组 push、JSON 往返等),并打印每项测试的中位墙钟时间。可用于基本 sanity check;每次 CI 基准测试运行的当前数据都会提交到 BENCH.md。

对于你自己的代码,替换为任意脚本:

命令行
php -d xdebug.mode=off bench/run.php path/to/your-script.js

若要进行 PHP 级性能分析,Xdebug 的分析器可像分析其他 PHP 应用一样,直接分析 Phasis。在 xdebug.mode=profile 下运行脚本并检查 cachegrind 输出,即可查看哪些 Phasis\ 类占主导。

路线图

按实现难度大致排序,哪些改进能进一步缩小 V8 差距:

  • 多态内联缓存 — 允许单个 LOAD_MEMBER 调用点在回退到慢速分发前缓存 2-4 种形态。适用于处理不同接收者的代码。
  • yield* 以及帧快照生成器路径中的 try-with-yield — 这些形态目前会转到基于 Fiber 的树遍历器。要支持快照,需要显式处理状态机(转发委托迭代器、在挂起期间保留处理器栈)。
  • 异步生成器以及快照上的 await — 规范将生成器挂起与微任务队列组合在一起。字节码函数体支持 yield 和 await 后,异步生成器也能支持快照。
  • JsToPhp 中的参数类型推断 — 函数参数默认采用数值类型,因此 function (obj) { obj.method() } 会导致编译器退出。根据参数在函数体中的用法推断对象类型,就能支持对函数参数调用方法,而无需通过 new 在本地构造接收者。
  • 直接生成 opcache — 直接写入 opcache 格式的操作码,跳过 JsToPhp 的 eval() 步骤。这是最深入的改动;其他改进都能在现有架构内完成。

对于 Phasis 面向的嵌入式工作负载,这些改进都不是必需的。