Pet*_*Lee 4 c x86 sse simd sse2
假设我有一个非常简单的代码,例如:
double array[SIZE_OF_ARRAY];
double sum = 0.0;
for (int i = 0; i < SIZE_OF_ARRAY; ++i)
{
sum += array[i];
}
Run Code Online (Sandbox Code Playgroud)
我基本上想要使用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);
Run Code Online (Sandbox Code Playgroud)
您可以通过使用多个累加器隐藏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);
Run Code Online (Sandbox Code Playgroud)
注意,a假设该数组是16字节对齐的,并且n假设元素的数量是2的倍数(或者在展开的循环的情况下为4).
另请参见在x86上执行水平浮点矢量和的最快方法,以获取在循环外执行水平和的其他方法.SSE3支持并不是完全通用的(特别是AMD CPU后来支持它比英特尔).
此外,_mm_hadd_pd即使在支持它的CPU上通常也不是最快的方式,因此在现代CPU上仅SSE2版本不会更糟.不过,它在循环之外并没有太大的区别.