建議的OpenMP並行化不正確,可能會導致錯誤的結果。當指定collapse(2)
時,線程同時執行(j,k)次迭代。如果兩個(或更多)線程在相同的j但不同的k上工作,他們將A[i][k]*B[k][j]
的結果累加到相同的陣列位置C[i][j]
。這就是所謂的競爭條件,即「兩個或兩個以上的線程可以訪問共享數據,並嘗試同時更改它」(What is a race condition?)。 數據競爭不一定會導致錯誤的結果,儘管代碼不是OpenMP有效的,並且可能因多種因素(調度,編譯器實現,線程數量...)而產生錯誤結果。要解決在上面的代碼中的問題,OpenMP的提供reduction
條款:
#pragma omp parallel
{
for(int i=0;i<n;i++) {
#pragma omp for collapse(2) reduction(+:C)
for(int j=0;j<n;j++) {
for(int k=0;k<n;k++) {
C[i][j]+=A[i][k]*B[k][j];
使「私人副本中的每個隱含的任務(...)創建,並與減少的價值初始化初始化-identifier。在區域結束後,使用與縮減標識符「(http://www.openmp.org/wp-content/uploads/openmp-4.5.pdf)相關聯的組合器,使用專用副本的值更新原始列表項目」(http://www.openmp.org/wp-content/uploads/openmp-4.5.pdf)。請注意,從OpenMP 4.5開始,標準直接支持C中數組的減少(檢查編譯器是否支持它,否則有舊的手動方法來實現它,Reducing on array in OpenMp)。
然而,對於給定的代碼,它應該是可能更充分,以避免內部循環的並行化,這樣是不是需要在所有的減少:
#pragma omp parallel
{
#pragma omp for collapse(2)
for(int i=0;i<n;i++) {
for(int j=0;j<n;j++) {
for(int k=0;k<n;k++) {
C[i][j]+=A[i][k]*B[k][j];
序列可以快於OpenMP的版本小尺寸的矩陣和/或少量的線程。 在我的英特爾機器上,使用多達16個內核,n = 1000,GNU編譯器v6.1,當-O3優化被激活時,收支平衡點約爲4個核心,而平衡點約爲2個核心編譯-O0。爲了清楚我彙報,我測量性能:
Serial 418020
----------- WRONG ORIG -- +REDUCTION -- OUTER.COLLAPSE -- OUTER.NOCOLLAPSE -
OpenMP-1 1924950 2841993 1450686 1455989
OpenMP-2 988743 2446098 747333 745830
OpenMP-4 515266 3182262 396524 387671
OpenMP-8 280285 5510023 219506 211913
OpenMP-16 2227567 10807828 150277 123368
使用減少性能損失是巨大的(反向加速)。外部平行(w或w/o摺疊)是最好的選擇。
關於您的大矩陣失敗,可能的原因與可用堆棧的大小有關。嘗試擴大系統和OpenMP堆棧大小,即:
ulimit -s unlimited
export OMP_STACKSIZE=10000000
這看起來更有利於外環的並行性。正如其他人所暗示的,包括內部循環在內的崩潰可能會排除線程中的重要優化。無論如何,在沒有首先優化單線程的情況下拋出並行性都有侷限性。 – tim18