2010-06-29 163 views
8

我遇到了一个奇怪的问题。我工作的一种算法是由大量的计算这样32位与64位浮点性能

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,其余的都是通过施放开销连续进行的。

+0

您已添加赏金 - 您对dsimcha的回答有什么不喜欢的?也许值得尝试一下最新的GCC或英特尔的编译器http://software.intel.com/en-us/articles/non-commercial-software-download/,看看他们是否做得更好,编译/矢量化。 – Rup 2010-07-06 16:56:16

+0

@Rup我喜欢他的回答,但也希望其他意见,所以我把一个赏金 – Anycorn 2010-07-06 19:12:21

回答

24

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

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

  1. 内存带宽是瓶颈。
  2. 64位数字在8字节边界上没有正确对齐。 32位数字只需要4字节对齐以获得最佳效率,所以它们不那么挑剔。一些编译器(数字火星D编译器浮现在脑海中)并不总是适合存储在堆栈上的64位双精度。这会导致加载一次所需的内存操作量的两倍,实际上导致与正确对齐的64位浮点数或32位浮点数相比,性能下降了约2倍。

就SIMD优化而言,应该注意的是,大多数编译器在自动矢量化代码上都很糟糕。如果您不想直接用汇编语言编写代码,那么利用这些指令的最好方法就是使用类似阵列的操作,例如在D中可用,并按照SSE指令来实现。同样,在C或C++中,你可能想要使用一个高级函数库,这些函数是经过SSE优化的,尽管我不知道这是一个好的例子,因为我主要用D编程。

+4

“x87” - 略好于那些旧的x86处理器。 :-) – Thanatos 2010-07-10 01:57:06

+4

http://en.wikipedia.org/wiki/X87 – Adam 2010-07-10 02:20:47

0

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

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可能。

+0

谢谢,实际上'q()'是宏直接转换为原始指针访问 – Anycorn 2010-07-10 02:21:26

+0

@aaa:那么如果有任何数学,它可能仍然是一个很大的比例。另外,我不知道编译器如何处理混合FP和其他内容。这可能足以阻止它使用矢量操作。 – BCS 2010-07-10 16:04:56