Fortran声明数组过大问题解决办法

| Comments

在实际编程中会遇到Fortran程序中声明的数组过大,程序一运行就崩溃,报“段错误”等 错误提示。我在stackoverflow上看到一个针对此问题的解决方法,翻译成中文和大 家分享。


实际编程中,程序经常用到局部大数组用于中间计算。局部变量通常存储在堆栈中,堆栈 一般是整个系统内存的一小部分,通常几十MB。当局部变量大小超出堆栈大小,将会看到 如下症状——在调用相关程序后,但第一条执行语句前发生堆栈溢出

当出现这个问题,最好的办法是找到相关的局部大变量,然后再决定做什么。

一旦定位到变量,并且确定是问题的出处,有一些做法。将数组变小是一种做法。另外一 种方法是使堆栈大小变大,在Linux下可用ulimit -s [newsize]。然而这不是一劳永逸 的方法,在windows机器下必须做些其他。

避免这个问题的另一类方法是不把大数据放到堆栈中,而是放在内存中其他部分(堆中) 。可用通过给数组加上save属性(C中是static,这将使变量放在堆中,因此使变 量在调用之间的持久存在。该方法缺点潜在的改变子程序行为,并且意味子程序不能递归 使用,类似线程不安全。优点是此方法容易并且便于移植。然而,这只对固定大小的局部 变量有用,如果临时数组大小依赖于输入,就不能用该方法(由于不是单个变量为save, 每次程序被调用时可能大小不同)。

有编译器选项可以指定将所有数组(或大于一定大小的所有数组)放在堆中而不是堆栈里 ,每个Fortran编译器都有这个选项。对于ifort,linux中是-heap-arrays或windows中 /heap-arrays。对于gfortran,这可能是默认的。确保会发生什么是有好处的,但这意 味着要使代码工作每个编译器选项不一样。

最后,可以将数组变为allocatable。分配数组将放在堆中,但指向他们的变量在堆栈中, 这样你就得到两种方法的好处。同时这完全是标准Fortran用法,完全可移植。缺点是 需要改变代码。而且,分配过程会花大量时间,如果调用子程序很多次,可能会注意到轻 微拖慢。(然而这个可能的性能退化容易修复,如果多次调用相同大小数组,可以用一个 可选参数传递给一个预分配的局部数组)

Comments