为什么在这个openmp代码中发生了分段错误?

xsl*_*ass 17 fortran 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
Run Code Online (Sandbox Code Playgroud)

模块:

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
Run Code Online (Sandbox Code Playgroud)

编译使用:

ifort -openmp main.f90 my_module.f90
Run Code Online (Sandbox Code Playgroud)

它在执行时给出Segmentation故障.如果删除主程序中的一个打印命令,它运行正常.另外如果删除omp函数并在没有-openmp选项的情况下编译,它也运行正常.

Hri*_*iev 23

导致此行为的最可能原因是您的堆栈大小限制太小(无论出于何种原因).由于e_in每个OpenMP线程都是私有的,因此每个线程在线程堆栈上分配一个副本(即使您已指定-heap-arrays!).取1616 kB(或1579 KiB)的202000元素REAL(KIND=8).

堆栈大小限制可以通过以下几种机制控制:

  • 在标准的Unix系统shell上,堆栈大小的数量由ulimit -s <stacksize in KiB>.这也是主OpenMP线程的堆栈大小限制.pthreads在创建新线程时,POSIX threads()库也将此限制的值用作默认线程堆栈大小.

  • OpenMP支持通过环境变量控制所有其他线程的堆栈大小限制OMP_STACKSIZE.它的值是一个带有可选后缀k/ K用于KiB,m/ Mf为MiB或g/ G用于GiB的数字.此值不会影响主线程的堆栈大小.

  • GNU OpenMP运行时(libgomp)识别非标准环境变量GOMP_STACKSIZE.如果设置它会覆盖值OMP_STACKSIZE.

  • 英特尔OpenMP运行时可识别非标准环境变量KMP_STACKSIZE.如果设置它会覆盖值的值,OMP_STACKSIZE并且还会覆盖使用GOMP_STACKSIZE兼容性OpenMP运行时的值(这是默认值,因为当前唯一可用的英特尔OpenMP运行时库是compat一个).

  • 如果*_STACKSIZE未设置任何变量,则英特尔OpenMP运行时的默认值为2m32位体系结构和4m64 位体系结构.

  • 在Windows上,主线程的堆栈大小是PE头的一部分,并由链接器嵌入到那里.如果使用Microsoft LINK进行链接,则使用/STACK:reserve[,commit].该reserve参数指定以字节为单位的最大堆栈大小而可选commit参数指定初始提交大小.两者都可以使用0x前缀指定为十六进制值.如果不能重新链接可执行文件,则可以通过编辑PE头来修改堆栈大小EDITBIN.它采用与链接器相同的堆栈相关参数.使用MSVC的整个程序优化enabled(/GL)编译的程序无法编辑.

  • Win32目标的GNU链接器支持通过--stack参数设置堆栈大小.要直接从GCC传递选项,-Wl,--stack,<size in bytes>可以使用.

请注意,线程堆栈实际上是使用由(或默认值)设置的大小来分配*_STACKSIZE,与主线程的堆栈不同,主线程的堆栈从小开始,然后按需增长到设置限制.因此,不要设置*_STACKSIZE为任意大的值,否则可能会达到进程虚拟内存大小限制.

这里有些例子:

$ ifort -openmp my_module.f90 main.f90
Run Code Online (Sandbox Code Playgroud)

将主堆栈大小限制设置为1 MiB(额外的OpenMP线程将默认获得4 MiB):

$ ulimit -s 1024
$ ./a.out
zsh: segmentation fault (core dumped)  ./a.out
Run Code Online (Sandbox Code Playgroud)

将主堆栈大小限制设置为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)
Run Code Online (Sandbox Code Playgroud)

将主堆栈大小限制设置为2 MiB,将附加线程的堆栈大小设置为1 MiB:

$ ulimit -s 2048
$ KMP_STACKSIZE=1m ./a.out
zsh: segmentation fault (core dumped)  KMP_STACKSIZE=1m ./a.out
Run Code Online (Sandbox Code Playgroud)

在大多数Unix系统上,主线程的堆栈大小限制由PAM或其他登录机制设置(请参阅参考资料/etc/security/limits.conf).Scientific Linux 6.3的默认值为10 MiB.

另一种可能导致错误的可能情况是虚拟地址空间限制设置得太低.例如,如果虚拟地址空间限制为1 GiB且线程堆栈大小限制设置为512 MiB,则OpenMP运行时将尝试为每个其他线程分配512 MiB.使用两个线程,只有堆栈的1 GiB,当代码,共享库,堆等的空间相加时,虚拟内存大小将超过1 GiB,并且会发生错误:

将虚拟地址空间限制设置为1 GiB,并使用另外两个具有512 MiB堆栈的线程运行(我已将注释注释掉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
Run Code Online (Sandbox Code Playgroud)

在这种情况下,OpenMP运行时库将无法创建新线程,并会在中止程序终止之前通知您.


小智 6

分段错误是由于使用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)
Run Code Online (Sandbox Code Playgroud)

另外,这不会涉及更改任何默认环境参数。

在这里确认并参考ohm314的解决方案:使用堆内存分配的大型数组