我正在使用Fortran90,但我对它并不熟悉。
在代码的某些方面,我想在t0和tf之间创建一个n个线性等间距点的数组,所以我尝试了这个:
t = t0+(/(i,i=0,n-1)/)*(tf-t0)/(n-1)
与以下内容相同:
do while(i<=n)
t(i) = i-1
i=i+1
end do
t = t*(tf-t0)/(n-1)+t0
但是,如果n太大(n = 2000001或更大),则第一个选项不起作用。我收到错误:
forrtl:severe(170):程序异常 - 堆栈溢出
为什么?我该怎么做才能使隐含的do循环适用于n大?
答案 0 :(得分:4)
一些Fortran处理器使用硬件堆栈来存储在评估表达式时创建的临时数据。当表达式涉及大型对象(例如大型数组)时,临时工所需的存储空间可能超过硬件堆栈可用的总存储空间,并且堆栈溢出。
在第一种情况下,您的Fortran处理器可能正在创建一个大型临时文件来保存数组构造函数的结果。
不同的处理器在评估表达式的能力方面具有不同的功能而不需要临时表 - 例如,您可能会发现某些处理器在第二个示例中也存在堆栈溢出问题 - 分配给的变量t也在右侧引用侧面表达式可能足以让一些Fortran处理器要求将整个右侧评估为临时。
您的选择(可能是组合):
使用操作系统或编译器开关为硬件堆栈留出更多存储空间。
使用编译器开关或类似工具来指示处理器在堆上而不是堆栈上创建临时工具。
以不同的方式重述整个操作,使编译器不再创建临时文件(正如您已经探索过的那样)。这可能包括在do循环中将操作重新构造为元素操作的元素,而不是直接在数组上使用操作。
这些方法是特定于平台的。根据错误消息,我猜您在Windows上使用英特尔Fortran(或祖先) - 如果是这样,请参阅/heap-arrays
编译器选项和Windows链接器选项/stack
。如果您使用的是Linux,请参阅ulimit
命令。
答案 1 :(得分:3)
使用隐含循环,在堆栈中创建临时数组,而使用显式循环时,每个元素将单独处理,并且不会创建临时数组。默认情况下,堆栈大小是有限的,因此错误的数组足够大。
我将添加IanH提出的第三点,为了帮助决定是否重构代码以避免临时数组,值得考虑使用-Warray-temporaries与gfortran和-check arg_temp_created,或者更好,-check all,ifort。
答案 2 :(得分:2)
简单的循环没有错:
do i=1,n
t(i)=t0+(i-1)*(tf-t0)/(n-1)
enddo
当人们绝对尝试使用经常隐藏缺陷的数组表达式时,我总是感到惊讶。