假设我有一个非常简单的代码,如:如何使用SSE2添加数组中的所有元素?
double array[SIZE_OF_ARRAY];
double sum = 0.0;
for (int i = 0; i < SIZE_OF_ARRAY; ++i)
{
sum += array[i];
}
我基本上要做到使用SSE2相同的操作。我怎样才能做到这一点?
假设我有一个非常简单的代码,如:如何使用SSE2添加数组中的所有元素?
double array[SIZE_OF_ARRAY];
double sum = 0.0;
for (int i = 0; i < SIZE_OF_ARRAY; ++i)
{
sum += array[i];
}
我基本上要做到使用SSE2相同的操作。我怎样才能做到这一点?
这里是一个非常简单的SSE3实现:
#include <emmintrin.h>
__m128d vsum = _mm_set1_pd(0.0);
for (int i = 0; i < n; i += 2)
{
__m128d v = _mm_load_pd(&a[i]);
vsum = _mm_add_pd(vsum, v);
}
vsum = _mm_hadd_pd(vsum, vsum);
double sum = _mm_cvtsd_f64(vsum0);
您可以展开循环,通过使用多个蓄电池隐藏FP另外的延迟(由@Mysticial的建议),以获得更好的性能。与多个展开3次或4次“总和”向量瓶颈负载和FP-添加可以通过(一个或两个每一个时钟周期),而不是FP-添加延迟(每3下或4个周期):
__m128d vsum0 = _mm_setzero_pd();
__m128d vsum1 = _mm_setzero_pd();
for (int i = 0; i < n; i += 4)
{
__m128d v0 = _mm_load_pd(&a[i]);
__m128d v1 = _mm_load_pd(&a[i + 2]);
vsum0 = _mm_add_pd(vsum0, v0);
vsum1 = _mm_add_pd(vsum1, v1);
}
vsum0 = _mm_add_pd(vsum0, vsum1); // vertical ops down to one accumulator
vsum0 = _mm_hadd_pd(vsum0, vsum0); // horizontal add of the single register
double sum = _mm_cvtsd_f64(vsum0);
请注意,数组a
被假定为16字节对齐,并且元素数量n
被假定为2的倍数(或4,在展开循环的情况下)。
另请参阅Fastest way to do horizontal float vector sum on x86了解在循环外进行水平求和的替代方法。 SSE3支持并不是完全通用的(尤其是AMD CPU后来支持它比Intel)。
而且,_mm_hadd_pd
通常不是最快的方式,即使在支持它的CPU上也是如此,所以现代CPU上的SSE2版本不会更糟。尽管如此,它在循环之外,并且两种方式都没有太大的区别。
如果你真的需要使用双精度,那么它可能不值得打扰,因为现代大多数现代x86 CPU都有两个FPU。如果你可以下降到单精度(即浮点),那么它可能是值得的。你需要多少性能提升? –
强烈建议使用卡汉总结。问题和答案中提出的解决方案容易出错。 –