为什么在此代码中使用按位运算符的加法比算术加法慢得多

Arj*_*ran 1 c bit-manipulation bitwise-operators compiler-optimization

我尝试使用我写的按位运算来比较算术加法和函数 - 发现后者几乎10x慢了.这种速度差异的原因是什么?由于我在循环中添加相同的数字,编译器是否会在第一种情况下将其重写为更优的东西?

使用算术运算:

int main()
{
    clock_t begin = clock();
    int x;
    int i = 1000000000;
    while(i--) {
    x = 1147483000 + i;
    }
    printf("%d\n", x);

    clock_t end = clock();
    double time_spent = (double)(end - begin) / CLOCKS_PER_SEC;

    printf("time spent = %f\n", time_spent);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

输出:

1147483000
time spent = 3.520000
Run Code Online (Sandbox Code Playgroud)

使用按位运算符:

内线while loop被替换为:

x = add(1147483000, i);
Run Code Online (Sandbox Code Playgroud)

这是add功能:

int add(int x, int y) {
    while(y != 0) {
        int carry = (x & y);
        x = x ^ y;
        y = carry << 1;
    }
    return x;
}
Run Code Online (Sandbox Code Playgroud)

输出:

1147483000
time spent = 32.940000
Run Code Online (Sandbox Code Playgroud)

Sha*_*baz 5

你已经替换了这个:

x = 1147483000 + i;
Run Code Online (Sandbox Code Playgroud)

有了这个:

while(y != 0) {
    int carry = (x & y);
    x = x ^ y;
    y = carry << 1;
}
Run Code Online (Sandbox Code Playgroud)

当然,你会得到一个巨大的减速!+两个整数是一条汇编指令。您的while循环执行许多指令,在软件中有效地模拟硬件在执行加法时所做的事情。


更详细地说,这就是全加器的样子。通过 32 位加法,ALU包含 32 个这个单元级联。这些硬件元素中的每一个都有非常非常小的延迟。电线的延迟可以忽略不计。因此,如果软件将两个 32 位数字相加,则只需要很少的时间。

另一方面,如果您尝试手动模拟加法,则会使 CPU 进入内存、提取和解码某些指令 32 次,这需要相当长的时间。

在此处输入图片说明


Dav*_*nan 5

通常在非常少量的时钟周期中在硬件中执行整数运算.

您将无法在软件中接近这种性能.使用按位运算的实现涉及函数调用和循环.您执行的按位操作通常会花费与算术相似的时钟周期数.

您每次迭代执行三个按位运算.坦率地说,我很惊讶这里只有10倍.

我也想知道你的编译器设置是什么,特别是任何优化.一个好的编译器可以消除算术版本中的while循环.对于性能比较,您应该比较优化代码.看起来好像你可能没有这样做.

很难知道你想要在这里实现什么,但是不要期望超过硬件算术单元的性能.