2

私が書いている openmp コードの予期しない (私にとっては!) 動作を観察しました。コード構造は次のとおりです。

#pragma omp parallel for
for(int i=0;i<N;i++){ 
 // lots of calculations that produce 3 integers i1,i2,i3 and 3 doubles d1,d2,d3 
 #pragma omp atomic 
 J1[i1] += d1;
 #pragma omp atomic
 J2[i2] += d2; 
 #pragma omp atomic
 J3[i3] += d3; 
}

このコードの 3 つの異なるバージョンをコンパイルしました。

1) openmp を使用 (-fopenmp)

2) openmp なし

3) openmp あり、ただし 3 つのアトミック操作なし (アトミック操作が必要なため、テストとして)

バージョン 1) を環境変数 OMP_NUM_THREADS=1 で実行すると、バージョン 2) と比較して大幅な速度低下が見られます。バージョン 3) はバージョン 2) と同じくらい高速に実行されます。

この動作の理由 (シングル スレッドであっても、アトミック操作によってコードの速度が低下するのはなぜですか?!) と、バージョン 1) がバージョン 2)。

質問の最後に、前述の動作を示す実例を添付します。私は1)をコンパイルしました:

g++ -fopenmp -o toy_code toy_code.cpp -std=c++11 -O3

2) と:

g++ -o toy_code_NO_OMP toy_code.cpp -std=c++11 -O3

および 3) 以下の場合:

g++ -fopenmp -o toy_code_NO_ATOMIC toy_code_NO_ATOMIC.cpp -std=c++11 -O3

コンパイラのバージョンは gcc バージョン 5.3.1 20160519 (Debian 5.3.1-20) です。3 つのバージョンの実行時間は次のとおりです。

1) 1分24秒

2) 51秒

3) 51秒

アドバイスをよろしくお願いします!

// toy_code.cpp 
#include <stdio.h>
#include <iostream>
#include <stdlib.h>
#include <cmath>
#include <omp.h>
#define Np 1000000
#define N 1000

int main (){
        double* Xp, *Yp, *J,*Jb;
        Xp = new double[Np];
        Yp = new double[Np];  
        J = new double [N*N];
        Jb = new double [N*N];

        for(int i=0;i<N*N;i++){
            J[i]=0.0;
            Jb[i]=0.0;
        }

        for(int i=0;i<Np;i++){
            Xp[i] = rand()*1.0/RAND_MAX - 0.5;
            Yp[i] = rand()*1.0/RAND_MAX - 0.5;
        }

        for(int n=0; n<2000; n++){
        #pragma omp parallel for
        for(int p=0;p<Np;p++){
            double rx = (Xp[p]+0.5)*(N-1);
            double ry = (Yp[p]+0.5)*(N-1);
            int xindex = (int)floor(rx+0.5);
            int yindex = (int)floor(ry+0.5);
            int k;
            k=xindex*N+yindex;

            #pragma omp atomic
            J[k]+=1;
            #pragma omp atomic
            Jb[k]+=1;
         }
         }

        delete[] Xp;
        delete[] Yp;
        delete[] J;
        delete[] Jb;

return 0;
}
4

1 に答える 1