4

単純な Fortran プログラムに問題があります。Compaq Visual Fortran を使用して、Fortran 77 で作業しています。プログラムの構造は、有限要素法に関わる大きなプログラムの一部であるため、メインとサブルーチンの形式でなければなりません。

私の問題は、NHELEと にNVELEそれぞれ 10000 と 10000 の値を設定したいのですが、コードを実行するとプログラムが停止し、次のエラーが表示されます。

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の 2 次元配列として宣言されています。それが 10,000 × 10,000 の場合、この配列だけを割り当てるには、プログラムで使用されている他のメモリを除いて、381 メガバイトを超えるメモリが必要になります。(対照的に、配列が 500 x 500 の場合、同じ配列に対して約 1 メガバイトのメモリしか必要ありません。)NHNODENVNODE

問題は、古い Fortran がすべての配列をコード セグメントまたはスタックに直接割り当てることです。OS の「ヒープ」 (ラージ オブジェクト用の汎用メモリ) の概念は 1977 年までに発明されましたが、Fortran 77 にはそれを利用するための構造がまだありませんでした。したがって、サブルーチンが呼び出されるたびに、スタック ポインターをプッシュして、スタック上に 381 メガバイトのスペースを確保する必要があります。これはほぼ確実に、オペレーティング システムがスタック セグメントに許可しているスペースの量よりも大きく、スタック メモリをオーバーフローさせています (したがって、スタック オーバーフローが発生します)。

解決策は、そのメモリを別の場所から割り当てることです。COMMON古いFortranでは、ブロックを使用してコードセグメントから直接静的にメモリを割り当てることができることを知っています。それ以上を動的に割り当てることはまだできないため、サブルーチンを再入可能にすることはできませんが、サブルーチンが一度に 1 回しか呼び出されない場合 (そうであるように思われます)、これが最善の解決策である可能性があります。

より良い解決策は、Fortran 90 以降に切り替え、ALLOCATEキーワードを使用してスタックではなくヒープに配列を動的に割り当てることです。次に、OS が提供できる限り大きなチャンクを割り当てることができますが、メモリは別の場所から取得されるため、スタックのオーバーフローを心配する必要はありません。

MSB が示唆するように、コンパイラで変更することでこれを修正できる場合がありますが、より良い解決策は、単純にコードを修正することです。

于 2009-10-30T16:14:48.893 に答える
3

そのコンパイラには、ヒープに配列を配置するオプションがありますか?

まだサポートされているコンパイラなど、別のコンパイラを試すことができます。Fortran 95 コンパイラは FORTRAN 77 をコンパイルします。オープン ソースを含め、多くの選択肢があります。Compaq Visual Fortran の後継である Intel 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 (Daniel のように 4 バイトの数字を想定) -- 1.49 GB 対 381 kB。MELE はメイン プログラムで宣言されており、テストの結果、サイズは大きくても許容されます。したがって、NM を追加するとメモリ使用量が制限を超えるか (これら 2 つの配列の合計は 1.86 GB)、宣言の違いが問題になります。MELE のサイズはコンパイル時にわかりますが、NM のサイズは実行時にしかわかりません。そのため、おそらくコンパイラは別の方法でメモリを割り当てます。実際、このプログラムでは NM のサイズはわかっていますが、サブルーチンでは次元が引数として受け取られるため、コンパイラにはサイズが不明です。これを変更すると、コンパイラが NM にメモリを割り当てる方法が変更され、プログラムが実行される可能性があります。ドン' t 引数として NM の次元を渡します -- それらを定数にします。洗練された方法は、配列サイズを設定する 3 つの PARAMETER ステートメントを含むインクルード ファイルを作成し、そのインクルード ファイルを必要な場所にインクルードすることです。テストとして、サブルーチンで同一の PARAMETER ステートメントを繰り返すのは手っ取り早い方法ですが、同じ情報が 2 回あり、変更を行う場合は 2 回変更する必要があります。いずれの場合も、呼び出しとサブルーチン宣言の両方でサブルーチン引数から配列次元を削除するか、別の名前を使用する必要があります。これは、サブルーチン内の同じ変数をパラメーターと引数にすることはできないためです。それが機能しない場合は、メイン プログラムで NM を宣言し、それを引数としてサブルーチンに渡します。洗練された方法は、配列サイズを設定する 3 つの PARAMETER ステートメントを含むインクルード ファイルを作成し、そのインクルード ファイルを必要な場所にインクルードすることです。テストとして、サブルーチンで同一の PARAMETER ステートメントを繰り返すのは手っ取り早い方法ですが、同じ情報が 2 回あり、変更を行う場合は 2 回変更する必要があります。いずれの場合も、呼び出しとサブルーチン宣言の両方でサブルーチン引数から配列次元を削除するか、別の名前を使用する必要があります。これは、サブルーチン内の同じ変数をパラメーターと引数にすることはできないためです。それが機能しない場合は、メイン プログラムで NM を宣言し、それを引数としてサブルーチンに渡します。洗練された方法は、配列サイズを設定する 3 つの PARAMETER ステートメントを含むインクルード ファイルを作成し、そのインクルード ファイルを必要な場所にインクルードすることです。テストとして、サブルーチンで同一の PARAMETER ステートメントを繰り返すのは手っ取り早い方法ですが、同じ情報が 2 回あり、変更を行う場合は 2 回変更する必要があります。いずれの場合も、呼び出しとサブルーチン宣言の両方でサブルーチン引数から配列次元を削除するか、別の名前を使用する必要があります。これは、サブルーチン内の同じ変数をパラメーターと引数にすることはできないためです。それが機能しない場合は、メイン プログラムで NM を宣言し、それを引数としてサブルーチンに渡します。サブルーチンで同一の PARAMETER ステートメントを繰り返すことになりますが、同じ情報が 2 回あり、変更を行う場合は 2 回変更する必要があります。いずれの場合も、呼び出しとサブルーチン宣言の両方でサブルーチン引数から配列次元を削除するか、別の名前を使用する必要があります。これは、サブルーチン内の同じ変数をパラメーターと引数にすることはできないためです。それが機能しない場合は、メイン プログラムで NM を宣言し、それを引数としてサブルーチンに渡します。サブルーチンで同一の PARAMETER ステートメントを繰り返すことになりますが、同じ情報が 2 回あり、変更を行う場合は 2 回変更する必要があります。いずれの場合も、呼び出しとサブルーチン宣言の両方でサブルーチン引数から配列次元を削除するか、別の名前を使用する必要があります。これは、サブルーチン内の同じ変数をパラメーターと引数にすることはできないためです。それが機能しない場合は、メイン プログラムで NM を宣言し、それを引数としてサブルーチンに渡します。パラメータおよび引数である必要はありません。それが機能しない場合は、メイン プログラムで NM を宣言し、それを引数としてサブルーチンに渡します。パラメータおよび引数である必要はありません。それが機能しない場合は、メイン プログラムで NM を宣言し、それを引数としてサブルーチンに渡します。

COMMON ブロックについて - ディメンションはコンパイル時に既知である必要があるため、サブルーチン引数にすることはできません - 上記と同じです。Daniel が説明したように、配列を COMMON に配置すると、間違いなくスタックに配置されなくなります。

これは言語標準を超えています。コンパイラがどのようにメモリを提供するかは、「ボンネットの下」の実装の詳細です。したがって、解決策は部分的に当て推量です。コンパイラのマニュアルまたはヘルプに、ヒープ割り当てオプションなどの回答がある場合があります。

于 2009-10-31T17:28:42.190 に答える
0

配列サイズに関連するスタック オーバーフローは、ヒープではなくコール スタックに全体がプッシュされていることを示す警告サインです。配列変数を作成してみましたallocatableか? (ただし、これが F77 で可能かどうかはわかりません)

于 2009-10-31T17:36:32.793 に答える