我在so thread上遇到了麻烦,因此决定用PHP编写类似的测试。 我的测试代码是这样的:
// Slow version
$t1 = microtime(true);
for ($n = 0, $i = 0; $i < 20000000; $i++) {
$n += 2 * ($i * $i);
}
$t2 = microtime(true);
echo "n={$n}\n";
// Optimized version
$t3 = microtime(true);
for ($n = 0, $i = 0; $i < 20000000; $i++) {
$n += $i * $i;
}
$n *= 2;
$t4 = microtime(true);
echo "n={$n}\n";
$speedup = round(100 * (($t2 - $t1) - ($t4 - $t3)) / ($t2 - $t1), 0);
echo "speedup: {$speedup}%\n";
2 * ($i * $i)
版本中,其运行方式与2 * $i * $i
类似,
8%
速度的情况下,
Java版本加快了16%
的速度。因此,PHP版本在Java代码中获得了大约1/2的加速因子。我不会详细介绍,但是优化和未优化代码中的乘法比例为->
1个总和:3/4
2个总和:4/6
3个总和:5/8
4个总和:6/10
...
一般来说:
其中,n是循环中的求和数。为了对我们有用的公式-我们需要计算N接近无穷大时的极限(以复制我们在一个循环中进行大量求和的情况)。因此:
因此我们得出结论,在优化的代码中,乘法必须减少50%。
答案 0 :(得分:0)
是时候分析由PHP解释器生成的PHP操作码了。为此,您需要安装VLD extension并从命令行使用它来生成php脚本的操作码。
$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命中率与加速比绘制为图形时,我们得到这样的画面:
因此,脚本很可能会获得10%的加速。这意味着我们的优化导致了 +2%的加速(原始脚本为8%)。
我非常确定我所做的所有事情-可以由PHP JIT'er自动完成。我认为在生成二进制可执行文件时,很难将一对POST_INC / FREE操作码自动更改为一个PRE_INC操作码。同样,PHP JIT'er可以应用循环展开并不是奇迹。这仅仅是优化的开始!
希望PHP 8.0会有一个准时人员