假设我有一个非常简单的代码,如:
double array[SIZE_OF_ARRAY];
double sum = 0.0;
for (int i = 0; i < SIZE_OF_ARRAY; ++i)
{
sum += array[i];
}
我基本上想要使用SSE2进行相同的操作。我怎么能这样做?
答案 0 :(得分:6)
这是一个非常简单的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所示)。使用多个“sum”向量展开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)。
另请参阅Fastest way to do horizontal float vector sum on x86了解在循环外执行水平和的其他方法。 SSE3的支持并不是完全通用的(特别是AMD的CPU后来支持它比英特尔)。
此外,即使在支持它的CPU上,_mm_hadd_pd
通常也不是最快的方式,因此在现代CPU上,仅SSE2版本不会更糟。不过,它在循环之外并没有太大的区别。