Fortran 程序中的堆栈溢出

发布于 2024-08-09 15:20:45 字数 1362 浏览 8 评论 0原文

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

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

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

我尝试迭代地减少所需的值,直到达到507& 507. 此时代码运行没有错误。

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

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

我尝试使用菜单 project>>settings>>link>>output>>reserve & 将堆栈大小增加到最大值提交 但这并没有什么区别。

我该如何解决这个问题?

这是我的程序:

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

谢谢。

I have a problem with my simple Fortran program. I am working in Fortran 77, using Compaq Visual Fortran. The program structure must be in the form of a main and a subroutine, because it is part of a big program related to the finite element method.

My issue is that I would like to set the values 10000 & 10000 for NHELE and NVELE respectively, but when I run the code, the program stops and gives the following error:

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

I've tried iteratively reducing the required values, until I reached 507 & 507. At this point the code runs without errors.

However, increasing the values to 508 & 508 causes the same error to reappear.

I think the problem is related to the subroutine NIGTEE, because when I rearrange the program without it, everything works fine.

I've tried increasing the stack size to a maximum by using the menu project>>settings>>link>>output>>reserve & commit
but this didn't make a difference.

How can I solve this problem?

Here is my program:

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

Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

花开雨落又逢春i 2024-08-16 15:20:45

更新

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

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

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

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

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

Update:

Here's your actual problem. Your NM array is being declared as being a two-dimensional array of NHNODE cells by NVNODE rows. If that is 10,000 by 10,000, then you will need more than 381 megabytes of memory to allocate this array alone, aside from any other memory being used by your program. (By contrast, if the array is 500 by 500, you only need about 1 megabyte of memory for the same array.)

The problem is that old Fortran would allocate all the arrays directly in the code segment or on the stack. The concept of an OS "heap" (general purpose memory for large objects) had been invented by 1977, but Fortran 77 still didn't have any constructs for making use of it. So every time your subroutine is called, it has to push the stack pointer to make room for 381 megabytes of space on the stack. This is almost certainly larger than the amount of space your operating system is allowing for the stack segment, and you are overflowing stack memory (and hence getting a stack overflow).

The solution is to allocate that memory from a different place. I know in old Fortran it is possible to use COMMON blocks to statically allocate memory directly from your code segment. You still can't dynamically allocate more, so your subroutine can't be reentrant, but if your subroutine only gets called once at a time (which it appears to be) this may be the best solution.

A better solution would be to switch to Fortran 90 or newer and use the ALLOCATE keyword to dynamically allocate the arrays on the heap instead of the stack. Then you can allocate as large a chunk as your OS can give you, but you won't have to worry about overflowing the stack, since the memory will be coming from another place.

You may be able to fix this by changing it in the compiler, as M.S.B. suggests, but a better solution is to simply fix the code.

桜花祭 2024-08-16 15:20:45

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

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

Does that compiler have an option to put arrays on the heap?

You could try a different compiler, such as one that is still supported. Fortran 95 compilers will compile FORTRAN 77. There are many choices, including open source. Intel Visual Fortran, the successor to Compaq Visual Fortran, has the heap option on Windows & Mac OS X for placing automatic and temporary arrays on the heap.

红墙和绿瓦 2024-08-16 15:20:45

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

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

MELE is actually a larger array then NM: 10000 x 10000 x 4 x 4, versus 10001 x 100001 x 4 (supposing 4 byte numbers, as did Daniel) -- 1.49 GB versus 381 kB. MELE is declared in your main program and, from your tests, is acceptable, even though it is larger. So either adding NM pushes the memory usage over a limit (the total for these two arrays is 1.86 GB) or the difference in the declaration matters. The size of MELE is known at compile time, that of NM only at run time, so probably the compiler allocates the memory differently. Really in this program the size of NM is known, but in the subroutine the dimensions are received as arguments, so to the compiler the size is unknown. If you change this, the compiler may change how it allocates the memory for NM and the program may run. Don't pass the dimensions of NM as arguments -- make them constants. The elegant way would be to make an include file with three PARAMETER statements setting up the array sizes, then include that include file wherever needed. Quick and dirty, as a test, would be to repeat identical PARAMETER statements in the subroutine -- but then you have the same information twice, which has to be changed twice if you make changes. In either case, you have to remove the array dimensions from the subroutine arguments in both the call and subroutine declaration, or use different names, because the same variable in a subroutine can't be a parameter and an argument. If that doesn't work, declare NM in the main program and pass it as an argument to the subroutine.

Re the COMMON block -- the dimensions need to be known at compile time, and so can't be subroutine arguments -- same as above. As Daniel explained, putting the array into COMMON would definitely cause it not to be on the stack.

This is beyond the language standard -- how the compiler provides memory is an implementation detail, "under the hood". So the solution is partially guess work. The manual or help for the compiler might have answers, e.g., a heap allocation option.

梦亿 2024-08-16 15:20:45

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

Stack overflows related to array size is a warning sign that they are being pushed whole onto the call stack, instead of on the heap. Have you tried making the array variables allocatable? (I'm not sure if this is possible in F77, though)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文