c++ - C++とFortranの並列MM速度差ループタイリング

原文 c++ multithreading parallel-processing fortran matrix-multiplication

この行列乗算の実装のC++での実行速度が、並列実行した場合のそれぞれのFortranコードよりも3倍遅い理由がわかりません。それはおよそです。シリアル版も同様です。

program scheduling
!$  use omp_lib
implicit none

integer :: i,j,k,y,x,z
integer, parameter :: tile = 8, N = 1000
double precision, dimension(:), allocatable :: a,b,c,d ! data must be allocated on the heap, otherwise OpenMP would allocate it in the stack and an stackoverflow would  occur for larger matrices.
double precision :: E,S

allocate(a(N*N))
allocate(b(N*N))
allocate(c(N*N))
allocate(d(N*N))

call random_seed()
call random_number(b)
call random_number(a)

!transpose b
do i = 1,N 
   do j = 1,N
      d((i-1)*N+j) = b((j-1)*N+i)
   end do
end do

S = omp_get_wtime()

!$OMP PARALLEL DO SHARED(a,d,c) PRIVATE(i,j,k,x,y,z) SCHEDULE(static) 
do i = 1,N,tile
   do j = 1,N,tile
      do k = 1,N,tile
         do x = i, min( i+tile-1,N)
            do y = j, min( j+tile-1,N)
               do z = k, min( k+tile-1,N)
                  c((x-1)*N+y) = c((x-1)*N+y) + a((x-1)*N+z) * d(z+(y-1)*N)
               enddo
            enddo
          enddo
       enddo
    enddo
 enddo
 !$OMP END PARALLEL DO
 E = omp_get_wtime()  
 print*, (E-S)

 ! Deallocation of memory
 deallocate(a)
 deallocate(b)
 deallocate(c)
 deallocate(d)

end program scheduling


そしてコンパイル:

$ gfortran -O3 -fopenmp scheduling.f08 -o scheduling
$ ./scheduling
0.901.... !for the parallel version and
1.3496... !for the serial version


(btw。a(i、j)などのインデックスバージョンより遅い)

そしてC++コード:

#include <iostream>
#include <cmath>
#include <omp.h>
#include <cstdlib>

int main(int argc, char *argv[])
{
    int i,j,k,x,y,z;
    const int N = 1000;
    double* a = new double[N*N];
    double* b = new double[N*N];
    double* c = new double[N*N];
    double* d = new double[N*N];

    int tile = 8; 

    for(int i = 0; i < N; i++){
        for(int j = 0; j < N; j++){
            a[i*N+j] = rand()%1000; 
            b[i*N+j] = rand()%1000; 
        }
    }
    // transpose
    for(int i = 0; i < N; i++){
        for(int j = 0; j < N; j++){
            d[i*N+j] = b[i+j*N];
        }   
    }
    double start = omp_get_wtime();

//#pragma omp parallel for shared(a,c,d) private(i,j,k,x,y,z) schedule(static)
    for( i = 0; i < N; i+=tile){
        for( j = 0; j < N; j+=tile){
            for( k = 0; k < N; k+=tile){
                for( x = i; x < std::min(i+tile,N); x++){
                    for( y = j; y < std::min(j+tile,N); y++){
                        for( z = k; z < std::min(k+tile,N); z++){
                            c[x*N+y] = c[x*N+y] +  a[x*N+z] * d[z+y*N];
                        }   
                    }     
                }
            }   
        }   
    } 

    double end = omp_get_wtime();
    std::cout << (end-start) << std::endl;

    delete[] a;
    delete[] b;
    delete[] c;
    delete[] d;

    return 0;

}

$g++ -O3 -fopenmp parallel.cpp -o parallel
$./parallel
2.347... //for the parallel version and 
1.47...  //for the serial one


2つのコードの違いは本当にわかりません。おおよそのはずです。同じですが違います。そして、私にはその理由がわかりません。シリアルバージョンからはコードは正しい(または少なくとも私が期待したもの)ようですが、パラレルコードの実行方法は大きく異なります。
答え
スレッド数を明示的に指定する必要があります。これが私が変更したものです:

... ceteris paribus ...
#pragma omp **omp_set_num_threads(2)** parallel for shared(a,c,d) private(i,j,k,x,y,z) 
... 

$ ./MM
0.98...


シリアルバージョンよりも少し高速です。しかし、少なくともそれほど遅くはありません。

それが役に立てば幸い。
関連記事

c++ - カスタムデータ型は、可変サイズの効率のための可能で実行可能なオプションですか?

c++ - このC++プログラムにコマンドライン入力を与える方法

c++ - リンクリストで変更されたデータはメモリに反映されません

c++ - Qtでマウスイベントが発生したときに呼び出される関数の宣言

c++ - OpenCVでのレーン検出器分割線C++

c++ - 誤った値を返す二重ハッシュ関数

c++ - C++プロキシDLL(64ビット)

java - Android Gstreamer SDKでANativeWindow_lockがエラー-22を返す

c++ - doxygenの「ファイルドキュメント」リストから「README.md」を除外します

java - 最小の「XOR」演算でバイナリマトリックスを再作成する