为什么递增 long long 比递增 int 慢得多?

Why is incrementing a long long much slower than incrementing an int?

我最近很想知道 C++ 在一秒钟内大致可以处理多少整数增量。为了测试这个,我写了一个简短的驱动程序,如下所示:

#include <iostream>

using namespace std;

int main()
{
    int num = 0;
    while(++num) {
        if(num%100000000 == 0) { // prints num every 100 million iterations
            cout << num << endl;
        }
    }

    return 0;
}

当我在 g++ 7.5.0 下使用优化 -O3 编译此代码时,该程序设法每秒递增大约 800,000,000 次。

但是当我将 int 的类型切换为 long long 时,我发现性能严重下降,每秒大约 100,000,000 次。

有人可以解释为什么会出现这种差异吗?

有符号除法是用IDIV指令完成的。根据 Agner Fog's instruction tables,在 Haswell 架构上,32 位寄存器的 IDIV 的倒数吞吐量是 8-11,而对于 64 位寄存器是 24-81。也就是说,使用 64 位寄存器进行 64 位整数除法比使用 32 位寄存器所需的时间大约长 2 到 10 倍。这些数字因架构而异,甚至对于 Haswell 来说也有很​​大的范围,但 8 倍的性能损失似乎是合理的。这不是增量(INC 具有固定且快得离谱的速度;显然每个时钟周期可以分派四次),这是限制使用 % 100000000 执行的输出量的测试更大的操作数大小。

也许尝试用基于 2 的大幂而不是 10 的幂打印的掩码替换它(AND 便宜得离谱并且与寄存器大小无关),例如:

if((num & ((1 << 27) - 1)) == 0)

如果您真的喜欢使用 10 的幂,可以随时 spring 升级到 IceLake;看起来区别仅在于 6 与 10 的相互吞吐量,因此性能损失将小于 2 倍。