我的简单Fortran程序存在问题。我在Fortran 77工作,使用Compaq Visual Fortran。程序结构必须采用main和子程序的形式,因为它是与有限元方法相关的大程序的一部分。
我的问题是我想设置值10000&分别为NHELE
和NVELE
10000,但是当我运行代码时,程序停止并给出以下错误:
forrt1: server <170>: program Exception - stack overflow
我已经尝试迭代地减少所需的值,直到我达到507&amp; 507.此时代码运行没有错误。
然而,将值增加到508&amp; 508会再次出现相同的错误。
我认为这个问题与子程序NIGTEE
有关,因为当我重新排列没有它的程序时,一切正常。
我尝试使用菜单project>>settings>>link>>output>>reserve & commit
将堆栈大小增加到最大值
但这并没有什么不同。
我该如何解决这个问题?
这是我的计划:
PARAMETER(NHELE=508,NVELE=508)
PARAMETER(NHNODE=NHELE+1,NVNODE=NVELE+1)
PARAMETER(NTOTALELE=NHELE*NVELE)
DIMENSION MELE(NTOTALELE,4)
CALL NIGTEE(NHELE,NVELE,NHNODE,NVNODE,NTOTALELE,MELE)
OPEN(UNIT=7,FILE='MeshNO For Rectangular.TXT',STATUS='UNKNOWN')
WRITE(7,500) ((MELE(I,J),J=1,4),I=1,NTOTALELE)
500 FORMAT(4I20)
STOP
END
SUBROUTINE NIGTEE(NHELE,NVELE,NHNODE,NVNODE,NTOTALELE,MELE)
DIMENSION NM(NVNODE,NHNODE),NODE(4)
DIMENSION MELE(NTOTALELE,4)
KK=0
DO 20 I=1,NVNODE
DO 20 J=1,NHNODE
KK=KK+1
NM(I,J)=KK
20 CONTINUE
KK=0
DO 30 I=1,NVELE
DO 30 J=1,NHELE
NODE(1)=NM(I,J)
NODE(2)=NM(I,J+1)
NODE(3)=NM(I+1,J+1)
NODE(4)=NM(I+1,J)
KK=KK+1
DO 50 II=1,4
50 MELE(KK,II)=NODE(II)
30 CONTINUE
RETURN
END
感谢。
答案 0 :(得分:8)
<强>更新强>:
这是你的实际问题。您的NM
数组被NHNODE
行声明为NVNODE
个单元格的二维数组。如果这是10,000乘10,000,那么除了程序使用的任何其他内存之外,你需要超过381兆字节的内存才能单独分配这个数组。 (相比之下,如果阵列是500乘500,那么同一阵列只需要大约1兆字节的内存。)
问题是旧的Fortran会直接在代码段或堆栈中分配所有数组。操作系统“堆”(大型对象的通用内存)的概念是在1977年发明的,但Fortran 77仍然没有任何构造可以使用它。因此,每次调用子例程时,都必须推送堆栈指针,以便为堆栈上的381 MB空间腾出空间。这几乎可以肯定大于操作系统允许堆栈段的空间量,并且堆栈内存溢出(因此得到stack overflow)。
解决方案是从不同的地方分配内存。我知道在旧的Fortran中,可以使用COMMON
块直接从代码段静态分配内存。您仍然无法动态分配更多,因此您的子例程不能重入,但如果您的子例程一次只调用一次(看起来是这样),这可能是最好的解决方案。
更好的解决方案是切换到Fortran 90或更新版本并使用ALLOCATE
关键字在堆上而不是堆栈上动态分配数组。然后你可以像操作系统给你的那样分配一个大块,但你不必担心堆栈溢出,因为内存将来自另一个地方。
您可以通过在编译器中更改它来解决此问题,如M.S.B.建议,但更好的解决方案是简单地修复代码。
答案 1 :(得分:3)
该编译器是否可以选择将数组放在堆上?
您可以尝试使用其他编译器,例如仍然支持的编译器。 Fortran 95编译器将编译FORTRAN 77.有很多选择,包括开源。英特尔Visual Fortran是Compaq Visual Fortran的继承者,在Windows和Windows上具有堆选项。 Mac OS X用于在堆上放置自动和临时数组。
答案 2 :(得分:1)
MELE实际上是一个更大的数组,然后NM:10000 x 10000 x 4 x 4,相对于10001 x 100001 x 4(假设4字节数字,与Daniel一样) - 1.49 GB与381 kB。 MELE在您的主程序中声明,并且从您的测试中可以接受,即使它更大。因此,添加NM会将内存使用量超过限制(这两个数组的总数为1.86 GB)或声明中的差异很重要。 MELE的大小在编译时是已知的,NM的大小仅在运行时,因此编译器可能以不同方式分配内存。实际上在这个程序中NM的大小是已知的,但是在子例程中,维度作为参数被接收,因此对于编译器来说,大小是未知的。如果更改此设置,编译器可能会更改为NM分配内存的方式,并且程序可能会运行。不要将NM的维度作为参数传递 - 使它们成为常量。优雅的方法是创建一个包含三个PARAMETER语句的包含文件来设置数组大小,然后在需要的地方包含该包含文件。作为测试,快速和脏,将在子例程中重复相同的PARAMETER语句 - 但是您有两次相同的信息,如果进行更改,则必须更改两次。在任何一种情况下,您都必须从调用和子例程声明中的子例程参数中删除数组维度,或使用不同的名称,因为子例程中的相同变量不能是参数和参数。如果这不起作用,则在主程序中声明NM并将其作为参数传递给子例程。
重新使用COMMON块 - 在编译时需要知道维度,因此不能是子程序参数 - 与上面相同。正如Daniel解释的那样,将数组放入COMMON肯定会导致它不在堆栈中。
这超出了语言标准 - 编译器如何提供内存是一个实现细节,“在幕后”。所以解决方案是部分猜测工作。编译器的手册或帮助可能有答案,例如堆分配选项。
答案 3 :(得分:0)
与数组大小相关的堆栈溢出是一个警告信号,表明它们被整个推送到调用堆栈而不是堆上。您是否尝试过制作数组变量allocatable
? (不过我不确定这在F77中是否可行)