假設我有一個非常簡單的代碼,如:如何使用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。如果你可以下降到單精度(即浮點),那麼它可能是值得的。你需要多少性能提升? –
強烈建議使用卡漢總結。問題和答案中提出的解決方案容易出錯。 –