メインプログラム:
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オプションなしでコンパイルすると、正常に実行されます。
この動作の最も可能性の高い原因は、スタックサイズの制限が小さすぎることです(何らかの理由で)。 _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ランタイムライブラリは新しいスレッドの作成に失敗し、プログラムの終了を中止する前に通知します。
セグメンテーション違反は、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の解決策への謝辞と参照: ヒープメモリ割り当てを使用する大きな配列