为什么递增 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 倍。
我最近很想知道 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 倍。