2

粒子をセル内のクラウドメッシュに分散させるプログラムがあります。パーティクルの総数(Ntot)をループして、256 ^ 3メッシュを作成します(つまり、各パーティクルは8つのセルに分散されます)。

% gfortran -fopenmp cic.f90 -o ./cic

これはうまくコンパイルされます。しかし、それを実行すると(./cic)、セグメンテーション違反が発生します。私のループは古典的なompdoの問題です。プログラムは、openmpでコンパイルしないと機能します。

!$omp parallel do
 do i = 1,Ntot
   if (x1(i).gt.0.and.y1(i).gt.0.and.z1(i).gt.0) then
     dense(int(x1(i)),int(y1(i)),int(z1(i))) = dense(int(x1(i)),int(y1(i)),int(z1(i))) &
     + dx1(i) * dy1(i) * dz1(i) * mpart
   end if

   if (x2(i).le.Ng.and.y1(i).gt.0.and.z1(i).gt.0) then
     dense(int(x2(i)),int(y1(i)),int(z1(i))) = dense(int(x2(i)),int(y1(i)),int(z1(i))) &
     + dx2(i) * dy1(i) * dz1(i) * mpart
   end if

   if (x1(i).gt.0.and.y2(i).le.Ng.and.z1(i).gt.0) then
     dense(int(x1(i)),int(y2(i)),int(z1(i))) = dense(int(x1(i)),int(y2(i)),int(z1(i))) &
     + dx1(i) * dy2(i) * dz1(i) * mpart
   end if

   if (x2(i).le.Ng.and.y2(i).le.Ng.and.z1(i).gt.0) then
     dense(int(x2(i)),int(y2(i)),int(z1(i))) = dense(int(x2(i)),int(y2(i)),int(z1(i))) &
     + dx2(i) * dy2(i) * dz1(i) * mpart
   end if

   if (x1(i).gt.0.and.y1(i).gt.0.and.z2(i).le.Ng) then
     dense(int(x1(i)),int(y1(i)),int(z2(i))) = dense(int(x1(i)),int(y1(i)),int(z2(i))) &
     + dx1(i) * dy1(i) * dz2(i) * mpart
   end if

   if (x2(i).le.Ng.and.y1(i).gt.0.and.z2(i).le.Ng) then
     dense(int(x2(i)),int(y1(i)),int(z2(i))) = dense(int(x2(i)),int(y1(i)),int(z2(i))) &
     + dx2(i) * dy1(i) * dz2(i) * mpart
   end if

   if (x1(i).gt.0.and.y2(i).le.Ng.and.z2(i).le.Ng) then
     dense(int(x1(i)),int(y2(i)),int(z2(i))) = dense(int(x1(i)),int(y2(i)),int(z2(i))) &
     + dx1(i) * dy2(i) * dz2(i) * mpart
   end if

   if (x2(i).le.Ng.and.y2(i).le.Ng.and.z2(i).le.Ng) then
     dense(int(x2(i)),int(y2(i)),int(z2(i))) = dense(int(x2(i)),int(y2(i)),int(z2(i))) &
     +  dx2(i) * dy2(i) * dz2(i) * mpart
   end if
  end do
!$omp end parallel do

反復間に依存関係はありません。アイデア?

4

2 に答える 2

2

この問題は、他の質問の問題と同様に、OpenMP が有効になっていると自動ヒープ配列が無効になるという事実に起因します。つまり、 を使用しない-fopenmp場合、大きな配列は静的ストレージ (.bssセグメントと呼ばれる) に自動的に配置され、小さな配列はスタックに割り当てられます。OpenMP サポートをオンにすると、自動静的割り当ては使用されず、dense配列はルーチンのスタックに割り当てられます。OS X のデフォルトのスタック制限は非常に制限的であるため、セグメンテーション違反が発生します。

ここにはいくつかのオプションがあります。最初のオプションは、属性denseを指定して静的割り当てを行うことです。もう1つのオプションは、ヒープを作成してからステートメントを使用しSAVEて明示的にヒープに割り当てることです。ALLOCATABLEALLOCATE

REAL, DIMENSION(:,:,:), ALLOCATABLE :: dense

ALLOCATE(dense(256,256,256))

! Computations, computations, computations

DEALLOCATE(dense)

Fortran の新しいバージョンでは、配列SAVEが範囲外になると、属性のない配列の自動割り当て解除がサポートされています。

OpenMP ディレクティブは問題なく、追加のデータ共有句は必要ないことに注意してください。ループ カウンターにはプライベート データ共有クラスが事前に定義されているため、句iで宣言する必要はありません。他の変数は暗黙的に共有されるためPRIVATE、句に入れる必要はありません 。SHAREDただし、実行する操作はdense同期する必要がありますATOMIC UPDATE(または単にATOMIC古い OpenMP 実装で) か、または を使用する必要がありますREDUCTION(+:dense)。アトミックな更新はロックされた追加に変換され、ループ内に条件文があることによる大幅な速度低下と比較して、速度が大幅に低下することはありません。

INTEGER :: xi, yi, zi

!$OMP PARALLEL DO PRIVATE(xi,yi,zi)
...
if (x1(i).gt.0.and.y1(i).gt.0.and.z1(i).gt.0) then
  xi = int(x1(i))
  yi = int(y1(i))
  zi = int(z1(i))
  !$OMP ATOMIC UPDATE
  dense(xi,yi,zi) = dense(xi,yi,zi) &
                  + dx1(i) * dy1(i) * dz1(i) * mpart
end if
...

他のケースでは、適切な変更を加えてコードを複製します。UPDATEコンストラクト内の句についてコンパイラが文句を言う場合はATOMIC、単純に削除してください。

REDUCTION(+:dense)は、各スレッドで のコピーを 1 つ作成denseします。これは大量のメモリを消費し、最終的に適用される削減は、 のサイズに応じてますます遅くなりますdense。小さな配列の場合、アトミック更新よりもうまく機能します。

于 2012-12-14T18:09:21.437 に答える
-1

変数を共有および非公開にする方法については、https://computing.llnl.gov/tutorials/openMP/#Clausesを参照してください。

iプライベートにする必要があるループ変数を除いて、すべての変数を共有する必要があるようです。これは、次の行を使用することをお勧めします。

!$omp parallel do default(shared) private(i)

これにより、セグメンテーション違反が修正されるはずです(すべての変数が正しいと仮定すると

denseただし、異なるスレッドが の同じ部分を同時に上書きしようとするため、合計が不正確になるリスクがあります。このケースから保護するには、各割り当てを orセクションdenseなどでラップする必要があります。!$omp atomic!$omp critical

ただし、このようなクリティカル セクションが原因で、スレッドがほとんどの時間を待機に費やすことになる場合があるため、純粋なシリアル コードよりも改善が見られない場合があります。

dense原則として、キーワードで宣言することでこの問題を解決できますがreduction、残念ながら配列には使用できません。

于 2012-12-14T02:33:47.890 に答える