在可能的情况下,我想知道用比特移位替换单个乘法后跟整数除法是否更快。说我有一个int k,我想把它乘以2.25。
什么更快?
int k = 5;
k *= 2.25;
std::cout << k << std::endl;
或
int k = 5;
k = (k<<1) + (k/4);
std::cout << k << std::endl;
输出
11
11
两者都给出相同的结果,您可以查看this full example。
答案 0 :(得分:10)
我定义了函数regularmultiply()
和bitwisemultiply()
,如下所示:
int regularmultiply(int j)
{
return j * 2.25;
}
int bitwisemultiply(int k)
{
return (k << 1) + (k >> 2);
}
使用Instruments进行分析(在2009 Macbook OS X 10.9.2的XCode中),bitwisemultiply
的执行速度似乎比regularmultiply
快2倍。
汇编代码输出似乎证实了这一点,bitwisemultiply
大部分时间都花在寄存器重排和函数返回上,而regularmultiply
大部分时间花在乘法上。
<强> regularmultiply 强>:
<强> bitwisemultiply 强>:
但是我的试验时间太短了。
接下来,我尝试用1000万次乘法执行这两个函数,这次将循环放在函数中,这样所有函数的进入和离开都不会模糊数字。而这一次,结果是每种方法花费大约52毫秒的时间。因此,至少对于相对较大但不是很大的计算次数,这两个函数大约需要相同的时间。这让我感到惊讶,所以我决定用更长的数字来计算数字。
这一次,我只增加了1亿到5亿到2.25,但bitwisemultiply
实际上比regularmultiply
略慢。
最后,我切换了两个函数的顺序,只是为了看看Instruments中不断增长的CPU图表是否可能减慢第二个函数的速度。但是,regularmultiply
表现稍好一些:
以下是最终的计划:
#include <stdio.h>
int main(void)
{
void regularmultiplyloop(int j);
void bitwisemultiplyloop(int k);
int i, j, k;
j = k = 4;
bitwisemultiplyloop(k);
regularmultiplyloop(j);
return 0;
}
void regularmultiplyloop(int j)
{
for(int m = 0; m < 10; m++)
{
for(int i = 100000000; i < 500000000; i++)
{
j = i;
j *= 2.25;
}
printf("j: %d\n", j);
}
}
void bitwisemultiplyloop(int k)
{
for(int m = 0; m < 10; m++)
{
for(int i = 100000000; i < 500000000; i++)
{
k = i;
k = (k << 1) + (k >> 2);
}
printf("k: %d\n", k);
}
}
那么我们能说些什么呢?我们可以肯定的一点是,优化编译器比大多数人更好。此外,当存在 lot 计算时,这些优化会显示更多,这是您唯一真正想要优化的时间。因此,除非您在汇编中对优化进行编码,否则将乘法更改为位移可能不会有太大帮助。
考虑应用程序的效率总是好的,但微效率的提高通常不足以保证使代码的可读性降低。
答案 1 :(得分:4)
实际上,这取决于多种因素。所以我刚刚通过运行和测量时间来检查它。所以我们感兴趣的字符串只需要很少的CPU指令,这非常快,所以我将它包装到循环中 - 将一个代码的执行时间乘以一个大数字,我得到k *= 2.25;
是关于比k = (k<<1) + (k/4);
慢1.5倍。
这是我的两个代码:comapre:
PROG1:
#include <iostream>
using namespace std;
int main() {
int k = 5;
for (unsigned long i = 0; i <= 0x2fffffff;i++)
k = (k<<1) + (k/4);
cout << k << endl;
return 0;
}
prog 2:
#include <iostream>
using namespace std;
int main() {
int k = 5;
for (unsigned long i = 0; i <= 0x2fffffff;i++)
k *= 2.25;
cout << k << endl;
return 0;
}
Prog1需要8秒,Prog2需要14秒。因此,通过使用体系结构和编译器运行此测试,您可以获得对您的特定环境正确的结果。
答案 2 :(得分:3)
这在很大程度上取决于CPU架构:浮点运算(包括乘法)在许多CPU上变得相当便宜。但必要的float-&gt; int转换可能会让你感到困惑:例如,在POWER-CPU上,由于在将值从浮点单元移动到整数单元时生成的管道刷新,常规乘法将会爬行。
在某些CPU(包括我的,这是一台AMD CPU)上,这个版本实际上是最快的:
k *= 9;
k >>= 2;
因为这些CPU可以在一个周期内进行64位整数乘法运算。我的版本的其他CPU肯定比你的bitshift版本慢,因为它们的整数乘法不是那么大的优化。大多数CPU在乘法运算上并不像过去那样糟糕,但乘法仍然需要超过四个周期。
因此,如果您知道程序将运行哪个CPU,请测量哪个CPU最快。如果您不知道,您的bithift版本在任何架构上都不会表现不佳(与常规版本和我的版本不同),这使得这是一个非常安全的选择。
答案 3 :(得分:1)
这在很大程度上取决于您使用的硬件。在现代硬件上,浮点乘法运行速度可能比整数运算速度快,因此您可能希望更改整个算法并开始使用双精度数而不是整数。如果您正在为现代硬件编写并且您有很多操作,例如乘以2.25,我建议使用双精度而不是整数,如果没有别的办法阻止您这样做。
由数据驱动 - 衡量性能,因为它受编译器,硬件和实现算法的影响。