Переполнение стека в программе на Фортране - PullRequest
4 голосов
/ 30 октября 2009

У меня проблема с моей простой программой на Фортране. Я работаю в Fortran 77, используя Compaq Visual Fortran. Структура программы должна быть в форме главной и подпрограммы, потому что она является частью большой программы, связанной с методом конечных элементов.

Моя проблема в том, что я хотел бы установить значения 10000 и 10000 для NHELE и 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 ]

8 голосов
/ 30 октября 2009

Обновление

Вот ваша актуальная проблема. Ваш массив NM объявляется как двумерный массив из NHNODE ячеек NVNODE строк. Если это 10 000 на 10 000, то вам потребуется более 381 мегабайт памяти для выделения только этого массива, помимо любой другой памяти, используемой вашей программой. (В отличие от этого, если массив 500 на 500, вам понадобится всего около 1 мегабайта памяти для одного и того же массива.)

Проблема в том, что старый Фортран размещал все массивы непосредственно в сегменте кода или в стеке. Концепция ОС "куча" (универсальная память для больших объектов) была изобретена в 1977 году, но у Fortran 77 до сих пор не было никаких конструкций для ее использования. Поэтому каждый раз, когда вызывается ваша подпрограмма, она должна нажимать на указатель стека, чтобы освободить место в стеке для 381 мегабайта. Это почти наверняка больше, чем объем пространства, которое ваша операционная система выделяет для сегмента стека, и вы переполняете стековую память (и, следовательно, получаете переполнение стека ).

Решение состоит в том, чтобы выделить эту память из другого места. Я знаю, что в старом Фортране можно использовать блоки COMMON для статического выделения памяти непосредственно из вашего сегмента кода. Вы по-прежнему не можете динамически распределять больше, поэтому ваша подпрограмма не может быть реентерабельной, но если ваша подпрограмма вызывается только один раз за один раз (что кажется), это может быть лучшим решением.

Лучшим решением было бы переключиться на Fortran 90 или новее и использовать ключевое слово ALLOCATE для динамического размещения массивов в куче, а не в стеке. Затем вы можете выделить такой большой кусок, какой может дать ваша ОС, но вам не придется беспокоиться о переполнении стека, так как память будет поступать из другого места.

Вы можете исправить это, изменив его в компиляторе, как M.S.B. предлагает, но лучшее решение состоит в том, чтобы просто исправить код.

3 голосов
/ 30 октября 2009

Есть ли у этого компилятора возможность помещать массивы в кучу?

Вы можете попробовать другой компилятор, например, тот, который все еще поддерживается. Компиляторы Fortran 95 скомпилируют FORTRAN 77. Есть много вариантов, в том числе с открытым исходным кодом. Intel Visual Fortran, наследник Compaq Visual Fortran, имеет опцию кучи в Windows и Mac OS X для размещения автоматических и временных массивов в куче.

1 голос
/ 31 октября 2009

MELE на самом деле больше массива, чем NM: 10000 x 10000 x 4 x 4 против 10001 x 100001 x 4 (предполагается, что 4-байтовые числа, как это делал Даниэль) - 1,49 ГБ против 381 кБ. MELE объявлен в вашей основной программе и, исходя из ваших тестов, является приемлемым, даже если он больше. Таким образом, добавление NM приводит к превышению лимита использования памяти (общий объем этих двух массивов составляет 1,86 ГБ), или разница в объявлении имеет значение. Размер MELE известен во время компиляции, а размер NM - только во время выполнения, поэтому, вероятно, компилятор выделяет память по-разному. Действительно, в этой программе размер NM известен, но в подпрограмме измерения принимаются в качестве аргументов, поэтому компилятору размер неизвестен. Если вы измените это, компилятор может изменить то, как он выделяет память для NM, и программа может работать. Не передавайте размеры NM в качестве аргументов - сделайте их постоянными. Элегантным способом было бы создать файл включения с тремя операторами PARAMETER, задающими размеры массива, а затем включить этот файл включения там, где это необходимо. Быстро и грязно, как тест, было бы повторить идентичные операторы PARAMETER в подпрограмме - но тогда у вас есть одна и та же информация дважды, которую нужно изменить дважды, если вы вносите изменения. В любом случае вы должны удалить измерения массива из аргументов подпрограммы в объявлении вызова и подпрограммы или использовать разные имена, поскольку одна и та же переменная в подпрограмме не может быть параметром и аргументом. Если это не сработает, объявите NM в основной программе и передайте его в качестве аргумента подпрограмме.

Относительно ОБЩЕГО блока - размеры должны быть известны во время компиляции, и поэтому не могут быть аргументами подпрограммы - так же, как и выше. Как объяснил Даниэль, помещение массива в COMMON определенно приведет к тому, что он не будет в стеке.

Это выходит за рамки языкового стандарта - то, как компилятор обеспечивает память, является деталью реализации "под капотом". Так что решение частично угадать работу. В руководстве или справке для компилятора могут быть ответы, например, опция выделения кучи.

0 голосов
/ 31 октября 2009

Переполнения стека, связанные с размером массива, являются предупреждением о том, что они помещаются целиком в стек вызовов, а не в кучу. Вы пытались сделать переменные массива allocatable? (Хотя я не уверен, возможно ли это в F77)

...