数组的分段错误,但仅限于派生类型的组件

Joh*_*hnE 5 fortran gfortran

非常简单的设置,在linux上使用gfortran 4.8.5(红帽):

  • 如果我的实数数组(在派生类型中)的大小> 2,000,000,我会得到一个段错误.这似乎是一个标准的堆栈/堆问题,因为我检查时我的堆栈大小是8mb ulimit.

  • 这是没有问题,如果数组NOT派生类型内

  • 请注意,正如@francescalus猜测的那样,删除初始值可以= 0.0消除这个问题

编辑添加:请注意,我发布了一个后续问题与派生类型的组件相关的分段错误,它代表了一个更现实的用例,并进一步缩小了发生这种情况的条件.

program main

    call sub1     ! seg fault  if col size >   2,100,000
    call sub2     ! works fine at col size = 100,000,000  

end program main

subroutine sub1

    type table
        real :: col(2100000) = 0.0     ! works if "= 0.0" removed
    end type table

    type(table) :: table1
    table1%col = 1.0

end subroutine sub1

subroutine sub2
    real :: col(100000000) = 0.0
    col = 1.0
end subroutine sub2
Run Code Online (Sandbox Code Playgroud)

一些明显的问题:

  • 这是预期的行为,还是在较新版本的gfortran中修复的一些错误?

  • 我在这里遵循标准的fortran操作程序,还是做错了什么?

  • 建议避免这种情况的方法是什么(请假设我在近期内无法更新到更新版本的gfortran)?我几乎肯定会解决一个可分配的数组组件,原因不是这个问题的具体原因,但这可能不是一个理想的通用解决方案,我想知道我在这里有的所有好选项.

  • 特别是,正在初始化派生类型的不良实践的组件?

jme*_*e52 6

由于堆栈不足,这可能是运行时问题,而不是gfortran的错误.

Gfortran使用堆栈来存储自动数组和其他初始化数据.当一个这样的数组很小时代码不会产生问题,但是当数组的大小增加时会出现段错误,可能的原因就是堆栈耗尽.

在更新版本的gfortran中,这个问题似乎是一样的.我用gfortran 4.8.4,4.9.3,5.5.0,6.4.0,7.3.0和8.2.0编译并运行了你的程序.在所有情况下,我都使用默认堆栈大小获得了分段错误,但是当堆栈大小稍微增加时没有错误.

$  ./sfa
Segmentation fault
$ ulimit -s
8192
$ ulimit -s 8256 
$ ./sfa && echo "DONE"
DONE
Run Code Online (Sandbox Code Playgroud)

你的问题可以通过运行来解决

$ ulimit -s unlimited
Run Code Online (Sandbox Code Playgroud)

在执行二进制文件之前 我不知道这样做有什么特别的惩罚,但程序员更多地意识到内存管理的细节,比如编译器开发人员,可能会另有想法.

初始化派生类型的组件并不是一种坏习惯,但正如您所看到的,如果组件是一个大型阵列,它可能会产生堆栈问题 - 无论是由于组件本身的存储还是存储器的存储研究任务的RHS.如果组件在子例程中可分配和分配,则该数组存储在堆中而不是堆栈中,通常可以避免此问题.在这种情况下,它可能是关于在子例程中而不是在编译时动态地设置数组的值.它可能不那么优雅,但我认为这是值得的,因为它是代码开发工作的典型示例,可以在执行二进制文件时防止可避免的,与环境相关的错误.

您上面的代码符合标准.正如注释中所解释的那样,缺少子例程的显式接口并不是一种好的做法,但对于这些简单的子例程,它并不违反规则.

某些编译器具有标志,允许您更改在内存中分配某些对象的位置.虽然它可以解决特定问题,但标志依赖于编译器,并且在比较不同的编译器时通常不等效.根据我的经验,使用可分配的动态内存是一种更强大的解决方案.

最后,请注意,如果您使用的是OpenMP,则上面的ulimit命令只影响主线程 - 您需要通过环境变量设置每个其他线程的堆栈大小OMP_STACKSIZE,而不能unlimited.请记住,运行堆栈的非主线程是一个更难以诊断的问题,因为二进制文件可能在没有正确的分段错误错误的情况下停止.