C / C ++:乘法,或者比特移位然后除?

时间:2014-06-01 21:14:48

标签: c++ c multiplication bit-shift

在可能的情况下,我想知道用比特移位替换单个乘法后跟整数除法是否更快。说我有一个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

4 个答案:

答案 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倍。

enter image description here

汇编代码输出似乎证实了这一点,bitwisemultiply大部分时间都花在寄存器重排和函数返回上,而regularmultiply大部分时间花在乘法上。

<强> regularmultiply

enter image description here

<强> bitwisemultiply

enter image description here

但是我的试验时间太短了。

第二次尝试

接下来,我尝试用1000万次乘法执行这两个函数,这次将循环放在函数中,这样所有函数的进入和离开都不会模糊数字。而这一次,结果是每种方法花费大约52毫秒的时间。因此,至少对于相对较大但不是很大的计算次数,这两个函数大约需要相同的时间。这让我感到惊讶,所以我决定用更长的数字来计算数字。

第三次尝试

这一次,我只增加了1亿到5亿到2.25,但bitwisemultiply实际上比regularmultiply略慢。

最后的尝试

最后,我切换了两个函数的顺序,只是为了看看Instruments中不断增长的CPU图表是否可能减慢第二个函数的速度。但是,regularmultiply表现稍好一些:

enter image description here

以下是最终的计划:

#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,我建议使用双精度而不是整数,如果没有别的办法阻止您这样做。

由数据驱动 - 衡量性能,因为它受编译器,硬件和实现算法的影响。