2012-11-07 119 views
9

主程序:为什么分段错误发生在这个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选项的情况下编译,它也可以正常运行。

回答

16

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

堆栈大小限制可以通过若干机制来控制:

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

  • OpenMP支持通过环境变量OMP_STACKSIZE控制所有附加线程的堆栈大小限制。其值为KiB的可选后缀k/K,MiB的m/M或GiB的g/G。这个值不会影响主线程的堆栈大小

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

  • 英特尔OpenMP运行时识别非标准环境变量KMP_STACKSIZE。如果设置为覆盖OMP_STACKSIZE的值,并且覆盖GOMP_STACKSIZE的值(如果使用兼容性OpenMP运行时(默认为当前唯一可用的英特尔OpenMP运行时库是compat之一)),则该值将被覆盖。

  • 如果没有*_STACKSIZE变量的设置,为英特尔的OpenMP运行时默认为2m在32位架构和4m在64位的。

  • 在Windows上,主线程的堆栈大小是PE头的一部分,并由链接器嵌入其中。如果使用Microsoft的LINK进行链接,则使用/STACK:reserve[,commit]指定大小。参数reserve指定最大堆栈大小(字节),而可选commit参数指定初始提交大小。两者都可以使用前缀0x指定为十六进制值。如果重新链接可执行文件不是选项,则可以通过编辑EDITBIN的PE标头来修改堆栈大小。它采用与链接器相同的堆栈相关参数。无法编辑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,并将堆栈大小设置为a dditional线程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吉布并用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的运行时库将无法创建一个新线程,并在中止程序终止之前通知您。

+0

我错过了,以为这些阵列都只是2个元素。无论如何,我经常使用数组大一些的-fstack-arrays。但不是与Ifort。 –

+0

使用'gfortran''和'ifort'有点让人困惑,静态数组的私有副本总是自动的,并且在线程堆栈上分配,尽管任何编译器选项可能会影响常规数组的放置。 –

4

分段错误是由于使用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

+1

Windows应用程序的主线程堆栈大小在链接时指定,并且不能像大多数Unix系统一样随意控制。我的答案是Unix特有的。我将添加一个关于Windows的部分。当然,堆分配是最好的解决方案,除非无法真正负责修改代码。 –

相关问题