4

我的简单 Fortran 程序有问题。我在 Fortran 77 中工作,使用 Compaq Visual Fortran。程序结构必须是主程序和子程序的形式,因为它是与有限元法相关的大程序的一部分。

我的问题是我想分别为NHELE和设置值 10000 和 10000 NVELE,但是当我运行代码时,程序停止并给出以下错误:

forrt1: server <170>: program Exception - stack overflow

我尝试反复减少所需的值,直到达到 507 和 507。此时代码运行没有错误。

但是,将值增加到 508 和 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

谢谢。

4

4 回答 4

8

更新

这是您的实际问题。您的数组被声明为按行NM排列的二维单元格数组。如果是 10,000 x 10,000,那么除了程序使用的任何其他内存之外,您将需要超过 381 MB 的内存来单独分配这个数组。(相比之下,如果数组是 500 x 500,则同一个数组只需要大约 1 兆字节的内存。)NHNODENVNODE

问题是旧的 Fortran 会直接在代码段或堆栈中分配所有数组。操作系统“堆”(大型对象的通用内存)的概念是在 1977 年发明的,但 Fortran 77 仍然没有任何构造来使用它。因此,每次调用您的子程序时,它都必须将堆栈指针压入堆栈,以便为堆栈上的 381 兆字节空间腾出空间。这几乎肯定大于您的操作系统允许堆栈段的空间量,并且您正在溢出堆栈内存(并因此得到堆栈溢出)。

解决方案是从不同的地方分配内存。我知道在旧的 Fortran 中,可以使用COMMON块直接从代码段中静态分配内存。您仍然不能动态分配更多,因此您的子例程不能重入,但是如果您的子例程一次只被调用一次(看起来是这样),这可能是最好的解决方案。

更好的解决方案是切换到 Fortran 90 或更高版本,并使用ALLOCATE关键字在堆而不是堆栈上动态分配数组。然后你可以分配尽可能大的块,因为你的操作系统可以给你,但你不必担心堆栈溢出,因为内存将来自另一个地方。

正如 MSB 建议的那样,您可以通过在编译器中更改它来解决此问题,但更好的解决方案是简单地修复代码。

于 2009-10-30T16:14:48.893 回答
3

该编译器是否可以选择将数组放在堆上?

您可以尝试使用其他编译器,例如仍受支持的编译器。Fortran 95 编译器将编译 FORTRAN 77。有很多选择,包括开源。英特尔 Visual Fortran 是 Compaq Visual Fortran 的继任者,在 Windows 和 Mac OS X 上具有堆选项,用于在堆上放置自动和临时数组。

于 2009-10-30T16:10:28.217 回答
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 分配内存的方式并且程序可以运行。大学教师' t 将 NM 的维度作为参数传递——使它们成为常量。优雅的方法是使用三个 PARAMETER 语句设置数组大小来创建包含文件,然后在需要的地方包含该包含文件。快速而肮脏的测试,将是在子例程中重复相同的 PARAMETER 语句 - 但是您有两次相同的信息,如果您进行更改,则必须更改两次。无论哪种情况,您都必须在调用和子例程声明中从子例程参数中删除数组维度,或者使用不同的名称,因为子例程中的相同变量不能是参数和参数。如果这不起作用,请在主程序中声明 NM 并将其作为参数传递给子程序。优雅的方法是使用三个 PARAMETER 语句设置数组大小来创建包含文件,然后在需要的地方包含该包含文件。快速而肮脏的测试,将是在子例程中重复相同的 PARAMETER 语句 - 但是您有两次相同的信息,如果您进行更改,则必须更改两次。无论哪种情况,您都必须在调用和子例程声明中从子例程参数中删除数组维度,或者使用不同的名称,因为子例程中的相同变量不能是参数和参数。如果这不起作用,请在主程序中声明 NM 并将其作为参数传递给子程序。优雅的方法是使用三个 PARAMETER 语句设置数组大小来创建包含文件,然后在需要的地方包含该包含文件。快速而肮脏的测试是在子例程中重复相同的 PARAMETER 语句 - 但是您有两次相同的信息,如果您进行更改,则必须更改两次。无论哪种情况,您都必须在调用和子例程声明中从子例程参数中删除数组维度,或者使用不同的名称,因为子例程中的相同变量不能是参数和参数。如果这不起作用,请在主程序中声明 NM 并将其作为参数传递给子程序。将是在子例程中重复相同的 PARAMETER 语句 - 但是您有两次相同的信息,如果您进行更改,则必须更改两次。无论哪种情况,您都必须在调用和子例程声明中从子例程参数中删除数组维度,或者使用不同的名称,因为子例程中的相同变量不能是参数和参数。如果这不起作用,请在主程序中声明 NM 并将其作为参数传递给子程序。将是在子例程中重复相同的 PARAMETER 语句 - 但是您有两次相同的信息,如果您进行更改,则必须更改两次。无论哪种情况,您都必须在调用和子例程声明中从子例程参数中删除数组维度,或者使用不同的名称,因为子例程中的相同变量不能是参数和参数。如果这不起作用,请在主程序中声明 NM 并将其作为参数传递给子程序。t 是参数和参数。如果这不起作用,请在主程序中声明 NM 并将其作为参数传递给子程序。t 是参数和参数。如果这不起作用,请在主程序中声明 NM 并将其作为参数传递给子程序。

关于 COMMON 块——在编译时需要知道尺寸,因此不能是子程序参数——与上面相同。正如 Daniel 解释的那样,将数组放入 COMMON 肯定会导致它不在堆栈上。

这超出了语言标准——编译器如何提供内存是一个实现细节,“在幕后”。所以解决方案是部分猜测工作。编译器的手册或帮助可能有答案,例如,堆分配选项。

于 2009-10-31T17:28:42.190 回答
0

与数组大小相关的堆栈溢出是一个警告信号,表明它们被整个推到调用堆栈上,而不是堆上。您是否尝试过制作数组变量allocatable?(不过我不确定这在 F77 中是否可行)

于 2009-10-31T17:36:32.793 回答