我遇到了一个奇怪的问题。我正在研究的算法包括许多像这样的计算
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 :(得分:24)
至少在x87上,一切都是在内部以80位精度完成的。精度实际上只决定了这些位中有多少存储在内存中。这是为什么不同的优化设置可以稍微改变结果的部分原因:它们将舍入量从80位更改为32位或64位。
实际上,使用80位浮点(C和C ++中的long double
,D中的real
通常很慢,因为没有有效的方法从内存加载和存储80位。 32位和64位通常同样快,只要内存带宽不是瓶颈,即无论如何一切都在高速缓存中。如果发生以下任何一种情况,64位可能会更慢:
就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可能会。