2

OpenMP を使用して Fortran90 でプログラムを並列化しようとすると、セグメンテーション違反エラーが発生します。

    !$OMP PARALLEL DO NUM_THREADS(4) &
    !$OMP PRIVATE(numstrain, i)
    do irep = 1, nrep
        do i=1, 10
            PRINT *, numstrain(i)
        end do
    end do
    !$OMP END PARALLEL DO

「PRINT *、numstrain(i)」をコメントアウトするか、openmp フラグを削除すると、エラーなしで動作することがわかりました。numstrain(i) に並列アクセスするとメモリアクセス競合が発生するためだと思います。i と numstrain はプライベート変数として宣言済みです。誰かが私に理由を教えてもらえますか?どうもありがとう。:)

アップデート:

以前のバージョンを修正しましたが、このバージョンでは正しい結果を出力できます。

integer, allocatable :: numstrain(:)
integer :: allocate_status
integer :: n
!$OMP PARALLEL DO NUM_THREADS(4) &
!$OMP PRIVATE(numstrain, i)
n = 1000000
do irep = 1, nrep
    allocate (numstrain(n), stat = allocate_status)
    do i=1, 10
        PRINT *, numstrain(i)
    end do
    deallocate (numstrain, stat = allocate_status)
end do
!$OMP END PARALLEL DO

ただし、このサブルーチンによって呼び出される別のサブルーチンにアクセスする numstrain を移動すると (コードは以下に添付)、1. 常に 1 つのスレッドで処理されます。2. ある時点 (i=4 または 5) で、Segmentation Fault:11 を返します。NUM_THREADS が異なると、Segmentation Fault:11 を返す変数 i が異なります。

integer, allocatable :: numstrain(:)
integer :: allocate_status
integer :: n
!$OMP PARALLEL DO NUM_THREADS(4) &
!$OMP PRIVATE(numstrain, i)
n = 1000000
do irep = 1, nrep
    allocate (numstrain(n), stat = allocate_status)
    call anotherSubroutine(numstrain)
    deallocate (numstrain, stat = allocate_status)
end do
!$OMP END PARALLEL DO

subroutine anotherSubroutine(numstrain)
    integer, allocatable   :: numstrain(:)
    do i=1, 10
        PRINT *, numstrain(i)
    end do
end subroutine anotherSubroutine

また、ヘルプ サブルーチンとメイン サブルーチンで割り当て/割り当て解除の両方を試み、ヘルプ サブルーチンでのみ割り当て/割り当て解除を試みました。何も変更されていません。

4

1 に答える 1

2

これの最も一般的な理由は、 のプライベート コピーを保持するための十分なスペースがスタックにないことですnumstrain。次の 2 つの値を計算して比較します。

  • バイト単位の配列のサイズ
  • スタックサイズの制限

2 種類のスタック サイズ制限があります。メイン スレッドのスタック サイズは、Unix システムのプロセス制限などによって制御され (ulimit -sこの制限を確認して変更するために使用)、Windows ではリンク時に固定されます (制限を変更するには、実行可能ファイルの再コンパイルまたはバイナリ編集が必要です)。 )。追加の OpenMP スレッドのスタック サイズは、標準のような環境変数OMP_STACKSIZE、または実装固有のGOMP_STACKSIZE(GNU/GCC OpenMP) およびKMP_STACKSIZE(Intel OpenMP) によって制御されます。

ほとんどの Fortran OpenMP 実装では、ヒープに大きな配列を割り当てるコンパイラ オプションを有効にしても、プライベート配列は常にスタックに置かれることに注意してください (GNUgfortranおよび Intelでテスト済みifort)。

PRINTステートメントをコメント アウトすると、実質的に への参照が削除numstrainされ、コンパイラはそれを自由に最適化できます。たとえば、単に のプライベート コピーを作成できないためnumstrain、スタック制限を超えることはありません。


あなたが提供した追加情報の後、スタックサイズが原因ではないと結論付けることができます。配列を扱うときprivate ALLOCATABLEは、次のことを知っておく必要があります。

  • 未割り当ての配列のプライベート コピーは未割り当てのままです。
  • 割り当てられた配列のプライベート コピーは、同じ境界で割り当てられます。

numstrain並列領域の外で使用しない場合は、最初のケースで行ったことを実行しても問題ありませんが、いくつかの変更があります。

integer, allocatable :: numstrain(:)
integer :: allocate_status
integer, parameter :: n = 1000000
interface
   subroutine anotherSubroutine(numstrain)
      integer, allocatable :: numstrain(:)
   end subroutine anotherSubroutine
end interface

!$OMP PARALLEL NUM_THREADS(4) PRIVATE(numstrain, allocate_status)
allocate (numstrain(n), stat = allocate_status)
!$OMP DO
do irep = 1, nrep
   call anotherSubroutine(numstrain)
end do
!$OMP END DO
deallocate (numstrain)
!$OMP END PARALLEL

並列領域の外でも使用する場合numstrain、割り当てと割り当て解除は外に出ます。

allocate (numstrain(n), stat = allocate_status)
!$OMP PARALLEL DO NUM_THREADS(4) PRIVATE(numstrain)
do irep = 1, nrep
   call anotherSubroutine(numstrain)
end do
!$OMP END PARALLEL DO
deallocate (numstrain)

ALLOCATABLEまた、引数として配列を取るルーチンを呼び出すときは、そのルーチンに明示的なインターフェイスを提供する必要があることも知っておく必要があります。ブロックを作成するかINTERFACE、呼び出されたルーチンをモジュールに配置してからUSEそのモジュールに配置することができます。どちらの場合も、明示的なインターフェイスを提供します。明示的なインターフェイスを提供しない場合、コンパイラは配列を正しく渡さず、サブルーチンはその内容にアクセスできません。

于 2012-11-26T09:14:35.697 に答える