2017-05-12 62 views
0
auto t1 = chrono::steady_clock::now(); 
    #pragma omp parallel 
    { 

     for(int i=0;i<n;i++) 
     { 
      #pragma omp for collapse(2) 
      for(int j=0;j<n;j++) 
      { 

       for(int k=0;k<n;k++) 
       { 
        C[i][j]+=A[i][k]*B[k][j]; 
       } 

      } 
     } 
    } 
auto t2 = chrono::steady_clock::now(); 

auto t = std::chrono::duration_cast<chrono::microseconds>(t2 - t1).count(); 

有和沒有並行化時,變量t保持相當恆定。我不知道爲什麼會發生這種情況。也有一段時間t輸出爲0. 我面臨的另一個問題是,如果我將n的值增加到500,編譯器無法運行程序(這裏我取n = 100) 我使用GNU GCC編譯器的code :: blocks。使用C++中的OpenMP保持矩陣乘法的性能

+1

這看起來更有利於外環的並行性。正如其他人所暗示的,包括內部循環在內的崩潰可能會排除線程中的重要優化。無論如何,在沒有首先優化單線程的情況下拋出並行性都有侷限性。 – tim18

回答

1

建議的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 
+0

感謝您的詳細答案,但事實是,儘管事實上我沒有使用減少與我的崩潰,我得到了正確的答案。另外,當n比處理器數量大得多時,我可以得到這個崩潰可能不需要。儘管如此,根據我的觀點,它應該比串行程序的性能提高一些。另外,如果我將n設爲500,我的程序就會崩潰。可能是什麼原因? – Sanit

+0

如何更改C++中的大小?我無法找到更改omp堆棧大小的方法,並且出現錯誤'libgomp:線程創建失敗:資源暫時不可用'。 – Sanit

+0

嘗試使用較小的OMP_STACKSIZE值。在我的機器上面的數字工作,但它取決於機器。 – Franz

0

collapse指令可能實際上對此負責,因爲索引j是使用divide/mod operations重新創建的。

你試過沒有collapse

+0

您的建議可行,但我仍然不明白在這裏使用摺疊有什麼問題。另外,爲什麼我的程序崩潰,如果我把它拿到像n = 500這樣的東西? – Sanit