初期化されていない変数:エラーの検索







多くの科学研究では、Fortranで記述されたコードを使用しています。 また、残念ながら、「科学的な」アプリケーションは、初期化されていない変数などのありふれたエラーの影響を受けません。 言うまでもなく、このような計算は何につながるのでしょうか? そのようなエラーの影響は、科学の「重大な突破」につながる場合や、本当に大きな問題を引き起こす場合があります-結果をどこで使用できるかは誰が知っていますか(しかし、どこで推測しますか)? Intelコンパイラーを使用して既存のFortranコードをチェックし、そのようなトラブルを回避できるようにするための簡単で効果的な方法をいくつか紹介したいと思います。



浮動小数点数に関連する問題を調べます。 初期化されていない変数のエラーは、特にコードがFortran 77規格で記述され始めた場合、見つけるのが困難です。具体的には、変数を宣言しなくても、いわゆるルールに従って、名前の最初の文字に応じて暗黙的に宣言されます暗黙的な型定義(これはすべて最新の標準でもサポートされています)。 I〜Nの文字はタイプINTEGERを示し、残りの文字はタイプREALを示します。 つまり、変数Fがコードに予期せず出現し、それによって何かが乗算される場合、コンパイラはエラーをスローせず、単にFを実際の型にします。 コンパイルと実行が非常にうまくできるこのような素晴らしい例は次のとおりです。



program test z = f*10 print *, z, f end program test
      
      





ご存知のように、すべてが画面に表示されます。 私はこれを持っています:



 -1.0737418E+09 -1.0737418E+08
      
      





同じ標準では、変数を宣言することでそのような「ゲーム」を禁止することが可能でしたが、 暗黙のnoneを書くことによってプログラム単位内でのみ可能であったことは興味深いです。 確かに、いくつかのモジュールでこれを行うのを忘れると、「ファントム」変数がそこに表示されます。 計算で変数名にランダムに追加された文字を見たことがあるのは興味深いです。 どうやら、誰かがノートブックに誤って何かを入力し、ウィンドウの切り替え時にそれらの一部がプログラムコードに追加されたようです。 その結果、すべてが考慮され続け、変数に呪われた人はいませんでした。 このようなエラーを追跡することは、特にコードが何年も問題なく機能している場合は非常に困難です。



したがって、明示的に定義されていない変数については、 暗黙的なnoneを常に使用し、コンパイラーからエラーを取得することを強くお勧めします(初期化され、すべてが問題なくても)。



 program test implicit none ... end program test error #6404: This name does not have a type, and must have an explicit type. [Z] error #6404: This name does not have a type, and must have an explicit type. [F]
      
      





すでに記述されたコードを理解している場合、すべてのソースを変更するのは非常に面倒なので、 / warn:宣言 (Windows)または-warn宣言 (Linux)コンパイラオプションを使用できます。 彼女は私たちに警告を与えます:



 warning #6717: This name has not been given an explicit type. [Z] warning #6717: This name has not been given an explicit type. [F]
      
      





暗黙的に宣言されたすべての変数を処理し、それらにエラーがないことを確認したら、Marlezonバレエの次の部分、つまり初期化されていない変数を検索します。



標準的な方法の1つは、特定の値ですべての変数を初期化するコンパイラーです。これにより、変数を操作するときに、開発者が初期化を忘れたことを簡単に理解できます。 この値は非常に「異常な」値である必要があり、それを操作するときは、いわば「行動に移す」ためにアプリケーションを停止することが望ましいです。



SNaN -Signaling NaN(Not-a-Number)の「シグナル」値を使用することは非常に論理的です。 これは特別な表現を持つ浮動小数点数であり、それを使用して何らかの操作を実行しようとすると、例外が発生します。 特定の変数は値NaNを取得でき、特定の操作(たとえば、ゼロによる除算、ゼロと無限大の乗算、無限大と無限大の除算など)を実行するときに値を取得できます。 したがって、初期化されていない変数の「トラップ」に進む前に、浮動小数点数の処理に関連するコードに例外がないことを確認したいと思います。



これを行うには、オプション/ fpe:0および/ traceback (Windows)、または-fpe0および-traceback (Linux)を有効にし、アプリケーションをビルドして実行します。 すべてが通常通りに進み、例外をスローせずにアプリケーションが出てきた場合、素晴らしいです。 しかし、すでにこの段階でさまざまな「予期せぬ瞬間」が「上昇」する可能性は十分にあります。 そして、すべてfpe0は、浮動小数点数の例外を使用してデフォルトの動作を変更するためです。 デフォルトで無効になっており、これを疑わずに静かに0で除算すると、例外がスローされ、プログラムが停止します。 ところで、0で除算するとき(ゼロで除算するとき)だけでなく、浮動小数点数をオーバーフローさせるとき(浮動小数点オーバーフロー)、無効な操作中(浮動小数点を無効にするとき)も同様です。 同時に、非正規化された数値は0に「リセット」されるため、数値結果もわずかに変化する可能性があります。これは、非正規化された数値の操作が非常に遅いため、アプリケーションの実行時に大幅に加速する可能性がありますが、ゼロ-あなたは理解しています。



別の興味深い点は、ベクトル化などの特定のコンパイラーの最適化の結果としてfpe0オプションで発生する可能性のある例外です。 ループ内にあり、0でない場合は値で除算し、ifチェックを実行するとします。 コンパイラーは、マスクされた操作を使用するよりもはるかに高速であると判断したため、分割が引き続き発生する場合があります。 この場合、投機モードで作業しています。



したがって、これは/ Qfp-speculation:strict (Windows)または-fp-speculation = strict (Linux)オプションを使用して制御でき、浮動小数点数を使用する場合は同様のコンパイラー最適化を無効にします。 別の方法は、 -fp-model strictを使用して作業モデル全体を変更することです。これは、アプリケーションの全体的なパフォーマンスに大きな悪影響を与えます。 インテル®コンパイラーで使用可能なモデルについては、すでに以前に説明しました



ところで、Windowsの/ O1または/ Odオプション(Linuxでは-O1および-O0 )を使用して、最適化のレベルを簡単に下げることができます。



トレースバックオプションを使用すると、エラーが発生した場所に関するより詳細な情報(関数名、ファイル、コード行)を取得できます。



Windowsでテストを行い、最適化せずにコンパイルします( / Odオプションを使用):



 program test implicit none real a,b a=0 b = 1/a print *, 'b=', b end program test
      
      





その結果、画面に次のように表示されます。



 b= Infinity
      
      





/ fpe:0および/ tracebackオプションを有効にして、予期される例外を取得します



 forrtl: error (73): floating divide by zero Image PC Routine Line Source test.exe 00F51050 _MAIN__ 5 test.f90 …
      
      





次のステージの開始前に、このような問題をコードから削除する必要があります。つまり、/ Qinitオプションを使用してSNaN値で強制的に初期化します。snan、arrays / traceback (Windows)または-init = snan、arrays -traceback (Linux)。



これで、初期化されていない変数にアクセスするたびに、ランタイムエラーが発生します。



 forrtl: error (182): floating invalid - possible uninitialized real/complex variable.
      
      





最も単純な例では:



 program test implicit none real a,b b = 1/a print *, 'b=', b end program test forrtl: error (182): floating invalid - possible uninitialized real/complex variable. Image PC Routine Line Source test.exe 00D01061 _MAIN__ 4 test.f90 …
      
      





この突飛な初期化オプションが何であるかについてのいくつかの言葉。 コンパイラバージョン16.0(今日のコンパイラの最新バージョンは17.0であることを思い出してください)から登場したのはそれほど昔ではなく、 SNaNで次の構成を初期化できます。





ただし、 initが機能しない制限がいくつかあります





ところで、このオプションはSNaNの値を初期化できるだけでなく 、それらをnullにすることもできます。 これを行うには、Windowsでは/ Qinit:zero (Linuxでは-init = zero )を指定します。REAL / COMPLEXタイプだけでなく、整数INTEGER / LOGICALも初期化されます。 配列を追加することにより、スカラー値だけでなく配列も初期化します。



たとえば、オプション:



 -init=snan,zero ! Linux and OS X systems /Qinit:snan,zero ! Windows systems
      
      





REAL型またはCOMPLEX型のスカラーを値SNaNで初期化し、INTEGER型またはLOGICAL型をゼロで初期化します。 次の例は、初期化アクションを配列にも拡張します。



 -init=zero -init=snan –init=arrays ! Linux and OS X systems /Qinit:zero /Qinit:snan /Qinit:arrays ! Windows systems
      
      





過去に、Intelは-ftrapuvオプションを使用してこのような機能を実装しようとしましたが、現在は使用が推奨されておらず、推奨されていませんが、計画どおりに値を初期化する必要がありました- うまくいきませんでした。



ところで、第1世代のIntel Xeon Phiコプロセッサー(Knights Corner)で作業している場合、そこにはSNaNのサポートないため、このオプションは利用できません。



最後に、ドキュメントの例を示します。これは、提案されたすべてのオプションを使用してLinuxでコンパイルし、実行時に初期化されていない変数を見つけます。



 ! ============================================================== ! ! SAMPLE SOURCE CODE - SUBJECT TO THE TERMS OF SAMPLE CODE LICENSE AGREEMENT, ! http://software.intel.com/en-us/articles/intel-sample-source-code-license-agreement/ ! ! Copyright 2015 Intel Corporation ! ! THIS FILE IS PROVIDED "AS IS" WITH NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT ! NOT LIMITED TO ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR ! PURPOSE, NON-INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS. ! ! =============================================================== module mymod integer, parameter :: n=100 real :: am real, allocatable, dimension(:) :: dm real, target, dimension(n) :: em real, pointer, dimension(:) :: fm end module mymod subroutine sub(a, b, c, d, e, m) use mymod integer, intent(in) :: m real, intent(in), dimension(n) :: c real, intent(in), dimension(*) :: d real, intent(inout), dimension(*) :: e real, automatic, dimension(m) :: f real :: a, b print *, a,b,c(2),c(n/2+1),c(n-1) print *, d(1:n:33) ! first and last elements uninitialized print *, e(1:n:30) ! middle two elements uninitialized print *, am, dm(n/2), em(n/2) print *, f(1:2) ! automatic array uninitialized e(1) = f(1) + f(2) em(1)= dm(1) + dm(2) em(2)= fm(1) + fm(2) b = 2.*am e(2) = d(1) + d(2) e(3) = c(1) + c(2) a = 2.*b end program uninit use mymod implicit none real, save :: a real, automatic :: b real, save, target, dimension(n) :: c real, allocatable, dimension(:) :: d real, dimension(n) :: e allocate (d (n)) allocate (dm(n)) fm => c d(5:96) = 1.0 e(1:20) = 2.0 e(80:100) = 3.0 call sub(a,b,c,d,e(:),n/2) deallocate(d) deallocate(dm) end program uninit
      
      





最初に、 -fpe0でコンパイルして実行します。

 $ ifort -O0 -fpe0 -traceback uninitialized.f90; ./a.out 0.0000000E+00 -8.7806177E+13 0.0000000E+00 0.0000000E+00 0.0000000E+00 0.0000000E+00 1.000000 1.000000 0.0000000E+00 2.000000 0.0000000E+00 0.0000000E+00 3.000000 0.0000000E+00 0.0000000E+00 0.0000000E+00 1.1448686E+24 0.0000000E+00
      
      





アプリケーションには浮動小数点数を使用した操作に関連する例外はありませんが、いくつかの「奇妙な」値があります。 initオプションを使用して、初期化されていない変数を探します。



 $ ifort -O0 -init=snan -traceback uninitialized.f90; ./a.out NaN NaN 0.0000000E+00 0.0000000E+00 0.0000000E+00 0.0000000E+00 1.000000 1.000000 0.0000000E+00 2.000000 0.0000000E+00 0.0000000E+00 3.000000 NaN 0.0000000E+00 0.0000000E+00 1.1448686E+24 0.0000000E+00 forrtl: error (182): floating invalid - possible uninitialized real/complex variable. Image PC Routine Line Source a.out 0000000000477535 Unknown Unknown Unknown a.out 00000000004752F7 Unknown Unknown Unknown a.out 0000000000444BF4 Unknown Unknown Unknown a.out 0000000000444A06 Unknown Unknown Unknown a.out 0000000000425DB6 Unknown Unknown Unknown a.out 00000000004035D7 Unknown Unknown Unknown libpthread.so.0 00007FC66DD26130 Unknown Unknown Unknown a.out 0000000000402C11 sub_ 39 uninitialized.f90 a.out 0000000000403076 MAIN__ 62 uninitialized.f90 a.out 00000000004025DE Unknown Unknown Unknown libc.so.6 00007FC66D773AF5 Unknown Unknown Unknown a.out 00000000004024E9 Unknown Unknown Unknown Aborted (core dumped)
      
      





39行目で、MYMODモジュールから初期化されていない変数AMを参照していることがわかります。



 b = 2.*am
      
      





このコードには、Intelコンパイラを使用して自分で見つけることをお勧めする他のエラーがあります。 この投稿がFortranでコードを作成するすべての人に役立つこと、そして、アプリケーションが「ライト」のリリース前であっても、初期化されていない変数の必要なチェックに合格することを願っています。 これに感謝し、すぐに会いましょう! 明けましておめでとうございます!



All Articles