当a
声明为double时,以下代码较慢,而不是int:
double a = 0;
int j[1000];
for(int i=0; i<1000; i++){
a += (i * j[i]);
}
如果a
被声明为int,那么编译器选择不同的汇编指令会导致双精度加法的性能下降吗?
我试图了解CPU是否进行任何转换&#34;它自己在运行时的单/双精度,在组装和成本执行时间中看不到?
答案 0 :(得分:6)
让我们将循环中的表达式分解为各自的部分。为此,我们将重写代码,以便每行只有一个赋值和一个操作。从int
版本开始时,它看起来如下所示:
// not depending on a
/* 1 */ auto t1 = j[i];
/* 2 */ auto t2 = i * j
// depending on decltype(a)
/* 3 */ decltype(a+t2) t3 = static_cast<decltype(a+t2)>(t2);
/* 4 */ decltype(a+t3) t4 = static_cast<decltype(a+t3)>(a);
/* 5 */ a = t3 + t4;
前两个操作根本不依赖于a
的类型,并且在任何一种情况下都会完全相同。
然而,从操作3开始,存在差异。原因是添加a
和t2
,编译器必须首先将它们转换为公共类型。在a
是整数的情况下,操作3和4根本不执行任何操作(int
+ int
会产生int
,因此两个强制转换都转换int
s到int
s)。在a
为double
的情况下,t2
必须转换为double
(int
+ double
,double
})在添加之前。
这意味着操作5中的添加类型也不同:它可以是int
或double
添加。忽略double
通常是int
的两倍大的明显方面,这意味着此时计算机需要做一些不同的事情。
当使用优化编译器为现代x64机器编译该程序时,应该注意的是,当按原样说明时,整个程序可以被优化掉。假设这没有发生,并且您的编译器不应用任何非法优化,并且您可以使用未初始化的变量(j
的元素)引入的UB,可能会发生以下情况:
// not depending on a
MOV EAX, i // copy i to EAX register
IMUL j[i] // EAX = EAX * j[i] (high 32 bits are stored in EDX and ignored)
// if a is int
ADD a, EAX // integer addition: a += EAX
// else if a is double
CVTSI2SD XMM0, EAX // convert the multiplication result to double
ADDPD a, XMM0 // double addition: a += XMM0
// endif
一个好的编译器会将循环展开一点并交错其中的一些,因为循环限制是已知的。正如您所看到的,操作至少有两倍的增加,以及依赖链的指令。此外,第二个版本中的说明是slower,而不是第一个版本中的单个。
虽然我确信第二个版本可以在更高效的版本中声明,但应该注意的是,第一个版本的整数ADD
是几乎所有CPU上最快的操作之一,并且通常比它的浮点数更快。
所以,回答你的问题:CPU确实在浮点和整数之间执行转换 - 这些转换在程序集中可见并且具有(可能很大的)运行时成本。
由于您也询问了单精度问题,让我们一起检查使用float
时会发生什么:
// not depending on a
MOV EAX, i // copy i to EAX register
IMUL j[i] // EAX = EAX * j[i] (high 32 bits are stored in EDX and ignored)
// if a is float
CVTSI2SS XMM0, EAX // convert the multiplication result to float
ADDPS a, XMM0 // float addition: a += XMM0
程序集没有显示出显着差异(我们只为D
交换了两个double
,S
为single
。而且,有趣的是,性能上的差异也很小(例如,Haswell核心转换为1倍,转换为浮点数,而转换为浮动,而加法本身表现相同)。
为了验证我的声明,我已经运行了2000000次循环并确保a
没有被优化掉。结果是:
int : 601.1 ms
float : 2567 ms
double: 2593 ms
答案 1 :(得分:1)
我忽略了您的示例未编译且数组j
未初始化(我希望您能解决此问题)。
浮点运算通常比整数运算慢。但是,您的代码具有更昂贵的操作:将整数转换为浮点数。你的代码使用混合模式算法会遭受双重打击(双关语)。