众所周知,向左移位比乘法快,因为桶形移位器直接在硬件中实现。因此,这个简单的基准测试应该是错误的:
$start = 1;
$timestart = microtime(1);
for ($i = 0; $i < 10000000; $i++) {
$result2 = $start << 2;
}
echo microtime(1) - $timestart;
$timestart = microtime(1);
for ($i = 0; $i < 10000000; $i++) {
$result1 = $start * 4;
}
echo microtime(1) - $timestart;
echo "\n";
因为我多次执行它,所以乘法总是比向左移位快。例如:
0.73733711242676
0.71091389656067
因此,或者基准测试错误或PHP解释器在此处执行了某些操作。该测试由在Ubuntu中运行的PHP 7.0.32执行:
PHP 7.0.32-0ubuntu0.16.04.1(cli)(NTS)
CPU:3.20GHz时的Intel®Core™i5-4460 CPU
编辑:
在具有几乎相同的CPU(Intel®Core™i5-4460S CPU @ 2.90GHz)的Windows机器中执行它,结果与预期的一样:
0.24960112571716
0.28080010414124
这种情况的PHP版本不同:
PHP 7.1.19(cli)(内置:2018年6月20日23:24:42)(ZTS MSVC14(Visual C ++ 2015)x64)
答案 0 :(得分:1)
您关于硬件的推理基本上是无关紧要的。您使用的是一种解释语言,其中大部分成本是解释器开销。
任一循环的asm版本每个时钟可以运行1次(假设有固定计数的移位),因此(在3GHz CPU上)仅进行100k次迭代就需要0.033毫秒(0.000033秒),比PHP快250倍。时间。
此外,解释循环必须使用可变计数移位(因为它不能JIT将移位计数编译为机器代码中的立即数),实际上,这对于Intel的吞吐量(3微秒)而言更为昂贵由于x86遗留的行李(标志语义)而导致的CPU。 AMD CPU甚至具有可变移位计数,也具有单uup移位。 (shl reg, cl
与shr reg, imm8
)。有关shl reg,cl
为什么在Sandybridge家族上为3 oups,以及如何通过标志创建虚假依赖的更多信息,请参见INC instruction vs ADD 1: Does it matter?。
在Intel Sandybridge系列和AMD Ryzen上,整数乘以1 uop,每时钟1个吞吐量,3个周期延迟。我每2个时钟在AMD Bulldozer系列上运行一次,但未完全流水线化。因此,是的,乘法具有更高的延迟,但是它们都已完全流水线化以提高吞吐量。您的循环会丢弃结果,因此没有循环承载的依赖链,因此延迟无关紧要(并且由于乱序执行而被隐藏)。
但是,这个微小的差异(额外的2个ouop)不足以解决所测得的差异。实际的移位或乘法运算仅占循环总周期的1/250。您说切换循环的顺序并不会改变结果,因此,这不仅是CPU加速到最大时钟速度之前的预热效果。
您没有提到正在运行的CPU微体系结构,但答案可能并不取决于移位与乘法指令的解码方式。