是时候分析由PHP解释器生成的PHP操作码了。为此,您需要安装
VLD extension
在命令行中使用它来生成手头的PHP脚本的操作码。
Opcode分析
-
似乎
$i++
与…不同
++$i
在操作码和内存使用方面。语句$I++;生成操作码:
POST_INC ~4 !1
FREE ~4
将计数器增加1,并将以前的值保存到内存插槽4中。然后,因为从未使用过这个值-将其从内存中释放。问题-如果不使用价值,为什么我们需要存储价值?
-
似乎确实存在循环惩罚,因此我们可以通过执行
循环展开
.
优化测试代码
将post_inc更改为assign_add(它不会在内存中保存附加信息)并执行循环展开,将使用以下测试代码:
while (true) {
// Slow version
$t1 = microtime(true);
for ($n = 0, $i = 0; $i < 2000; $i+=10) {
// loop unrolling
$n += 2 * (($i+0) * ($i+0));
$n += 2 * (($i+1) * ($i+1));
$n += 2 * (($i+2) * ($i+2));
$n += 2 * (($i+3) * ($i+3));
$n += 2 * (($i+4) * ($i+4));
$n += 2 * (($i+5) * ($i+5));
$n += 2 * (($i+6) * ($i+6));
$n += 2 * (($i+7) * ($i+7));
$n += 2 * (($i+8) * ($i+8));
$n += 2 * (($i+9) * ($i+9));
}
$t2 = microtime(true);
echo "{$n}\n";
// Optimized version
$t3 = microtime(true);
for ($n = 0, $i = 0; $i < 2000; $i+=10) {
// loop unrolling
$n += ($i+0) * ($i+0);
$n += ($i+1) * ($i+1);
$n += ($i+2) * ($i+2);
$n += ($i+3) * ($i+3);
$n += ($i+4) * ($i+4);
$n += ($i+5) * ($i+5);
$n += ($i+6) * ($i+6);
$n += ($i+7) * ($i+7);
$n += ($i+8) * ($i+8);
$n += ($i+9) * ($i+9);
}
$n *= 2;
$t4 = microtime(true);
echo "{$n}\n";
$speedup = round(100 * (($t2 - $t1) - ($t4 - $t3)) / ($t2 - $t1), 0);
$table[$speedup]++;
echo "****************\n";
foreach ($table as $s => $c) {
if ($s >= 0 && $s <= 20)
echo "$s,$c\n";
}
}
结果
脚本将CPU命中一个或其他加速值的次数聚合起来。
当CPU命中vs加速被绘制成一个图表时,我们得到这样的图片:
所以很可能脚本会加速10%。这意味着我们的优化结果
+ 2%
加速(与原始脚本8%相比)。
期望
我非常确定我所做的所有这些事情——都可以通过一个php jit'er自动完成。我认为在生成二进制可执行文件时,很难将一对post_inc/free操作码自动更改为一个pre_inc操作码。此外,php jit'er可以应用循环展开也不是一个奇迹。这只是一个优化的开始!
希望会有一个吉特在
PHP 8.0