24

主程序:

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 选项的情况下编译,它也可以正常运行。

4

2 回答 2

30

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

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

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

  • 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 标头的一部分,并由链接器嵌入其中。如果使用 MicrosoftLINK进行链接,则使用/STACK:reserve[,commit]. reserve参数指定最大堆栈大小(以字节为单位),而可选参数commit指定初始提交大小。两者都可以使用0x前缀指定为十六进制值。如果重新链接可执行文件不是一个选项,则可以通过编辑 PE 标头来修改堆栈大小EDITBIN。它采用与链接器相同的堆栈相关参数。启用 MSVC 的整个程序优化 ( /GL) 编译的程序无法编辑。

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

请注意,线程堆栈实际上是按照设置的大小(或默认值)分配*_STACKSIZE的,与主线程的堆栈不同,主线程的堆栈一开始很小,然后根据需要增长到设置的限制。所以不要设置*_STACKSIZE为任意大的值,否则您可能会达到进程虚拟内存大小限制。

这里有些例子:

$ ifort -openmp my_module.f90 main.f90

将主堆栈大小限制设置为 1 MiB(默认情况下,额外的 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)

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

$ 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 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

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

于 2012-11-07T09:20:02.017 回答
11

分段错误是由于使用 OpenMP 时的堆栈内存限制。使用上一个答案中的解决方案并没有解决我在 Windows 操作系统上的问题。使用堆内存分配而不是堆栈内存似乎有效:

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的解决方案:large array using heap memory allocation

于 2016-05-24T02:47:23.723 回答