32位与64位浮点性能

时间:2010-06-29 01:40:39

标签: performance floating-point precision

我遇到了一个奇怪的问题。我正在研究的算法包括许多像这样的计算

q = x(0)*y(0)*z(0) + x(1)*y(1)*z(1) + ...

其中总和的长度在4到7之间。

原始计算都是使用64位精度完成的。对于实验,我尝试对x,y,z输入值使用32位精度(以便使用32位执行计算),并将最终结果存储为64位值(直接转换)。

我希望32位性能更好(缓存大小,SIMD大小等),但令我惊讶的是,性能没有差异,甚至可能会有所下降。

有问题的架构是Intel 64,Linux和GCC。两个代码似乎都使用SSE,两种情况下的数组都与16字节边界对齐。

为什么会这样?到目前为止,我的猜测是32位精度只能在前四个元素上使用SSE,其余的由连续渲染开销串联完成。

3 个答案:

答案 0 :(得分:24)

至少在x87上,一切都是在内部以80位精度完成的。精度实际上只决定了这些位中有多少存储在内存中。这是为什么不同的优化设置可以稍微改变结果的部分原因:它们将舍入量从80位更改为32位或64位。

实际上,使用80位浮点(C和C ++中的long double,D中的real通常很慢,因为没有有效的方法从内存加载和存储80位。 32位和64位通常同样快,只要内存带宽不是瓶颈,即无论如何一切都在高速缓存中。如果发生以下任何一种情况,64位可能会更慢:

  1. 内存带宽是瓶颈。
  2. 64位数字未在8字节边界上正确对齐。 32位数字只需要4字节对齐以获得最佳效率,因此它们不那么挑剔。一些编译器(数字Mars D编译器浮现在脑海中)并不总是能够正确地存储在堆栈中的64位双精度数。这导致加载一个内存操作量的两倍,实际上与正确对齐的64位浮点数或32位浮点数相比,性能下降了大约2倍。
  3. 就SIMD优化而言,应该注意的是大多数编译器在自动矢量化代码方面都很糟糕。如果您不想直接使用汇编语言编写,那么利用这些指令的最佳方法是使用类似于数组的操作,例如,在D中可用,并根据SSE指令实现。类似地,在C或C ++中,你可能想要使用SSE优化的高级函数库,虽然我不知道一个很好的函数,因为我主要用D编程。

答案 1 :(得分:0)

这可能是因为您的处理器仍然进行64位计数,然后修剪数字。有一些CPU标志你可以改变,但我不记得了......

答案 2 :(得分:0)

首先检查生成的ASM。它可能不是你所期望的。

还尝试将其写为循环:

typedef float fp;
fp q = 0
for(int i = 0; i < N; i++)
  q += x[i]*y[i]*z[i]

某些编译器可能会注意到循环而不是展开的表单。

最后,您的代码使用()而不是[]。如果你的代码正在进行大量的函数调用(12到21),那将会淹没FP的成本,甚至一起删除fp计算也不会产生太大的影响。内联OTOH可能会。