
写在前面|Swoole 团队悄悄上线了一个把 PHP 编译成原生机器码的 AOT 编译器,官方跑分 fib(40) 快了约 135 倍。这不是又一个玩具项目,而是一次「PHP 写代码、机器码运行」的大胆尝试。今天这篇文章,带你把它看明白。
—— 你的 PHP 技术观察员 Tinywan
前几天刷 Swoole 官网,发现他们悄悄上线了一个新东西:一个把 PHP 编译成原生机器码的 AOT 编译器,原来叫 Swoole AOT,现在正式改名叫 TypePHP。
看了一眼官方给的跑分,fib(40) 从 14.82 秒干到 0.11 秒,快了大概 135 倍。我第一反应是不信,第二反应是把官网翻了个底朝天。这篇文章把查到的东西整理出来,尽量说人话。
这个话题聊了十几年,其实原因不复杂,就三件事。
第一,PHP 是解释执行的。就算有 OPcache 缓存了字节码,最后还是 Zend 虚拟机一条一条地解释,这个开销省不掉。
第二,PHP 的变量全都装在 zval 这个结构里。你写 a + b,虚拟机得先看看这俩是什么类型、引用计数多少、要不要转换。换成 C++,同样的代码就是一条加法指令,没了。
第三,PHP 太灵活了。函数存不存在、参数对不对,都要跑到那一行才知道,虚拟机必须随时准备处理意外。灵活性换来的就是开销。
PHP 8 的 JIT 想解决这事,但它是运行时编译,Web 请求生命周期那么短,经常是热点代码还没编译完,请求已经结束了,实际收益很有限。
所以思路很自然:能不能别解释了,直接编译成机器码?TypePHP 就是干这个的。
原理一句话:先把 PHP 源码翻译成 C++,再用 GCC、Clang 或者 MSVC 编译成原生二进制。
编译流水线
PHP 源码 → C++ → 目标文件 → 可执行文件
这个设计挺聪明的地方在于,它不自己造编译后端。代码优化、内联、向量化这些活,GCC 干了几十年,直接白嫖。产物是真正的机器码,不是字节码,目标机器上甚至可以没有 PHP 环境。
Linux、macOS、Windows 三个平台都能出对应的原生可执行文件。
先看官方基准(PHP 8.4 ZendVM 对比 AOT 编译器 -O3):
fib(40) 递归|14.82s → 0.11s,快约 135 倍
PI 计算(1 亿次迭代)|6.52s → 0.094s,快约 69 倍
为什么递归能快到这种程度?因为 fib 这种代码全是函数调用和整数加法,一点 IO 都没有。解释模式下每次调用都要过虚拟机,编译后就是裸指令,再加上 use native_types 把 int 变成 C++ 的原生 int,zval 的包装也省了,几个因素叠起来就是这个效果。
但官方 FAQ 里自己说得很克制:密集计算场景配合 native_types,大概能提升 3 到 10 倍;IO 密集型应用只有 1 到 1.5 倍,因为瓶颈在数据库和网络,跟解释执行没关系。
说白了,你的项目如果是常规 CRUD 的 Web 应用,这玩意对你帮助不大。它真正瞄准的是图像处理、金融计算、科学运算、命令行批处理这类被 CPU 卡脖子的场景。
区别就在编译时机。OPcache 只是缓存字节码,还是虚拟机跑;JIT 是运行时挑热点代码编译,掌握的信息有限;TypePHP 是编译期一次性把所有静态可分析的代码变成机器码。
AOT 还白送一个好处:编译期类型检查。类型不对、函数没定义、参数数量不匹配,编译的时候直接报错,不用等线上半夜炸。对维护过大项目的人来说,这个可能比性能还值钱。
代价也有。eval()、create_function()、可变变量、动态属性、动态函数名,这些运行时动态特性都不支持。官方的说法是「大多数静态可分析的 PHP 代码均可正常编译」,翻译过来就是:框架里那些魔术玩法得小心。
性能之外,TypePHP 给 PHP 加了一些以前想都不敢想的东西:
use native_types 一行声明,int/float/bool 就变成 C++ 原生类型。内置了 BigInt、Decimal、BigFloat 三种高精度数值类型,底层是 mpdecimal,搞金融计算的终于不用跟 bcmath 的字符串 API 较劲了。可以直接内联 C++ 代码,调 C++ 库零 FFI 开销,还能双向调用。另外还有 vector、map 这些强类型容器,Getter/Setter、编译期注解,甚至能编译成 WebAssembly、跟 Python 互调。
这些展开讲都是独立的文章,后面会一篇一篇写。
翻官方发布动态能看到,它已经迭代了相当长时间。这里有个细节要注意:版本号分两条线。phpx 是底层的 C++ 运行时(部署时带的那个 libphpx),现在到 v2.5.x 了,中间经历了命名参数、Windows 支持、大数库、Fiber 生成器、WASM 支持这些节点;TypePHP 编译器本身是 v0.x,目前出到 v0.4.0。
现在的基线是 PHP 8.4 ZTS + embed SAPI,curl、openssl、pdo、mbstring 这些常用扩展内置支持,PECL 扩展得适配之后才能用。
官网 FAQ 的说法很直接:「完全免费,可自由使用、修改和再分发,包括商业用途。无论是个人开发者还是企业,均可免费使用,无需购买任何授权。」协议是 GPL。
说句实话|我写这篇的时候去翻了 GitHub 的 swoole/aot-compiler 仓库,里面只有 README 和示例代码,编译器源码和 LICENSE 文件都还没放出来。README 指向的其实是 Swoole Compiler——一款商业 PHP 加密保护产品,3.2 版做字节码混淆,4.0 版才是原生二进制编译。TypePHP 应该是从这条产品线里开源出来的。
所以「免费可商用」这个承诺我认为是可信的(官方白纸黑字),但源码什么时候完整放出、GPL 的具体边界,还得等正式发布。本文所有授权相关表述以官方为准。
另外给个 GPL 的常识提醒:就算正式 GPL 了,编译产物要链接运行库 libphpx,对外分发二进制的时候 copyleft 义务可能跟着走。自己公司内部用没问题,要把编译产物当产品卖给别人,得留意这事。
部署本身倒是很省事:编译产物加上 libphpx.so 扔到目标机器就能跑,不用装 PHP、不用 Composer、不用装扩展。对做交付的人来说,「交付物就一个文件」这件事本身就很诱人。
光说不练假把式,先看一个能跑的例子。TypePHP 程序约定用全局 main() 做入口:
PHP · fib.php
<?php
// fib.php
use native_types;
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
{
$begin = microtime(true);
echo fib(40) . "\n";
echo "Time: " . (microtime(true) - $begin) . "s\n";
}
编译加运行就两条命令:
Shell
$ tpc fib.php -O2 -o fib
$ ./fib
原生二进制直接跑,官方基准里这个 fib(40) 的耗时是 0.11 秒。拿同样的代码用 php fib.php 跑一下做对比,差距一目了然。
Windows 用户不用搭环境,下载官方工具包解压,打开 VS2022 的 x64 命令行就能编,第三篇有完整步骤。
我对 TypePHP 的判断是:它不会改变大多数 PHP 开发者的日常,但给计算密集场景打开了一个以前不存在的选项。以前这种需求你只能咬牙用 C 扩展或者换语言,现在多了一条「代码还是 PHP 写」的路。
这个系列打算写 16 篇,把官方那将近 50 篇文档从头到尾过一遍:性能实测、环境搭建、类型系统、C++/Python 互操作、WebAssembly、调试部署,全都覆盖。下一篇先拿基准数据开刀,顺便讲讲怎么用 profiling 找出你自己项目里的热点。
1. TypePHP 官网 https://swoole.com/aot/
2. GitHub 仓库 https://github.com/swoole/aot-compiler
3. 性能文档 https://www.swoole.com/aot/docs/performance
4. 执行过程文档 https://www.swoole.com/aot/docs/execution
—— 如果这篇对你有帮助 ——
点赞 · 在看 · 转发,是对我最大的支持
关注「开源技术小栈」,第一时间见证 TypePHP 的每一次关键进化