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.

4 Answers
4

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.

@ghazoo If you're on a Unix system you might be able to increase the maximum stack size. See the man page for ulimit.
– AmokOct 30 '09 at 19:44

@Daniel .. Thank you very much i really understand now how is it working. Two things 1) how did you calculate the memory needed of 381 MB ?? 2) I tryed to use COMMON method in the main and subroutine program , but inside the subroutine it asked me to put a specific dimensions for the MELE matrix (Which is the only matrix that i need to be common), and it doesn't accept to use the parameters .. how to make that ?? thank you
– ghazoooOct 31 '09 at 5:42

@ghazooo: I'm not too familiar with the COMMON block, honestly. I have seen it done with external parameters files that get included into the code file, but I'm not completely familiar with how that's set up. You might want to ask another question on this site to see if anyone could help you with that.
– Daniel PrydenOct 31 '09 at 16:28

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.

Hi , thank you well , I don't know if it has that option !! :D I am not an expert ... I am thinking of use the INTERFACE declaration statment, but I don't know how to use it here . If you know how to use it ... I will be very thankful or if you have another method to slove this problem thank you
– ghazoooOct 30 '09 at 19:04

@ghazooo: INTERFACE isn't going to help you, since it's for linking to an implementation of the subroutine compiled separately (often by a different compiler).
– Daniel PrydenOct 30 '09 at 19:29

As Daniel wrote, an INTERFACE declaration won't help -- this just describes the subroutine, it doesn't change it. Try searching the manual or the help for an option relating to heap allocation, or switch to a currently supported compiler. Daniel's other suggestion to put the large arrays into a COMMON block might work.
– M. S. B.Oct 30 '09 at 22:49

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.

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)

"allocatable" was introduced in Fortran 90. At least some versions of Compaq Visual Fortran were Fortran 95 compilers: cs-software.com/software/fortran/compaq/cvf_66b_relnotes.html: "Compaq Visual Fortran conforms to the Fortran 95 Standard, Fortran 90 Standard, and previous Fortran standards.", so allocatable should be available if ghazooo is willing to introduce Fortran 90 features into his FORTRAN 77 program. (The allocatable suggestion was already made by Daniel.)
– M. S. B.Nov 1 '09 at 16:38