为什么增加longlong比增加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 次。
但是当我将 a 的类型切换int到 a 时long long,我发现性能严重下降,每秒大约 100,000,000 次。
有人可以解释为什么会出现这种差异吗?
回答
带符号的除法是通过IDIV指令完成的。根据Agner Fog 的指令表,在 Haswell 架构上,IDIV32 位寄存器的倒数吞吐量为 8-11,而 64 位寄存器的倒数吞吐量为 24-81。也就是说,使用 64 位寄存器进行 64 位整数除法所需的时间大约比使用 32 位寄存器所需的时间长 2 到 10 倍。这些数字因架构而异,甚至对于 Haswell 来说也有很大的范围,但 8 倍的性能损失似乎是合理的。这不是增量(INC具有固定且非常快的速度;显然每个时钟周期可以分派四次),这是您的测试,以限制您正在执行的% 100000000与更大操作数大小一起使用的输出量。
也许尝试将其替换为基于 2 的大幂而不是 10 的幂的掩码打印(AND非常便宜且与寄存器大小无关),例如:
if((num & ((1 << 27) - 1)) == 0)
如果您真的喜欢使用 10 的幂,您可以随时升级到 IceLake;看起来差异只是 6 和 10 的倒数吞吐量,因此性能损失将小于 2 倍。