4

假设我有一个非常简单的代码,例如:

double array[SIZE_OF_ARRAY];
double sum = 0.0;

for (int i = 0; i < SIZE_OF_ARRAY; ++i)
{
    sum += array[i];
}

我基本上想用 SSE2 做同样的操作。我怎样才能做到这一点?

4

1 回答 1

5

这是一个非常简单的 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-add 吞吐量(每个时钟周期一个或两个)的瓶颈,而不是 FP-add 延迟(每 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,在展开循环的情况下)。

另请参阅在 x86 上进行水平浮点向量求和的最快方法,了解在循环外进行水平求和的替代方法。SSE3 支持并不完全普遍(尤其是 AMD CPU 比 Intel 支持它晚)。

此外,_mm_hadd_pd即使在支持它的 CPU 上,它通常也不是最快的方式,因此仅 SSE2 的版本在现代 CPU 上不会更糟。不过,它在循环之外并且没有太大的区别。

于 2012-10-01T22:24:25.833 回答