web-dev-qa-db-ja.com

このopenmpコードでセグメンテーション違反が発生しているのはなぜですか?

メインプログラム:

program main                                                                                                                                                    
  use omp_lib                                                                                                                                                   
  use my_module                                                                                                                                                 
  implicit none                                                                                                                                                 

  integer, parameter :: nmax = 202000                                                                                                                           
  real(8) :: e_in(nmax) = 0.D0                                                                                                                                  
  integer i                                                                                                                                                     

call omp_set_num_threads(2)                                                                                                                                     
!$omp parallel default(firstprivate)                                                                                                                            
!$omp do                                                                                                                                                        
  do i=1,2                                                                                                                                                      
     print *, e_in(i)                                                                                                                                           
     print *, eTDSE(i)                                                                                                                                          
  end do                                                                                                                                                        
!$omp end do                                                                                                                                                    
!$omp end parallel                                                                                                                                              
end program main

モジュール:

module my_module                                                                                                                                                
  implicit none                                                                                                                                                 

  integer, parameter, private :: ntmax = 202000                                                                                                  
  double complex :: eTDSE(ntmax) = (0.D0,0.D0)                                                                                                                  
!$omp threadprivate(eTDSE)                                                                                                                                      

end module my_module

を使用してコンパイル:

ifort -openmp main.f90 my_module.f90

実行時にセグメンテーション違反が発生します。メインプログラムのいずれかの印刷コマンドを削除すると、正常に実行されます。また、omp関数を削除し、-openmpオプションなしでコンパイルすると、正常に実行されます。

18
xslittlegrass

この動作の最も可能性の高い原因は、スタックサイズの制限が小さすぎることです(何らかの理由で)。 _e_in_は各OpenMPスレッドにプライベートであるため、スレッドごとに1つのコピーがスレッドスタックに割り当てられます(_-heap-arrays_!を指定した場合でも)。 REAL(KIND=8)の_202000_要素は1616 kB(または1579 KiB)かかります。

スタックサイズの制限は、いくつかのメカニズムによって制御できます。

  • 標準のUnixシステムシェルでは、スタックサイズの量は_ulimit -s <stacksize in KiB>_によって制御されます。これは、メインのOpenMPスレッドのスタックサイズ制限でもあります。この制限の値は、新しいスレッドを作成するときのデフォルトのスレッドスタックサイズとして、POSIXスレッド(pthreads)ライブラリでも使用されます。

  • OpenMPは、環境変数_OMP_STACKSIZE_を介して、すべての追加スレッドのスタックサイズ制限の制御をサポートします。その値は、KiBの場合はオプションのサフィックスk/K、MiBの場合はm/M f、GiBの場合はg/Gが付いた数値です。この値は、メインスレッドのスタックサイズに影響を与えません

  • GNU OpenMPランタイム(libgomp)は、非標準の環境変数_GOMP_STACKSIZE_を認識します。設定すると、_OMP_STACKSIZE_の値が上書きされます。

  • Intel OpenMPランタイムは、非標準の環境変数_KMP_STACKSIZE_を認識します。設定すると、_OMP_STACKSIZE_の値がオーバーライドされ、互換性OpenMPランタイムが使用されている場合は_GOMP_STACKSIZE_の値もオーバーライドされます(現在使用可能なインテルOpenMPランタイムライブラリは、 compat one)。

  • _*_STACKSIZE_変数が設定されていない場合、インテルOpenMPランタイムのデフォルトは32ビットアーキテクチャーでは_2m_、64ビットアーキテクチャーでは_4m_です。

  • Windowsでは、メインスレッドのスタックサイズはPEヘッダーの一部であり、リンカーによってそこに埋め込まれます。 MicrosoftのLINKを使用してリンクを行う場合、サイズは_/STACK:reserve[,commit]_を使用して指定されます。 reserve引数は最大スタックサイズをバイト単位で指定し、オプションのcommit引数は初期コミットサイズを指定します。どちらも、_0x_プレフィックスを使用して16進値として指定できます。実行可能ファイルの再リンクがオプションでない場合は、PEヘッダーを EDITBIN で編集することにより、スタックサイズを変更できます。リンカと同じスタック関連の引数を取ります。 MSVCのプログラム全体の最適化を有効にしてコンパイルされたプログラム(_/GL_)は編集できません。

  • Win32ターゲットのGNUリンカーは、_--stack_引数を介したスタックサイズの設定をサポートします。オプションをGCCから直接渡すには、_-Wl,--stack,<size in bytes>_を使用できます。

メインスレッドのスタックとは異なり、スレッドスタックは実際には_*_STACKSIZE_(またはデフォルト値)によって設定されたサイズで割り当てられますことに注意してください、これは小さく始まり、要求に応じて設定された制限まで増加します。したがって、_*_STACKSIZE_を任意の大きな値に設定しないでください。設定すると、プロセスの仮想メモリサイズの制限に達する可能性があります。

ここではいくつかの例を示します。

_$ ifort -openmp my_module.f90 main.f90
_

メインスタックサイズの制限を1MiBに設定します(追加のOpenMPスレッドはデフォルトで4 MiBを取得します):

_$ ulimit -s 1024
$ ./a.out
zsh: segmentation fault (core dumped)  ./a.out
_

メインスタックサイズの制限を1700 KiBに設定します。

_$ ulimit -s 1700
$ ./a.out
  0.000000000000000E+000
 (0.000000000000000E+000,0.000000000000000E+000)
  0.000000000000000E+000
 (0.000000000000000E+000,0.000000000000000E+000)
_

メインスタックサイズの制限を2MiBに設定し、追加スレッドのスタックサイズを1MiBに設定します。

_$ ulimit -s 2048
$ KMP_STACKSIZE=1m ./a.out
zsh: segmentation fault (core dumped)  KMP_STACKSIZE=1m ./a.out
_

ほとんどのUnixシステムでは、メインスレッドのスタックサイズ制限はPAMまたは他のログインメカニズムによって設定されます(_/etc/security/limits.conf_を参照)。 Scientific Linux6.3のデフォルトは10MiBです。

エラーにつながる可能性のある別のシナリオは、仮想アドレス空間の制限が低すぎる場合です。たとえば、仮想アドレス空間の制限が1 GiBで、スレッドスタックサイズの制限が512 MiBに設定されている場合、OpenMPランタイムは追加のスレッドごとに512MiBを割り当てようとします。スレッドが2つある場合、スタックのみに1 GiBがあり、コード、共有ライブラリ、ヒープなどのスペースを合計すると、仮想メモリのサイズが1 GiBそしてエラーが発生します:

仮想アドレス空間の制限を1 GiBに設定し、512 MiBスタックで2つの追加スレッドで実行します(omp_set_num_threads()への呼び出しをコメントアウトしました):

_$ ulimit -v 1048576
$ KMP_STACKSIZE=512m OMP_NUM_THREADS=3 ./a.out
OMP: Error #34: System unable to allocate necessary resources for OMP thread:
OMP: System error #11: Resource temporarily unavailable
OMP: Hint: Try decreasing the value of OMP_NUM_THREADS.
forrtl: error (76): Abort trap signal
... trace omitted ...
zsh: abort (core dumped)  OMP_NUM_THREADS=3 KMP_STACKSIZE=512m ./a.out
_

この場合、OpenMPランタイムライブラリは新しいスレッドの作成に失敗し、プログラムの終了を中止する前に通知します。

21
Hristo Iliev

セグメンテーション違反は、OpenMP使用時のスタックメモリ制限が原因です。前の回答のソリューションを使用しても、Windows OSでの問題は解決しませんでした。スタックメモリではなくヒープへのメモリ割り当てを使用すると、うまくいくようです。

integer, parameter :: nmax = 202000  
real(dp), dimension(:), allocatable :: e_in
integer i

allocate(e_in(nmax))

e_in = 0

! rest of code

deallocate(e_in)

さらに、これにはデフォルトの環境パラメータの変更は含まれません。

ここにohm314の解決策への謝辞と参照: ヒープメモリ割り当てを使用する大きな配列

4
ceeely