我正在尋找一些關於如何與SSE做一個並行前綴總和的建議。我有興趣通過一系列整數,浮點數或雙精度來做這件事。與SSE並行前綴(累計)總和
我已經想出了兩個解決方案。特例和一般情況。在這兩種情況下,解決方案都以兩次與OpenMP並行的方式在陣列上運行。對於特殊情況,我在兩次通行證上都使用SSE。對於一般情況,我只在第二階段使用它。
我的主要問題是我如何在一般情況下的第一次使用SSE?以下鏈接simd-prefix-sum-on-intel-cpu顯示字節的改進,但不顯示32位數據類型。
特殊情況被稱爲特殊的原因是它需要數組是特殊的格式。例如,讓我們假設浮點數只有16個元素的數組a
。然後,如果陣列重新排列是這樣的(結構的數組爲結構陣列):
a[0] a[1] ...a[15] -> a[0] a[4] a[8] a[12] a[1] a[5] a[9] a[13]...a[3] a[7] a[11] a[15]
SSE垂直求和可以在兩個道次中使用。但是,如果數組已經處於特殊格式並且輸出可以以特殊格式使用,則這將僅有效。否則,必須在輸入和輸出上進行昂貴的重新排列,這會使其比一般情況慢得多。
也許我應該考慮前綴和的不同算法(例如二叉樹)?
代碼一般情況下:
void prefix_sum_omp_sse(double a[], double s[], int n) {
double *suma;
#pragma omp parallel
{
const int ithread = omp_get_thread_num();
const int nthreads = omp_get_num_threads();
#pragma omp single
{
suma = new double[nthreads + 1];
suma[0] = 0;
}
double sum = 0;
#pragma omp for schedule(static) nowait //first parallel pass
for (int i = 0; i<n; i++) {
sum += a[i];
s[i] = sum;
}
suma[ithread + 1] = sum;
#pragma omp barrier
#pragma omp single
{
double tmp = 0;
for (int i = 0; i<(nthreads + 1); i++) {
tmp += suma[i];
suma[i] = tmp;
}
}
__m128d offset = _mm_set1_pd(suma[ithread]);
#pragma omp for schedule(static) //second parallel pass with SSE as well
for (int i = 0; i<n/4; i++) {
__m128d tmp1 = _mm_load_pd(&s[4*i]);
tmp1 = _mm_add_pd(tmp1, offset);
__m128d tmp2 = _mm_load_pd(&s[4*i+2]);
tmp2 = _mm_add_pd(tmp2, offset);
_mm_store_pd(&s[4*i], tmp1);
_mm_store_pd(&s[4*i+2], tmp2);
}
}
delete[] suma;
}
雖然像gcc/icc這樣的編譯器可以爲第二部分做自動矢量化,所以你不需要使用SIMD內在函數。你有沒有得到性能改進,比較普通的c代碼和一些編譯器選項,比如'-msse2' – kangshiyin
他們可能。我把它在MSVC2013上。它不會自動矢量化第二遍。我對MSVC的經驗是,當你使用OpenMP時,你必須自己做矢量化。我不認爲他們中的任何一個都會用SSE代碼展開循環,但在這種情況下無論如何都無濟於事。 –
爲了迴應性能問題,我發佈的通用代碼比在發佈模式下的順序代碼快3倍,AVX在我的4核心ivy橋系統上啓用。時間成本應該是'n/ncores *(1 + 1/SIMD_width)'。所以對於4核和SIMD_width = 2(雙精度)應該是3n/8。這大約增加2.7倍。超線程有一點幫助,所以我推測它超過了3(我使用8個線程,當我嘗試4個線程時,性能下降了一點)。 –