簡體   English   中英

使用C ++中的OpenMP,矩陣乘法的性能保持不變

[英]Performance of matrix multiplications remains unchanged with OpenMP in C++

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。

提議的OpenMP並行化是不正確的,並且可能導致錯誤的結果。 當指定collapse(2) ,線程“同時”執行(j,k)迭代。 如果兩個(或更多)線程在相同的j上但不同的k上工作,則它們將A[i][k]*B[k][j]的結果累加到相同的數組位置C[i][j] 這就是所謂的競爭條件,即“兩個或多個線程可以訪問共享數據,並且它們試圖同時更改共享數據”( 什么是競爭條件? )。 盡管代碼對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];

因此,“將在每個隱式任務(...)中創建一個私有副本,並使用reducer-identifier的初始化值對其進行初始化。在該區域結束之后,原始列表項將使用私有副本的值進行更新。使用與歸約標識符關聯的組合器”( http://www.openmp.org/wp-content/uploads/openmp-4.5.pdf )。 請注意,自Ope​​nMP 4.5起,該標准直接支持C語言中數組的縮減(請檢查編譯器是否支持它,否則,有一些舊的手動方法可以實現,即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的Intel機器上,當激活-O3優化時,GNU編譯器v6.1的收支平衡約為4個內核,而使用-O0編譯的收支平衡約為2個內核。 為了清楚起見,我報告了我測量的性能:

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

減少使用會導致性能損失(加速倒退)。 外部並行化(不折疊或不折疊)是最佳選擇。

關於大型矩陣的故障,可能的原因與可用堆棧的大小有關。 嘗試同時擴大系統和OpenMP堆棧的大小,即

ulimit -s unlimited
export OMP_STACKSIZE=10000000

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

您嘗試沒有collapse嗎?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM