[英]How to absolute 2 double or 4 floats using SSE instruction set? (Up to SSE4)
這是我嘗試使用SSE加速的示例C代碼,兩個數組是3072元素長的雙精度數,如果我不需要雙精度,可以將其下放到浮點數。
double sum = 0.0;
for(k = 0; k < 3072; k++) {
sum += fabs(sima[k] - simb[k]);
}
double fp = (1.0 - (sum / (255.0 * 1024.0 * 3.0)));
無論如何,我目前的問題是如何在SSE寄存器中執行fabs步驟為double或float,以便我可以將整個計算保留在SSE寄存器中,以便它保持快速,並且我可以通過部分展開此循環來並行化所有步驟。
我建議使用按位和掩碼。 正值和負值具有相同的表示,只有最高有效位不同,正值為0,負值為1,請參見雙精度數格式 。 您可以使用以下方法之一:
inline __m128 abs_ps(__m128 x) {
static const __m128 sign_mask = _mm_set1_ps(-0.f); // -0.f = 1 << 31
return _mm_andnot_ps(sign_mask, x);
}
inline __m128d abs_pd(__m128d x) {
static const __m128d sign_mask = _mm_set1_pd(-0.); // -0. = 1 << 63
return _mm_andnot_pd(sign_mask, x); // !sign_mask & x
}
此外,展開循環以打破循環攜帶的依賴關系鏈可能是個好主意。 由於這是非負值的總和,因此求和的順序並不重要:
double norm(const double* sima, const double* simb) {
__m128d* sima_pd = (__m128d*) sima;
__m128d* simb_pd = (__m128d*) simb;
__m128d sum1 = _mm_setzero_pd();
__m128d sum2 = _mm_setzero_pd();
for(int k = 0; k < 3072/2; k+=2) {
sum1 += abs_pd(_mm_sub_pd(sima_pd[k], simb_pd[k]));
sum2 += abs_pd(_mm_sub_pd(sima_pd[k+1], simb_pd[k+1]));
}
__m128d sum = _mm_add_pd(sum1, sum2);
__m128d hsum = _mm_hadd_pd(sum, sum);
return *(double*)&hsum;
}
通過展開和斷開依賴關系(sum1和sum2現在是獨立的),您讓處理器執行我們的順序添加。 由於指令是在現代CPU上流水線化的,因此CPU可以在前一個完成之前開始處理新的添加。 此外,按位操作在單獨的執行單元上執行,CPU實際上可以在與加/減相同的周期中執行它。 我建議使用Agner Fog的優化手冊 。
最后,我不建議使用openMP。 循環太小,在多個線程之間分配作業的開銷可能比任何潛在的好處都要大。
-x和x的最大值應為abs(x)。 這是代碼:
x = _mm_max_ps(_mm_sub_ps(_mm_setzero_ps(), x), x)
可能最簡單的方法如下:
__m128d vsum = _mm_set1_pd(0.0); // init partial sums
for (k = 0; k < 3072; k += 2)
{
__m128d va = _mm_load_pd(&sima[k]); // load 2 doubles from sima, simb
__m128d vb = _mm_load_pd(&simb[k]);
__m128d vdiff = _mm_sub_pd(va, vb); // calc diff = sima - simb
__m128d vnegdiff = mm_sub_pd(_mm_set1_pd(0.0), vdiff); // calc neg diff = 0.0 - diff
__m128d vabsdiff = _mm_max_pd(vdiff, vnegdiff); // calc abs diff = max(diff, - diff)
vsum = _mm_add_pd(vsum, vabsdiff); // accumulate two partial sums
}
請注意,這可能不會比現代x86 CPU上的標量代碼更快,后者通常有兩個FPU。 但是,如果您可以降低到單精度,那么您的吞吐量可能會提高2倍。
另請注意,在循環之后,您需要將vsum
的兩個部分和vsum
成標量值,但這非常簡單,並且不是性能關鍵。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.