この章では、古い Fortran プログラムを Fortran 95 に移植するときに起きる可能性がある問題について説明します。
Fortran 95 の拡張機能、および FORTRAN 77 と互換性を保つための機能については、『Fortran ユーザーズガイド』で説明しています。
Fortran キャリッジ制御は、Fortran が最初に開発されていたときに使用されていた、機能の限られた装置から発達したものです。同じ歴史上の理由のため、UNIX オペレーティングシステムから派生したオペレーティングシステムには、Fortran のキャリッジ制御がありません。しかし、次の 2 つの方法で Fortran 95 でこの機能をシミュレートできます。
asa フィルタを使用して、Fortran のキャリッジ制御規則を UNIX のキャリッジ制御書式に変換してから (asa(1) のマニュアルページを参照) lpr を使用してファイルを出力してください。
FORTRAN 77 コンパイラ f77 では、OPEN(N, FORM='PRINT') を使用して、1 行送りや 2 行送り、用紙送り、および 1 カラム目の除去を行なっていました。f95 -f77 とともに FORM='PRINT' を使用してプログラムをコンパイルすると、現在でもこの機能を利用できます。-f77 を使用してコンパイルすると、装置 6 を開き直して FORM パラメータを PRINT に変更できます。たとえば、次のようにします。
OPEN( 6, FORM='PRINT') |
このようにして開いたファイルを、lp(1) コマンドを使用して出力できます。
Fortran の初期のシステムは名前付きファイルを使用せず、実際のファイル名と内部装置番号を対応させるコマンド行機構を提供していました。この機能は、標準の UNIX のリダイレクトなど、いくつかの方法でエミュレートできます。
例: stdin を redir.data からリダイレクトします。 csh(1) を使用した例です。
demo% cat redir.data データファイル 9 9.9 demo% cat redir.f ソースファイル read(*,*) i, z プログラムは標準入力を読み取る print *, i, z stop end demo% f95 -o redir redir.f コンパイル demo% redir < redir.data リダイレクトでデータファイルを読み取り 9 9.90000 demo% |
アプリケーションコードが本来、CRAY や CDC などの 64 ビット (または 60 ビット) メインフレーム用に開発されていた場合、UltraSPARC プラットフォームへポーティングしているときに、これらのコードを次のオプションを付けてコンパイルする場合があります。
-fast -m64 -xtypemap=real:64,double:64,integer:64
このオプションは、自動的にすべてのデフォルトの REAL 変数および定数を REAL*8 に、デフォルトの COMPLEX を COMPLEX*16 に昇格させます。宣言されていない変数または単に REAL や COMPLEX であると宣言されている変数だけを昇格させ、明示的に宣言された変数 (REAL*4 など) は昇格させません。単精度の REAL 定数もすべて REAL*8 に昇格されます。ターゲットプラットフォームに対して適切な -xarch や -xchip を設定します。デフォルトの DOUBLE PRECISION データも REAL*16 に昇格させるには、-xtypemap の例で double:64 を double:128 に変更します。
詳細は、『Fortran ユーザーズガイド』または f95(1) のマニュアルページを参照してください。
Fortran におけるデータオブジェクトのハードウェア表現に関する詳細は、『Fortran ユーザーズガイド』および『数値計算ガイド』を参照してください。通常、システムやハードウェアプラットフォーム間でデータ表現が異なると、移植性に関して重大な問題が生じます。
次のことに注意してください。
Sun では、浮動小数点演算に関しては IEEE 754 規格に準拠しています。このため、REAL*8 の最初の 4 バイトは REAL*4 と同じではありません。
実数型、整数型、論理型のデフォルトサイズは、-xtypemap= オプションを使用して変更する場合を除き、Fortran 95 規格に記述されています。
文字変数は、自由にほかの変数と EQUIVALENCE 文で結合できます。しかし、境界合わせの問題が生じる可能性があるので注意が必要です。
f95 の IEEE 浮動小数点演算では、オーバーフローまたはゼロ除算に関する例外が発生しますが、デフォルト (f95 では -ftrap=common がデフォルト) では SIGFPE を発行したり、トラップしたりしません。例外のシグナルが発行される場合は、結果は IEEE の不定形式になります。これについては、第 6 章「浮動小数点演算」を参照してください。
正規化された無限値が決定されることがあります。libm_single(3F) と libm_double(3F) のマニュアルページを参照してください。書式付き、および並びによる入出力文を使用すると、不定書式の書き込みや読み取りを行うことができます。
古い Fortran アプリケーションの多くは、ホレリス ASCII データを数値データオブジェクトに格納します。1977 Fortran 規格および Fortran 95 において、CHARACTER データ型はこの目的のために提供され、その使用が推奨されています。現在でも古い Fortran のホレリス (n H) 機能を使用して変数を初期化できますが、標準的な使い方ではありません。 次の表に、データ型に適合する文字の最大数を示します。この表では、太字のデータ型は、-xtypemap= コマンド行フラグによって昇格させられるデフォルトの型を示します。
表 7–1 データ型の最大文字数
|
標準 ASCII 文字の最大文字数 |
|
|
|
---|---|---|---|---|
データ型 |
デフォルト |
INTEGER:64 |
REAL:64 |
DOUBLE:128 |
BYTE |
1 |
1 |
1 |
1 |
COMPLEX |
8 |
8 |
16 |
16 |
COMPLEX*16 |
16 |
16 |
16 |
16 |
COMPLEX*32 |
32 |
32 |
32 |
32 |
DOUBLE COMPLEX |
16 |
16 |
32 |
32 |
DOUBLE PRECISION |
8 |
8 |
16 |
16 |
INTEGER |
4 |
8 |
4 |
8 |
INTEGER*2 |
2 |
2 |
2 |
2 |
INTEGER*4 |
4 |
4 |
4 |
4 |
INTEGER*8 |
8 |
8 |
8 |
8 |
LOGICAL |
4 |
8 |
4 |
8 |
LOGICAL*1 |
1 |
1 |
1 |
1 |
LOGICAL*2 |
2 |
2 |
2 |
2 |
LOGICAL*4 |
4 |
4 |
4 |
4 |
LOGICAL*8 |
8 |
8 |
8 |
8 |
REAL |
4 |
4 |
8 |
8 |
REAL*4 |
4 |
4 |
4 |
4 |
REAL*8 |
8 |
8 |
8 |
8 |
REAL*16 |
16 |
16 |
16 |
16 |
例: ホレリスを使用して変数を初期化します。
demo% cat FourA8.f double complex x(2) data x /16Habcdefghijklmnop, 16Hqrstuvwxyz012345/ write( 6, '(4A8, "!")' ) x end demo% f95 -o FourA8 FourA8.f demo% FourA8 abcdefghijklmnopqrstuvwxyz012345! demo% |
そのデータの型で使用しなければならない場合は、ホレリス定数によってデータ項目を初期化し、それをほかのルーチンへ引き渡してください。
ホレリス定数を引数として渡したり、式や比較の中で使用しようとすると、ホレリス定数は文字型の式として解釈されます。コンパイラオプション -xhasc=no を使用して、コンパイラが副プログラム呼び出しにおいて、ホレリス定数を引数の型なしデータとして扱うようにしてください。古い Fortran プログラムを移植するときにこの処理が必要な場合があります。
一般的に、アプリケーションプログラムをあるシステムのコンパイラから別のシステムのコンパイラに移植するとき、非標準のコーティングを削除すれば、移植は簡単になります。あるシステムで成功した最適化や回避策が、ほかのシステムでは曖昧であり、コンパイラを混乱させることもあります。特に、特定のアーキテクチャー用に最適化された手作業によるチューニングは、ほかの場所ではパフォーマンスを低下させる原因となる可能性もあります。これについては、パフォーマンスとチューニングに関する章で説明します。しかし、次の話題は、移植に際して、一般的に考慮すべきことです。
局所変数や COMMON 変数を自動的にゼロに初期化するシステムもあれば、「非数値」(NaN) に初期化するシステムもあります。しかし、標準的な取り決めはありません。 したがって、プログラムは変数の初期値に関して仮定を行うべきではありません。移植性を最大限保証するために、プログラムはすべての変数を初期化すべきです。
別名参照は、同じ記憶領域アドレスが複数の名前で参照されるときに発生します。通常、これは、ポインタを使用している場合や、副プログラムへの実引数が、それら実引数間で、あるいは副プログラム内の COMMON 変数間でオーバーラップしている場合に起こります。たとえば、引数 X と Z は同じ記憶領域の位置を参照します。B と H も同様です。
COMMON /INS/B(100) REAL S(100), T(100) ... CALL SUB(S,T,S,B,100) ... SUBROUTINE SUB(X,Y,Z,H,N) REAL X(N),Y(N),Z(N),H(N) COMMON /INS/B(100) ... |
古い Fortran プログラムの多くは、このような別名での参照を、当時の Fortran 言語では利用できなかった、ある種の動的なメモリー管理の手段として利用していました。
別名での参照は、すべての移植可能なコードの中で避けるべきです。一部のプラットフォーム上では、-O2 よりも高い最適化レベルを使用してコンパイルすると、予測できない結果になることがあります。
f95 コンパイラでは、規格に準拠したプログラムのコンパイルを前提としています。Fortran の規格に厳密に準拠していないプログラムをコンパイルすると、コンパイラによる解析や最適化に支障を来す状況が生じることがあります。その状況によっては、誤った結果が得られる可能性があります。
たとえば、配列の境界を越えて添字を付ける、ポインタを使用する、または、大域的変数を直接使用しているときに副プログラムの引数としても渡すといったことが行われると、コンパイラの機能が制限されて、すべての状況で正しい最適なコードを生成できなくなる可能性があります。
プログラム内に別名参照が存在することが明らかな場合は、-xalias オプションを使用して、コンパイラが考慮すべきレベルを指定してください。適切な -xalias を指定しないで、-O2 よりも高い最適化レベルでコンパイルすると、場合によってはプログラムが正しく実行されなくなることがあります。
このオプションのフラグには、別名で参照する状況の種類を示すキーワードをコンマで区切って並べたリストを指定します。各キーワードには、別名参照が存在しないことを示す no% という接頭辞を付けることができます。
表 7–2 -xalias のキーワードとその意味
次に、別名参照が行われる一般的な状況の例を示します。f95 コンパイラで高い最適化レベル (-O3 以上) でコンパイルする場合は、次に示す別名参照の状況がプログラムに含まれないようにし、-xalias=no%keyword を使用してコンパイルした方が、より良いコードを生成できます。
場合によっては、生成したコードによって正しい結果が得られるようにするために、-xalias=keyword を使用してコンパイルする必要があることがあります。
次の例の場合は、-xalias=dummy を使用してコンパイルする必要があります。
parameter (n=100) integer a(n) common /qq/z(n) call sub(a,a,z,n) ... subroutine sub(a,b,c,n) integer a(n), b(n) common /qq/z(n) a(2:n) = b(1:n-1) c(2:n) = z(1:n-1) コンパイラは、仮変数や共通の変数がオーバーラップする可能性があることを前提と する必要があります。 |
この例が有効なのは、-xalias=craypointer (デフォルト) を使用してコンパイルした場合だけです。
parameter (n=20) integer a(n) integer v1(*), v2(*) pointer (p1,v1) pointer (p2,v2) p1 = loc(a) p2 = loc(a) a = (/ (i,i=1,n) /) ... v1(2:n) = v2(1:n-1) コンパイラは、これらの場所がオーバーラップしている可能性があることを前提とす る必要があります。 |
次に、オーバーラップしない Cray ポインタの例を示します。この場合は、-xalias=no%craypointer を使用してコンパイルします。この方が、より良いパフォーマンスを期待できます。
parameter (n=10) integer a(n+n) integer v1(n), v2(n) pointer (p1,v1) pointer (p2,v2) p1 = loc(a(1)) p2 = loc(a(n+1)) ... v1(:) = v2(:) Cray ポインタは、オーバーラップしたメモリー領域を指していません。 |
次の例を、-xalias=ftnpointer を使用してコンパイルします。
parameter (n=20) integer, pointer :: a(:) integer, target :: t(n) interface subroutine sub(a,b,n) integer, pointer :: a(:) integer, pointer :: b(:) end subroutine end interface a => t a = (/ (i, i=1,n) /) call sub(a,a,n) .... end subroutine sub(a,b,n) integer, pointer :: a(:) real, pointer :: b(:) integer i, mold forall (i=2:n) a(i) = transfer(b(i-1), mold) コンパイラは、a と b がオーバーラップする可能性があることを前提とする必要があり ます。 |
この例では、コンパイラは、a と b が別のデータ型のデータを指していてもオーバーラップする可能性があることを前提とする必要があります。これは、Fortran 規格に反しています。コンパイラは、このような状況を検出すると警告を出力します。
次の例を、-xalias=overindex を使用してコンパイルします。
integer a,z common // a(100),z z = 1 call sub(a) print*, z subroutine sub(x) integer x(10) x(101) = 2 コンパイラは、副プログラムの呼び出しが z への書き込みを引き起こす可能性がある と想定する可能性があります。 -xalias=overindex を使用してコンパイルすると、プログラムは 1 ではなく 2 を 出力します。 |
配列の境界を越えた添字付けは古い FORTRAN 77 のプログラムの多くに含まれていますが、これらは避けるべきものです。多くの場合は、結果を予測することができません。正しい結果が得られることを確かめるには、プログラムをコンパイルし、-C (境界を越えている添字の検査) オプションを使用してテストします。これによって、配列の添字に問題があるかどうかを調べることができます。
一般に、overindex は古い FORTRAN 77 のプログラムにだけ使用することをお勧めします。-xalias=overindex は、配列、構文式、部分配列、WHERE 文、FORALL 文には適用されません。
正しいコードが生成されるように、Fortran 95 のプログラムを常に Fortran 規格の添字の規則に準拠させるようにしてください。たとえば、次の例では、配列構文式の中で不明確な添字付けが行われているため、常に、配列の境界を越えた添字付けによる誤った結果が得られます。
配列の境界を越えて添字が付けられるこの配列構文の例では、正しい結果が得られません。 parameter (n=10) integer a(n),b(n) common /qq/a,b integer c(n) integer m, k a = (/ (i,i=1,n) /) b = a c(1) = 1 c(2:n) = (/ (i,i=1,n-1) /) m = n k = n + n C C a への参照は、実際には b を参照しています。 C これは本来は b(2:n) = b(1:n-1) であるべきです。 C a(m+2:k) = b(1:n-1) C またはこれを逆に行います。 a(k:m+2:-1) = b(n-1:1:-1) ユーザーは直感的に、配列 b が今度は配列 c のようになることを期待しますが、実際 の結果は予測できません。 |
overindex フラグは配列構文式には適用されないため、xalias=overindex フラグを使用してもこの状況は修正されません。この例をコンパイルすることはできますが、生成されたコードからは正しい結果を得られません。この例を書き換えて、配列構文を等価な DO ループに置き換えると、-xalias=overindex を使用してコンパイルしたときにこのフラグが働くようになります。しかし、このようなプログラミングはそもそも避けるべきです。
コンパイラは、局所変数がどのように使用されるかを予測して、副プログラムの呼び出しによって変更されない変数について仮説を立てます。次の例では、副プログラムで使用されているポインタが原因で、コンパイラによる最適化処理が正しく行われず、結果が予測できないものになっています。正しい結果が得られるようにするには、-xalias=actual フラグを使用してコンパイルする必要があります。
program foo integer i call take_loc(i) i = 1 print * , i call use_loc() print * , i end subroutine take_loc(i) integer i common /loc_comm/ loc_i loc_i = loc(i) end subroutine take_loc subroutine use_loc() integer vi1 pointer (pi,vi) common /loc_comm/ loc_i pi = loc_i vi1 = 3 end subroutine use_loc |
take_loc が i のアドレスを取得して保存し、use_loc がそれを使用しています。これは、Fortran 規格に反しています。
-xalias=actual を使用してコンパイルすると、副プログラムへのすべての引数がそのコンパイル単位内の大域的な変数として見なされるべきであるとコンパイラに伝えられます。 このため、コンパイラは、実引数のように見える変数について仮説を立てるときに、より慎重になります。
Fortran 規格に反するこのようなプログラミングは避けるべきです。
リストを付けないで -xalias を指定した場合は、そのプログラムが Fortran の別名参照の規則に反していないものと仮定されます。これは、別名参照の全キーワードに no% が付いていると仮定するのと同じことです。
-xalias を指定しないでコンパイルする場合のコンパイラのデフォルトは次のとおりです。
-xalias=no%dummy,craypointer,no%actual,no%overindex,no%ftnpointer
Cray ポインタを使用していても、Fortran の別名参照の規則に準拠している場合、つまり、不明確な状況でもポインタの参照によって別名参照が行われない場合は、-xalias を使用してコンパイルすることによって、より良い性能のコードが生成される可能性があります。
古いコードには、古いベクトル化コンパイラに特定のアーキテクチャーに最適なコードを生成させるための、通常の計算の DO ループを再構成しているソースコードが含まれていることがあります。ほとんどの場合、この再構成は必要がないもので、しかもプログラムの移植性を下げます。よく使用される再構成は、Strip-mining (ストリップマイニング) とループの展開の 2 つです。
いくつかのアーキテクチャー上では、固定長のベクトルレジスタのために、プログラマは手作業でループ内の配列計算について、セグメントの中にストリップマイニングをしなければいけませんでした。
REAL TX(0:63) ... DO IOUTER = 1,NX,64 DO IINNER = 0,63 TX(IINNER) = AX(IOUTER+IINNER) * BX(IOUTER+IINNER)/2. QX(IOUTER+IINNER) = TX(IINNER)**2 END DO END DO |
ストリップマイニングは最近のコンパイラには適切でありません。このループは、次のようにより明瞭に書くことができます。
DO IX = 1,N TX = AX(I)*BX(I)/2. QX(I) = TX**2 END DO |
以前、手作業によるループの展開はソースコード最適化のための典型的なテクニックでした。しかし、現在はコンパイラがこの再構成を自動的に行います。次にループの例を示します。
DO K = 1, N-5, 6 DO J = 1, N DO I = 1,N A(I,J) = A(I,J) + B(I,K ) * C(K ,J) * + B(I,K+1) * C(K+1,J) * + B(I,K+2) * C(K+2,J) * + B(I,K+3) * C(K+3,J) * + B(I,K+4) * C(K+4,J) * + B(I,K+5) * C(K+5,J) END DO END DO END DO DO KK = K,N DO J =1,N DO I =1,N A(I,J) = A(I,J) + B(I,KK) * C(KK,J) END DO END DO END DO |
前述のループは、本来意図していたとおり、次のように書き換えるべきです。
DO K = 1,N DO J = 1,N DO I = 1,N A(I,J) = A(I,J) + B(I,K) * C(K,J) END DO END DO END DO |
時刻や CPU の経過時間を戻すライブラリ関数は、システムによって異なります。
次の表に、Fortran ライブラリでサポートされる時間関数を示します。
表 7–3 Sun Fortran 時間関数
名称 |
機能 |
マニュアルページ |
---|---|---|
time |
1970 年 1 月 1 日からの経過秒数を返す |
time(3F) |
date |
日付を文字列で返す |
date(3F) |
fdate |
現在の時刻と日付を文字列で返す |
fdate(3F) |
idate |
現在の月、日、年を整数配列で返す |
idate(3F) |
itime |
現在の時、分、秒を整数配列で返す |
itime(3F) |
ctime |
time 関数の返した時間を文字列に変換する |
ctime(3F) |
ltime |
time 関数の返した時間を現地時刻に変換する |
ltime(3F) |
gmtime |
time 関数の返した時間をグリニッジ標準時に変換する |
gmtime(3F) |
etime |
シングルプロセッサ:プログラムの実行で経過したユーザー時間とシステム時間を返す。 複数のプロセッサ:実測時間を返す。 |
etime(3F) |
dtime |
最後に dtime を呼び出した時点から経過したユーザー時間とシステム時間を返す |
dtime(3F) |
date_and_time |
日付と時刻を文字と数値で返す |
date_and_time(3F) |
詳細は、『Fortran ライブラリ・リファレンス』、またはそれぞれの関数のマニュアルページを参照してください。次に、これら時間関数を使用した簡単な例を示します (TestTim.f)。
subroutine startclock common / myclock / mytime integer mytime, time mytime = time() return end function wallclock() integer wallclock common / myclock / mytime integer mytime, time, newtime newtime = time() wallclock = newtime– mytime mytime = newtime return end integer wallclock, elapsed character*24 greeting real dtime, timediff, timearray(2) c 見出しを出力 call fdate( greeting ) print*, " こんにちは。現在の時刻は: , 挨拶 print*, " 'sleep 4' に何秒かかるか見てみよう" call startclock call system( 'sleep 4' ) elapsed = wallclock() print*, "sleep 4 の実行で経過した時間: ", elapsed," 秒" c ここで簡単な計算に必要な CPU 時間をテスト timediff = dtime( timearray ) q = 0.01 do 30 i = 1, 100000 q = atan( q ) 30 continue timediff = dtime( timearray ) print*, "atan(q) 10 万回にかかった時間: ", timediff ," 秒" end |
このプログラムを実行すると、次のような結果になります。
demo% TimeTest こんにちは。現在の時刻は: Thu Feb 8 15:33:36 2001 'sleep 4' に何秒かかるか見てみよう sleep 4 の実行で経過した時間: 4 秒 atan(q) 10 万回にかかった時間: 0.01 秒 demo% |
次の表に示すルーチンは、VMS Fortran のシステムルーチン idate と time との互換機能を提供します。これらのルーチンを使用するときは、f95 のコマンド行で -lV77 オプションを指定する必要があります。この場合、標準の f95 バージョンの代わりに VMS バージョンの方が使用されることになります。
表 7–4 非標準 VMS Fortran システムルーチンの要約
名称 |
定義 |
呼び出し手順 |
引数の型 |
---|---|---|---|
idate |
日、月、年 (d,m,y) 形式の日付 |
call idate( d, m, y ) |
integer |
time |
時分秒 (hhmmss) 形式の現在時刻 |
call time( t ) |
character*8 |
date(3F) ルーチンおよび idate(3F) ルーチンの VMS バージョンは年を示す場合に 2 桁の値しか返さないので、2000 年問題に対応していません。これらのルーチンから返される日付を差し引いて継続時間を計算するプログラムは、1999 年 12 月 31 日以降は正しく機能しなくなります。代わりに、Fortran 95 のルーチン date_and_time(3F) を使用してください。詳細は、『Fortran ライブラリ・リファレンス』を参照してください。
ここでは、Fortran 95 に移植したプログラムが予想どおりに動かないときに何をすればいいのかを提案します。
サイズと工学上の単位に注意してください。ゼロに非常に近い数が異なる場合がありますが、この差異はあまり問題ではありません。特にこの数が 2 つの巨大数の差である場合などは問題ではありません。たとえば、1.9999999e-30 と -9.9992112e-33 は異なりますが、差異はほとんどありません。
VAX の数学演算は IEEE の数学演算ほど正確ではありません。IEEE プロセッサ間でも結果が異なる場合があります。特に、これは三角関数を多く含んでいる場合に顕著です。複雑な要因がからんでおり、また、標準仕様が厳密に定義するのは基本的な算術関数だけです。このため、IEEE マシンの間にさえ微妙に差異があります。浮動小数点の問題については、第 6 章「浮動小数点演算」を確認してください。
call nonstandard_arithmetic() を使用して実行してみてください。これもパフォーマンスをかなり向上させ、Sun のワークステーションをより VAX システムに似せて動作させます。VAX またはほかのシステムが手近にある場合、その上でも実行してみてください。多くの数値アプリケーションが、浮動小数点の実装により多少異なる結果を生成するのは、ごく一般的なことです。
NaN、+Inf やそのほかの考えられるエラーがないか検査してください。さまざまな例外のトラップ方法についての説明は、第 6 章「浮動小数点演算」、または ieee_handler(3m) のマニュアルページを参照してください。ほとんどのマシンでは、これらの例外は単に実行を中止させるだけです。
2 つの数が 6 x 1029 だけ異なっていても、浮動小数点の表現は同じになることもあります。次に、違う数であるのに同じ表現の例を示します。
real*4 x,y x=99999990e+29 y=99999996e+29 write (*,10) x, x 10 format('99,999,990 x 10^29 = ', e14.8, ' = ', z8) write(*,20) y, y 20 format('99,999,996 x 10^29 = ', e14.8, ' = ', z8) end |
出力は次のように表示されます。
99,999,990 x 10^29 = 0.99999993E+37 = 7CF0BDC1 99,999,996 x 10^29 = 0.99999993E+37 = 7CF0BDC1 |
この例では、差は 6 x 1029 です。このような大きな差異が生じる理由は、IEEE の単精度で保証されているのは 10 進 - 2 進変換に対して 10 進の 6 桁だけだからです。7 桁や 8 桁を正しく変換できる場合もありますが、これは値によって異なります。
警告なしにプログラムが異常終了する場合で、実行のたびに異常が発生するまでの時間が異なる場合は、次のように対処してください。
最低の最適化 (-O1) でコンパイルしてください。プログラムが動作するようであれば、いくつかのルーチンを選んで、最適化レベルを上げてコンパイルしてください。
オプティマイザは、プログラムに関して前提条件を付けなければならないことを理解しておいてください。ユーザーが標準以外の処理を行なった場合は、問題を引き起こす可能性があります。すべてのオプティマイザが、プログラムに対してあらゆるレベルの最適化を行うわけではありません。「7.6.2 別名参照と -xalias オプション」を参照してください。