【问题标题】:Stack Overflow in Fortran programFortran 程序中的堆栈溢出
【发布时间】:2010-12-11 13:44:58
【问题描述】:

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

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

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

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

但是,将值增加到 508 和 508 会导致相同的错误再次出现。

我认为问题与子程序NIGTEE有关,因为当我在没有它的情况下重新排列程序时,一切正常。

我尝试使用菜单project&gt;&gt;settings&gt;&gt;link&gt;&gt;output&gt;&gt;reserve &amp; 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

谢谢。

【问题讨论】:

  • 我阅读了标题并认为这将是关于在 Fortran 中创建一个 SO 克隆。 /掌脸

标签: fortran stack-overflow


【解决方案1】:

更新

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

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

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

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

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

【讨论】:

  • @ghazoo 如果您使用的是 Unix 系统,您可能能够增加最大堆栈大小。请参阅 ulimit 的手册页。
  • @Daniel .. 非常感谢你,我现在真的明白它是如何工作的了。两件事 1) 你是如何计算 381 MB 所需的内存的? 2)我尝试在主程序和子程序中使用 COMMON 方法,但是在子程序中它要求我为 MELE 矩阵(这是我唯一需要通用的矩阵)放置一个特定的维度,它没有接受使用参数..如何做到这一点?谢谢
  • @ghazooo:我认为它是 10,000 x 10,000 x 4 字节(32 位整数)= 400,000,000 字节 = 390625 千字节 = 大约 381 兆字节。如果您的默认整数类型不是 32 位,则需要相应地调整内存值。
  • @ghazooo:老实说,我对COMMON 块不太熟悉。我已经看到它使用包含在代码文件中的外部参数文件完成,但我并不完全熟悉它是如何设置的。您可能想在此网站上提出另一个问题,看看是否有人可以帮助您。
【解决方案2】:

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

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

【讨论】:

  • 你好,谢谢你,不知道有没有这个选项!! :D 我不是专家......我正在考虑使用 INTERFACE 声明语句,但我不知道如何在这里使用它。如果你知道如何使用它......我将非常感激,或者如果你有其他方法来解决这个问题,谢谢
  • @ghazooo: INTERFACE 不会帮助你,因为它用于链接到单独编译的子例程的实现(通常由不同的编译器)。
  • 正如 Daniel 所写,INTERFACE 声明无济于事——这只是描述了子例程,并没有改变它。尝试在手册或帮助中搜索与堆分配相关的选项,或切换到当前支持的编译器。 Daniel 将大型数组放入 COMMON 块的其他建议可能会奏效。
【解决方案3】:

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 肯定会导致它不在堆栈中。

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

【讨论】:

    【解决方案4】:

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

    【讨论】:

    • "allocatable" 是在 Fortran 90 中引入的。至少 Compaq Visual Fortran 的某些版本是 Fortran 95 编译器:cs-software.com/software/fortran/compaq/cvf_66b_relnotes.html:“Compaq Visual Fortran 符合 Fortran 95 标准、Fortran 90 标准和以前的 Fortran 标准。”,因此如果 ghazooo 愿意将 Fortran 90 功能引入他的 FORTRAN 77 程序,那么分配应该是可用的。 (Daniel 已经提出了可分配的建议。)
    猜你喜欢
    • 2012-04-15
    • 2011-08-13
    • 1970-01-01
    • 2019-05-18
    • 2013-12-12
    • 2010-11-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多