让你的PHP7更快 GCC PGO
2017-09-11
依据来源:让你的PHP7更快(GCC PGO) | 风雪之隅
执行步骤
下载最新的 php-7.2.0RC4.tar.gz https://downloads.php.net/~remi/php-7.2.0RC4.tar.gz
./configure --enable-fpm --with-config-file-path=/etc --with-config-file-scan-dir=/etc/php.d
第一遍编译PHP7, 让它生成会产生profile数据的可执行文件
make prof-gen -j 8
prof-gen参数是PHP7的Makefile特有的。
然后, 开始训练GCC:
需要在文件app.php中指定file和controller,不然会报404或者其他错误,这里的更改只是为了训练用,训练结束还需要将代码改回去。
$file = 'nearby_controller.php';
$controller = 'nearby_controller'
在nearby_controller.php中指定id和count,不然之后的代码无法训练到(尽量训练多的代码)。
进行训练,可以是100或者更多次,要注意看报错
sapi/cgi/php-cgi -T 100 /api_v2.php
训练结束,开始安装
make prof-clean
make prof-use -j 8
sudo make install
压测结果
训练之前:
Requests per second: 326.02 [#/sec] (mean)
训练之后:
Requests per second: 361.31 [#/sec] (mean)
这个结果压测多次,发现有时训练之后的qps比训练之前的或高或低,但差距都不是很明显。
最终结论
GCC PGO在api的代码中优化效果不明显,而且操作起来较为复杂,不具备上线条件。
PGO相关解释
有编译器用到概率的,但我不知道最先进的编译器用到的概型有哪些先进的做法。编译器使用到概率的地方,最常见是跟profile-guided optimization(PGO)相关的。通过收集profile信息来估算某些代码执行的频繁程度,并对其做相应的优化。
例如说,
- cold code outlining
如果有:
if (cond) {
// then...
} else {
// else...
}
// next
并且收集到的profile说cond有超过90%都走到then分支上,那么就把then分支放在fallthrough位置上,而把else分支放到主干代码的“更后面”。于是生成的代码会是这样:
if !cond goto Label_else
// then...
// next...
...
Label_else:
// else...
这样,高概率执行的代码就都放在一起了,低概率执行的代码则会放到“外面”去。
- guarded devirtualization
例如说有这样一个虚函数调用:
o->foo()
如果profile收集到信息说,有80%的可能调用到A :: foo(),有11%的可能调用到B :: foo(),有9%未知,那么一种可能的代码生成策略是:
if (o.type == A) {
A::foo(o) // devirtualized
} else if (o.type == B) {
B::foo(o) // devirtualized
} else {
o->vtable[slotid(#foo)](o) // virtual call fallback
}
像上面这两种使用profile所收集到的概率来引导编译器优化的就是PGO。
其中,概率显然是要涉及到“嵌套/叠加”的:
- 假如有嵌套分支,概率要如何算?
- 假如有嵌套的虚函数调用+内联,而profile是扁平的(不带caller context)的话,被内联的callee里概率要怎么算?
- 假如有循环,概率要如何调整计算?
- …等等。