累计双打与整数

时间:2014-05-12 17:07:25

标签: c++ performance floating-point double cpu

a声明为double时,以下代码较慢,而不是int:

double a = 0;
int j[1000];

for(int i=0; i<1000; i++){
    a += (i * j[i]);
}

如果a被声明为int,那么编译器选择不同的汇编指令会导致双精度加法的性能下降吗?

我试图了解CPU是否进行任何转换&#34;它自己在运行时的单/双精度,在组装和成本执行时间中看不到?

2 个答案:

答案 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开始,存在差异。原因是添加at2,编译器必须首先将它们转换为公共类型。在a是整数的情况下,操作3和4根本不执行任何操作(int + int会产生int,因此两个强制转换都转换int s到int s)。在adouble的情况下,t2必须转换为doubleint + doubledouble })在添加之前。

这意味着操作5中的添加类型也不同:它可以是intdouble添加。忽略double通常是int的两倍大的明显方面,这意味着此时计算机需要做一些不同的事情。

x64

的含义

当使用优化编译器为现代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交换了两个doubleSsingle。而且,有趣的是,性能上的差异也很小(例如,Haswell核心转换为1倍,转换为浮点数,而转换为浮动,而加法本身表现相同)。

验证

为了验证我的声明,我已经运行了2000000次循环并确保a没有被优化掉。结果是:

int   : 601.1 ms
float : 2567 ms
double: 2593 ms

答案 1 :(得分:1)

我忽略了您的示例未编译且数组j未初始化(我希望您能解决此问题)。

浮点运算通常比整数运算慢。但是,您的代码具有更昂贵的操作:将整数转换为浮点数。你的代码使用混合模式算法会遭受双重打击(双关语)。