
# 基准测试

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

## 何时性能上限无关紧要

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

## 何时性能上限会造成影响

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

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

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

- **字节码 VM** — 大多数热点分发都经过 `VM::execute` 的操作码 switch，而不是 AST。
- **自定义调用栈** — 每次 JS 调用都会将状态保存在 `VM::execute` 中，并把调用方的帧存入池中；每次 JS 调用不会新建 PHP 帧。参见 [自定义调用栈（字节码 VM）](https://phasis.inline0.dev/zh/docs/advanced/bytecode-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))`。

## 测量你自己的工作负载

```bash
php bench/run.php
```

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

对于你自己的代码，替换为任意脚本：

```bash
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 面向的嵌入式工作负载，这些改进都不是必需的。
