php - 乘法如何比向左移位更快?
问题描述
众所周知,向左移位比乘法要快,因为桶形移位器直接在硬件中实现。因此,这个简单的基准测试应该是错误的:
$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:Intel(R) Core(TM) i5-4460 CPU @ 3.20GHz
编辑:
在 Windows 机器中执行它,使用几乎相同的 CPU(Intel(R) Core(TM) i5-4460S CPU @2.90GHz),结果如预期:
0.24960112571716
0.28080010414124
这种情况下的 PHP 版本是不同的:
PHP 7.1.19 (cli) (内置: Jun 20 2018 23:24:42) (ZTS MSVC14 (Visual C++ 2015) x64)
解决方案
您对硬件的推理基本上是无关紧要的。您正在使用一种解释语言,其中大部分成本是解释器开销。
任一循环的 asm 版本可以以每时钟 1 次运行(假设固定计数移位),因此(在 3GHz CPU 上)仅 100k 次迭代将花费 0.033 毫秒或 0.000033 秒,比 PHP 时间快约 250 倍。
此外,解释循环必须使用可变计数移位(因为它不能将移位计数 JIT 编译为机器代码中的立即数),这实际上对于 Intel CPU 上的吞吐量(3 uops)来说更昂贵,因为x86 遗留包(标志语义)。即使对于可变移位计数,AMD CPU 也具有单微指令移位。(shl reg, cl
与shr reg, imm8
)。请参阅INC 指令与 ADD 1:这有关系吗?更多关于为什么shl reg,cl
Sandybridge-family 是 3 uops,以及它如何通过标志创建错误依赖)
在 Intel Sandybridge 系列和 AMD Ryzen 上,整数乘法是 1 uop,每时钟 1 个吞吐量,3 个周期延迟。我在 AMD Bulldozer 系列上每 2 个时钟,未完全流水线化。所以是的,乘法具有更高的延迟,但它们都是完全流水线的吞吐量。您的循环会丢弃结果,因此没有循环携带的依赖链,因此延迟是无关紧要的(并且被乱序执行隐藏)。
但是那个微小的差异(2个额外的微指令)不足以解释测量的差异。 实际的移位或乘法仅是循环所用总周期的 1/250。您说切换循环的顺序不会改变结果,因此在您的 CPU 加速到最大时钟速度之前,这不仅仅是一种预热效果。
您还没有提到您正在运行的 CPU 微架构,但答案可能并不取决于移位指令与乘法指令的解码方式。
推荐阅读
- python - python pandas替换所有浮点值
- autodesk-forge - 使用 LeafletLoader 而不是 PDFLoader 将 PDF 加载到伪造查看器中不起作用
- django - 如何减少 Django 中模型方法生成的 SQL 查询计数?
- c# - 如何使用 await 简单安全地调用可为空的委托
- delphi - Delphi - 查找内存泄漏
- css - 我正在尝试将 css 文件链接到 https
- javascript - 为什么 console.time 显示的时间比实际时间短得多?
- ubuntu - Artifactory 启动失败
- api - Mulesoft 与 api 和数据库的集成
- laravel - 两个 select2_ajax 过滤器之间的关系