PHP也能Native AOT编译了!Swoole-Compiler让PHP代码直接变成机器码,性能提升150倍
2026年4月22日,Swoole 团队正式发布了 Swoole-Compiler v4 的 Native AOT 编译器,将 PHP 语言带入了"原生编译"时代。
这不是 Swoole 第一次让 PHP 变快——但这是第一次,让 PHP 直接"消失"在编译后的二进制里。
长期以来,PHP 被认为是"性能差"的语言。即使用了 OPcache + JIT,在密集计算场景下仍然无法与 Rust、Golang 等编译型语言相提并论。
Swoole-Compiler Native AOT 编译器彻底改写了这个结论:将 PHP 代码直接编译为原生机器指令,斐波那契数列测试中性能提升 150 倍,与 Rust、Golang 站在了同一水平线。
这意味着什么?
PHP 开发者不需要学 Rust,不需要重写项目,只需要用 AOT 编译器重新编译一次,现有 PHP 代码就能获得与编译型语言相当的执行效率。
一、为什么 PHP 一直"慢"?
要理解 AOT 编译的价值,先要理解 PHP 为什么"慢"。
解释型语言的宿命
PHP 是解释型脚本语言。每次执行 PHP 代码时,都需要经过以下流程:
- 词法分析:将 PHP 源代码转换为 token
- 语法分析:将 token 解析为 AST(抽象语法树)
- 编译:将 AST 编译为 Zend OPcode(中间指令)
- 解释执行:ZendVM 逐条执行 OPcode
这个流程在每次请求时都会重复执行——即使代码从未改变。
OPcache:解决了重复编译,但没有解决执行
PHP 7 引入的 OPcache 扩展,通过将编译后的 OPcode 缓存到内存中,解决了重复编译的问题。但执行阶段并没有变快——ZendVM 仍然要逐条"解释"OPcode。
JIT:让解释执行变快,但仍然受限
PHP 8 引入的 JIT(Just-in-Time)编译器,将热点 OPcode 动态编译为机器码,显著提升了执行效率。但 JIT 有两个根本局限:
- 启动开销:JIT 需要在运行时分析热点代码,冷启动时没有加速
- 内存开销:JIT 编译的机器码需要占用额外内存
更重要的是,JIT 优化的是"运行时",但 PHP 的执行框架本身仍然是一台虚拟机。
性能的"玻璃天花板"
用斐波那契数列测试可以看到:
| 执行方式 | 计算 fib(40) 时间 |
|---|---|
| PHP ZendVM(无 JIT) | 14.82 秒 |
| PHP ZendVM + JIT | 2.37 秒 |
| AOT 编译后 | 0.11 秒 |
从 14.82 秒到 0.11 秒,AOT 编译器实现了 135 倍的加速,比 JIT 快了 21 倍。
这不是微优化,这是数量级的跃升。
二、Native AOT 编译:绕过虚拟机,直达机器码
什么是 AOT 编译?
AOT(Ahead-of-Time,预先编译)是一种编译策略:在程序运行之前,就把源代码编译为机器码。
与 JIT 的区别在于:
- JIT:运行时编译,需要运行时环境(PHP 本身)
- AOT:编译时完成,不需要运行时环境,生成独立的二进制可执行文件
Swoole AOT 的工作原理
Swoole-Compiler AOT 编译器的工作流程:
PHP 源代码 → [clang-format 格式化] → C++ 中间代码 → [g++ 编译] → 原生二进制
具体步骤:
1. 准备阶段(prepare)
扫描 PHP 源代码,解析语法结构,生成编译器内部表示。
2. 转换阶段(convert)
将 PHP 代码转换为 C++ 代码。生成的 C++ 代码调用 PHPX 库,PHPX 是 ZendPHP 提供的 C++ 兼容层,负责处理 PHP 的类型系统、内存管理、函数调用等底层机制。
3. 编译阶段
调用系统 C++ 编译器(g++)将 C++ 代码编译为原生二进制文件。编译过程支持多任务并行(-j 参数),充分利用多核 CPU。
与 HHVM、KPHP 的根本区别
过去也有将 PHP 编译为原生代码的项目,最著名的是 Facebook 的 HHVM 和 VKontakte 的 KPHP。但它们的共同问题是:用另一个实现替代了官方 PHP 解释器。
这带来了严重的兼容性问题:
- 不支持某些 PHP 语法特性
- 不支持某些 PHP 扩展
- 与 Composer、autoload 等生态工具不兼容
Swoole AOT 编译器采用了完全不同的路线:
直接使用 ZendPHP 的底层库,通过 PHPX 库作为兼容层,在 ABI 层面与 PHP 完全互通。
这意味着:
- ✅ 可以调用所有 PHP 扩展的内置函数、类和常量
- ✅ 支持
eval、include、require等动态脚本执行 - ✅ 支持 Composer,支持 autoload
- ✅ 可以动态调用第三方 Composer 包提供的函数和类
AOT 编译器并不是 PHP 的另一个实现,而是 PHP 生态的原生编译加速器。
三、性能实测:斐波那契 150 倍,PI 计算 72 倍
斐波那契数列测试
<?php
function fib(int $n): int {
if ($n == 1 || $n == 2) return 1;
return fib($n - 1) + fib($n - 2);
}
function main(int $argc, array $argv): void {
$n = $argv[2];
$begin = microtime(true);
echo fib($n) . "\n";
echo "Time: " . (microtime(true) - $begin) . "\n";
}
编译命令:./swoole-compiler examples/fib.php -O3
执行结果对比:
| 执行方式 | fib(40) 耗时 | 相比 ZendVM |
|---|---|---|
| PHP ZendVM(无 JIT) | 14.82 秒 | 1x |
| PHP ZendVM + JIT | 2.37 秒 | 6.3x |
| AOT 编译(O3优化) | 0.11 秒 | 135x |
PI 值计算测试(Leibniz 公式)
<?php
use native_types; // 启用原生类型,精度与 C++ 一致
function main() {
$rounds = (int) file_get_contents("./rounds.txt", true);
$stop = $rounds + 2;
$x = 1.0;
$pi = 1.0;
for ($i = 2; $i <= $stop; $i++) {
$x = -1.0 + 2.0 * ($i & 0x1);
$pi += $x / (2 * $i - 1);
}
$pi *= 4.0;
print $pi . "\ntime: " . (microtime(true) - $begin) . "\n";
}
执行结果对比(1亿轮迭代):
| 执行方式 | 耗时 | 加速比 |
|---|---|---|
| PHP ZendVM | 6.52 秒 | 1x |
| PHP ZendVM + JIT | 2.24 秒 | 2.9x |
| AOT 编译 | 0.09 秒 | 72x |
JIT vs AOT:不是竞争,是互补
有一点需要澄清:JIT 在某些场景下表现更好(尤其是 PHP 自带的 bench.php 测试),原因是 OPcache 的死代码消除优化器会移除未实际执行的代码。
这说明 AOT 和 JIT 是互补的技术:
- JIT:适合 Web 场景的动态请求,热点代码即时优化
- AOT:适合 CLI 工具、批量处理、科学计算等固定代码场景
四、C++ 与 PHP 的无缝互调用:素数计算的性能飞跃
AOT 编译器最有想象力的功能,是允许在 PHP 代码中直接调用 C++ 函数,两者一起编译为单个可执行文件。
这解决了一个长期困扰 PHP 开发者的难题:如何用高性能 C++ 代码处理 PHP 的密集计算?
素数计算的性能对比
用 PHP 数组存储 1000 万个布尔值来筛选素数:
// 纯 PHP 实现
$isPrime = array_fill(0, $limit + 1, true);
PHP 的数组是哈希表,即使存储一个布尔值,也至少占用 16 字节。存储 1000 万个值需要 40GB+ 内存。
而用 C++ 的 std::vector<bool>(位图)只需要 120MB 内存。
AOT 编译器的互调用设计,让 PHP 开发者可以直接使用 C++ 的高效数据结构:
PHP 代码(调用 C++ 函数):
// 使用 C++ vector 替代 PHP 数组
$isPrime = vector_new($limit + 1, true);
vector_set($isPrime, 0, false);
vector_set($isPrime, 1, false);
// 使用 C++ 逻辑处理
for ($i = 2; $i * $i <= $limit; $i++) {
if (vector_get($isPrime, $i)) {
for ($j = $i * $i; $j <= $limit; $j += $i) {
vector_set($isPrime, $j, false);
}
}
}
C++ 实现(.cc 文件):
#include <phpx.h>
using namespace php;
class VectorBox : public Box {
public:
std::vector<bool> vec;
VectorBox(size_t size, bool init) { vec.resize(size, init); }
void checkOffset(Int offset) {
if (offset >= vec.size()) {
throwError("index[%ld] is out of range()", offset);
}
}
};
var php_vector_new(Int size, Bool init) { return {new VectorBox(size, init)}; }
Bool php_vector_get(var box, Int offset) {
auto vecbox = box.toBox<VectorBox>();
vecbox->checkOffset(offset);
return vecbox->vec.at(offset);
}
void php_vector_set(var box, Int offset, Bool value) {
auto vecbox = box.toBox<VectorBox>();
vecbox->checkOffset(offset);
vecbox->vec.at(offset) = value;
}
函数声明(.stub.php 文件):
<?php
function vector_new(int $size, bool $init = false): mixed {}
function vector_get(mixed $vector, int $offset): bool {}
function vector_set(mixed $vector, int $offset, bool $value): void {}
类型映射规则简单清晰:
| PHP 类型 | C++ 类型 |
|---|---|
| int | php::Int |
| bool | php::Bool |
| array | php::Array |
| float | php::Float |
| string | php::Str |
| void | void |
导出函数只需满足两个条件:以 php_ 为前缀 + 使用 PHP 类型作为参数和返回值。
五、使用方法:3 步完成编译
1. 安装 Swoole-Compiler
从 Swoole 官方获取 AOT 编译器二进制文件,添加到系统 PATH。
2. 编写入口函数
AOT 编译器不支持"游离代码"(不在函数内的代码),所有代码必须放在函数内。
<?php
// 标准入口函数
function main(int $argc, array $argv): void
{
echo "Hello World!\n";
var_dump(PHP_VERSION);
}
3. 编译为二进制
# 编译为二进制可执行文件(默认模式)
./swoole-compiler app.php -O3 -o myapp
# 编译为 PHP 扩展(用于 PHP-FPM / Apache)
./swoole-compiler extension/ -m ext -o myextension.so
# 多任务并行编译(使用 8 核)
./swoole-compiler app.php -O3 -j 8
4. 优化输出大小
编译后的 ELF 文件可以通过 strip 或 upx 压缩:
# 原始大小
-rwxrwxr-x 1 swoole swoole 377K main
# strip 后
-rwxrwxr-x 1 swoole swoole 39K main
从 377KB 压缩到 39KB,体积减少 90%。
六、语法限制:哪些 PHP 特性不能用?
AOT 是静态编译,某些依赖运行时动态特性的语法无法支持:
| 限制项 | 说明 | 替代方案 |
|---|---|---|
$$ 语法 | 局部变量为编译器符号,运行时无法使用 | 使用普通变量 |
extract() 函数 | 无法在运行时创建局部变量 | 手动赋值 |
yield/generator | 生成器语法静态编译不兼容 | 使用 Fiber / Swoole 协程 |
多层 break/continue | 静态编译无法处理 | 改为 goto 或 try/catch |
字面量字符串含 \0 | 与 C++ 不兼容 | 使用字符串拼接 |
| 参数数量不匹配调用 | 动态执行阶段允许,编译阶段不允许 | 严格参数数量 |
Property Hook 语法 | 不支持 | 使用传统 getter/setter |
| 闭包引用参数 | 运行时才能确定用值还是引用 | 使用 refval() 显式指定 |
这些限制大多是 PHP 动态特性的"遗产",在实际项目中使用频率很低,绝大多数 PHP 代码库可以零修改直接编译。
七、native_types:让整数运算再快一步
PHP 的 int 类型有一个特殊设计:溢出时自动转为浮点数。
这在大多数场景下是合理的(防止数据丢失),但在密集计算场景中牺牲了性能。AOT 编译器默认保持这个特性以兼容已有 PHP 代码。
如果你的代码不需要整数溢出保护,可以在文件顶部添加:
<?php
use native_types; // 启用原生类型
function main() {
$a = 10;
$b = $a / 3;
// $b = 3(截断),而不是 3.333...(浮点)
}
启用 native_types 后,整数和浮点类型使用 C++ 原生类型,执行速度更快,但溢出时直接丢弃精度而不是转为浮点。
八、适用场景:PHP 的新战场
AOT 编译器的价值不在于"替代 PHP",而在于拓展 PHP 的适用边界:
1. CLI 工具与系统脚本
用 PHP 编写系统管理脚本、自动化工具,编译后分发给服务器,无需安装 PHP 运行时,部署极简。
2. 科学计算与数据处理
大规模数据处理、统计分析、算法验证,用 PHP 原型快速开发,AOT 编译后获得 C++ 级性能。
3. 高性能服务与微服务
对性能敏感的微服务组件(如数据清洗、消息队列消费者),用 PHP 快速开发,AOT 编译后达到生产级性能。
4. 游戏服务器逻辑
某些需要高性能但逻辑复杂的游戏逻辑(如 AI 决策、物品计算),可以用 PHP 编写,AOT 编译后部署。
5. 嵌入式与边缘计算
编译后的二进制文件体积小(strip 后仅 39KB),适合嵌入式设备和边缘计算场景。
九、总结:PHP 不再是"慢语言"
Swoole-Compiler Native AOT 编译器的发布,对 PHP 生态的意义是深远的:
对开发者:不需要学习新语言,不需要大规模重写,现有 PHP 技能和代码库直接获得编译型语言的性能。
对语言:PHP 不再只能在 Web 领域"脚本语言"的角色里打转。它有了进入高性能计算、系统工具、边缘计算领域的能力。
对生态:Composer、OPcache、Swoole 协程、AOT 编译……PHP 正在构建一个从 Web 脚本到高性能系统的完整工具体系。
150 倍的性能提升,不只是数字,是 PHP 从"解释型语言"走向"全能语言"的一次跃迁。
参考资料:
- Swoole-Compiler 官方文档:https://github.com/swoole/swoole-src
- PHPX 库:https://github.com/swoole/phpx
- ZendPHP 官方:https://www.zend.com/enterprise/zend-php