プログラム開発で有用なオプション

対象 PGI プログラム開発用 オプション

  PGI の F77, F2003, C, C++ のコンパイラを使用してプログラムを開発する際に、有用なオプションを以下に示します。いずれもプログラムのデバッグを行うときに有効なオプションです。主に、pgfortran を使用した場合の例ですが、コンパイラのオプションの設定方法は、他の言語コンパイラでも同じです。
2012年2月15日更新 Copyright © 株式会社ソフテック

プログラムを開発する際に良く使うコンパイル・オプション

■ 実行時に配列境界のチェック (Bounds check) を行う

$ pgfortran -fastsse -Mbounds test.f 
あるいは
$ pgfortran -fastsse -C test.f 
  • -Mbounds -Mbounds オプションは、実行時に配列の境界チェックを有効にするようにオブジェクトを生成するためのものです。プログラムの配列境界の妥当性を確認するデバッグ時に非常に有効です。例えば、配列境界外のアクセスを行った場合、以下のような形式で出力されます。
【Fortran の場合】
PGFTN-F-Subscript out of range for array a (add.f: 211)
subscript=3, lower bound=1, upper bound=2, dimension=2

【C の場合】
PGC-F-Subscript out of range for array ST2 (test.c: 385)
subscript=2, upper bound=1, dimension=1 

■ 浮動小数点演算での例外処理としてトラップを掛けプログラムを終了する

$ pgfortran -fastsse -Ktrap=fp test.f
  • -Ktrap オプションを使うことで、浮動小数点演算の例外処理の方式の指定が可能です。 default は例外が起きても実行を続行しますが、このトラップ処理を入れることで、例外が起きた時点でプログラムを終了させることが可能です。
  • -Ktrap={flags} 形式で、適用する例外処理のフラグを指定することで、細かな操作が可能となります。以下のフラグがありますが、これはプロセッサの例外処理マスクに相当します。
inv    : invalid operation
denorm : denormalized operand
divz   : divide-by-zero
ovf    : overflow
unf    : underflow
inexact: precision (現在、PGI ではサポートしない、多数の exception が発生する可能性あり)
fp     : inv,divz,ovf と等価
none   :全てのトラップを抑止します(PGI 6.2以降) 

■ 浮動小数点演算の方式を IEEE 754 Standard に厳密に準拠するコードを生成

$  pgfortran -fastsse -Kieee test.f
  • -Kieee オプションを使うことで、浮動小数点演算の処理方式を厳密に IEEE 754 に準拠した計算方式での実行モジュールの作成を行います。この場合、コンパイラが行う最適化のいくつかが実施されません。このオプションの別の側面は、プログラムの結果精度に関する誤差感度の評価に使用できます。
  • -Kieee オプションをリンクステップで指定した場合、システムによってはより精度の高い数学ライブラリがリンクされる場合があります。

情報

IEEE 754 Standard に準拠した演算とは、例えば、演算に伴う右辺、左辺のメモリ・ロード/ストアを IEEE に準拠した形で厳密に行い、最適化等による copy propagetion (変化しない変数、定数の演算上の置き換え)等を行わない計算方式などを言う。また、除算 a/b では、コンパイル時に a*(1/b) といった近似演算も disable される。さらに、内部組み込み関数への浮動小数点の引数の引渡しでは、丸めが行われる。

■ 緩い精度での組込関数計算

-M[no]fprelaxed=[div,order,recip,rsqrt,sqrt]
  • 内部組込み関数 (div/sqrt/rsqrt) の計算において、緩い精度で行うことをコンパイラに指示します。性能は向上しますが、計算精度は劣ります。 (PGI 6.1 以降) デフォルトは、-Mnofprelaxed。
  • サブオプションを付加しない場合(-Mfprelaxedのみ)は、そのターゲットプロセッサに応じて、顕著な性能向上が行える処理に緩い精度での処理を行うかを選択し適用される。
  • PGI 6.2 以降、細かな制御を行うためのサブオプションを導入。サブオプションは以下のとおりです。
div    : 緩い精度で除算処理を行う
order  : a*b+a*cをa*(b+c)と変換する方式も含め、演算の順序の変更を許す
recip  : 緩和した精度で逆数近似 (PGI 9.0 以降)
rsqrt  : 緩い精度でsqrtの逆数近似(1/sqrt)の処理を行う
sqrt   : 緩い精度でsqrtの処理を行う

なお、サブオプションを付加しない場合(-Mfprelaxedのみ)は、そのターゲットプロセッサに応じて、
顕著な性能向上が行える処理に、緩い精度での処理を行うかを選択し適用される。

■ 緩い精度の浮動小数点計算

-M[no]fpapprox[=div|sqrt|rsqrt] 
  • -M[no]fpapprox[=div|sqrt|rsqrt]は、特定の浮動小数点演算において、低精度近似を使用して実行します。(PGI 7.1 以降) このオプションは結果の差異が生じる可能性がありますので、十分注意して使用してください。
div    : 浮動小数点除算近似
rsqrt  : 浮動小数点平方根近似
sqrt   : 浮動小数点逆数平方根近似

デフォルトでは、-Mfpapprox は使用されません。もし、サブ・オプションを指定しない
-Mfpapprox のみの場合は、上記の全てのサブ・オプションが指定されたものとして扱います。

■ x86 レジスタ(スタック)のビット長の制御(Intel px/p5/p6/piii CPUのみ)

$ pgfortran -fastsse -pc 64 test.f
  • -pc オプションは、浮動小数点演算に x87 演算スタックを使用する旧プロセッサ(Pentium III等)において有効です。具体的には、CPU target が px/p5/p6/piii の場合の add, subtract, multiply, divide, square root の演算に影響します。Pentim 4 以上のプロセッサでは、スカラ浮動小数点演算に、デフォルトでは x87 スタックを使用しません。この -pc オプションを使用するときには、強制的に -tp p6 等でクロスコンパイルを行い、実行することが必要です。
  • -pc オプションを使うことで、x86 アーキテクチャ上のレジスタビット長の使用精度の制御が可能です。x87 のレジスタは 80bit 長の長さを有します。PGI のデフォルトは、80bit そのままのビット長でレジスタ IN/OUT 処理を行います。このオプションを指定しない場合のデフォルトは PGI では、80bit ビット長を使用します。
  • -pc {80|64|32} 形式で、ビット長を指定することで、そのビット数でのレジスタ IN/OUT 処理を行うように指定できます。-pc 64 では、64bit を使用したStandard IEEE 倍精度の処理を明示的に指定できます。-pc 32 の場合も同様です。
  • このオプションの別の側面は、プログラムの結果精度に関する誤差感度の評価に使用できます。ビット長を変更することで、計算結果にどの程度の差異が相対的に生じるか評価することで、プログラム実装の精度安定性の見識を得ることが可能です。
  • -pc { 64 | 32 } を指定する場合、main program のコンパイルは、必ず、このオプションを指定してください。

■ 浮動小数点演算の数値結果に影響を与える最適化の有効/無効化

$ pgfortran -fastsse -Mvect=assoc test.f
  • -Mvect=assoc オプションは、より高度な最適化を行うためにループ内の処理を変換(ループ分割、ループネストの変更等)を許可するオプションで、浮動小数点演算の丸め誤差によって引き起こる計算結果の違いが起きても良いことを指示するものです。 ベクトル化における最適化手法では、数式的(数学的)には正しい組み換え(変換)を行いますが、計算機のような有限桁で計算を行う場合は、計算順序の変更により仮数部の桁落ち、あるいは、丸め誤差等が発生する副作用を伴います。これを有効化する場合に assoc フラグを使用します。
  • -Mvect=noassoc は、上記の最適化手法を disable にするオプションです。これは、デフォルトです。
  • このオプションの別の側面は、プログラムの結果精度に関する誤差感度の評価に使用できます。
$ pgfortran -fastsse -Mlre[=array|assoc|noassoc] test.f
  • -Mlre オプションは、loop-carried redundant removal と言う最適化手法です。loop-carried とは、ループ iteration 処理内と言う意味で、この中で共通数式(冗長数式)をまとめる、あるいは冗長な配列の参照を削除する最適化となります。浮動小数点演算の丸め誤差によって引き起こる計算結果の違いが生じる副作用があります。
    • array: 個々の配列要素の参照を冗長性削減の対象として扱う。デフォルトは、2以上のオペランドを含む冗長式のみが対象となる。
    • assoc:冗長性削減の対象を増やすことができる、演算式の再結合を許す最適化。結果の差異が生じる可能性がある。
    • noassoc:上記を許さない最適化
  • -Mnolre は、上記の最適化手法を disable にするオプションです。これは、デフォルトです。
  • このオプションの別の側面は、プログラムの結果精度に関する誤差感度の評価に使用できます。

■ ファイル I/O における内部エンディアン方式の変換

$ pgfortran -fastsse -byteswapio test.f
  • アンフォーマット Fortran データ・ファイルの入出力時にビッグエンディアン (big- Endian) からリトルエンディアン (little-endian) にあるいはその逆に、バイトをスワップ します。生成された実行モジュールは、自動的に read/write 処理中において、このエンディアン変換を行います。
  • RISC/UNIX システム、あるいはハイエンドのシステムで採用されているビッグエンディアン形式のデータファイルから x86 あるいは AMD64 で採用されているリトルエンディアン形式のデータファイルにプログラム実行時に自動変換する際に有効です。
  • データ変換時の仮定として、アンフォーマット・シーケンシャルファイル並びにダイレクト・アクセスファイルのレイアウトが両システム上で同一であることと、内部表現形式が IEEE 形式であることを仮定しています。
(例)
% more rtest.f
      program test
      real*4 ssmi
      OPEN(UNIT=10,FILE='ice.89',FORM='UNFORMATTED')
      read(10) ssmi
      print *,'OK: ',ssmi
      end
 
% more wtest.f
      program test
      real*4 ssmi
      ssmi = -999
      OPEN(UNIT=10,FILE='ice.89',FORM='UNFORMATTED')
      write(10) ssmi
      print *,'OK: ',ssmi
      end

On your Sun workstation (or other big-endian device) 
      f77 -o w_sparc wtest.f
      f77 -o r_sparc rtest.f

On your PGI workstation. 
     pgf77/pgfortran -o w86 wtest.f
     pgf77/pgfortran -o w86_swap -byteswapio wtest.f
     pgf77/pgfortran -o r86 rtest.f
     pgf77/pgfortran -o r86_swap -byteswapio rtest.f
 
------------------------------------------
If you write the file  |  Then read the file
  ice.89 with          |  ice.89 with
 
  w_sparc or w86_swap  |  r_sparc or r86_swap

    w86                |  r86

■ ループ内の演算密度(Intensity) の表示 (PGI 7.2以降)

$ pgfortran -fastsse -Minfo=intensity test.f
  • Minfo=intensity-ループ内の「演算密度」 (Computational Intensity) を表示します。各ループレベルまでの情報を表示しようとしますが、デフォルトではコンパイル時の静的解析できる最内側ループの情報が表示されます。その外側のループレベルの情報は、一度実行した後に、正確な数字が表示できます。演算密度とは、一般にループ内の演算数とメモリのロード・ストア数との比率を表し、演算とメモリ参照のバランスを見るための指標です。このような情報はパフォーマンス・チューニングにおいて特に重視されます。
  • ループ内の演算が浮動小数点演算である場合、演算密度は、浮動小数点演算総数を浮動小数点データのメモリロードとストアの総和で割った比率として定義します。
  • ループ内の演算が整数演算である場合、演算密度は、整数演算総数を整数データのメモリロードとストアの総和で割った比率として定義します。 以下は、Linux上で実施した例です。
PGF90 (Version     10.1)          01/29/2010  11:33:44      page 6
(  292)   do loop=1,nn
(  293)      gosa= 0.0
(  294)      do k=2,kmax-1
(  295)         do j=2,jmax-1
(  296)            do i=2,imax-1
(  297)               s0=a(I,J,K,1)*p(I+1,J,K) &
(  298)                    +a(I,J,K,2)*p(I,J+1,K) &
(  299)                    +a(I,J,K,3)*p(I,J,K+1) &
(  300)                    +b(I,J,K,1)*(p(I+1,J+1,K)-p(I+1,J-1,K) &
(  301)                                -p(I-1,J+1,K)+p(I-1,J-1,K)) &
(  302)                    +b(I,J,K,2)*(p(I,J+1,K+1)-p(I,J-1,K+1) &
(  303)                                -p(I,J+1,K-1)+p(I,J-1,K-1)) &
(  304)                    +b(I,J,K,3)*(p(I+1,J,K+1)-p(I-1,J,K+1) &
(  305)                                -p(I+1,J,K-1)+p(I-1,J,K-1)) &
(  306)                    +c(I,J,K,1)*p(I-1,J,K) &
(  307)                    +c(I,J,K,2)*p(I,J-1,K) &
(  308)                    +c(I,J,K,3)*p(I,J,K-1)+wrk1(I,J,K)
(  309)               ss=(s0*a(I,J,K,4)-p(I,J,K))*bnd(I,J,K)
(  310)               GOSA=GOSA+SS*SS
(  311)               wrk2(I,J,K)=p(I,J,K)+OMEGA *SS
(  312)            enddo
(  313)         enddo
(  314)      enddo
-----------------------

~/Himeno> pgf90 -fast -Minfo=intensity himenoBMTxp.f90
jacobi:
    292, Intensity = [symbolic], and not printable, try the -Mpfi -Mpfo options
    294, Intensity = [symbolic], and not printable, try the -Mpfi -Mpfo options
    295, Intensity = [symbolic], and not printable, try the -Mpfi -Mpfo options
    296, Intensity = 1.06

コンパイル時に情報が特定できないループ情報(外側ループの情報)は、1度実行する。
まず、-Mpfi オプションを付してコンパイル&リンクします。

~/Himeno> pgf90 -fast -Minfo=intensity himenoBMTxp.f90 -Mpfi
~/Himeno> ./a.out (実行)

実行後、pgfi.outと言う統計情報ファイルができます。これを元に再度、フィードバック
コンパイル(-Mpfo) を行うと、以下のように外側ループの Intensity が出力されます。

~/Himeno> pgf90 -fast -Minfo=intensity himenoBMTxp.f90 -Mpfo
jacobi:
    292, Intensity = 12.37
    294, Intensity = 1.06
    295, Intensity = 1.06
    296, Intensity = 1.06

■ プログラムのルーチン間のコールグラフの出力(PGI 6.1以降: Linux only)

●コールグラフ対応実行モジュールの作成
 pgfortran -fastsse -Mipa=cg -o a.out test.f

●コールグラフの出力(pgicgコマンドを使用)

 $pgicg -graph a.out (実行順番のコール・フロー)
 laplce_
 . opnfil_ (opnfil.f:4) [74]
 . . pgf90io_src_info [15 18 23 26]
 . . pgf90io_open [15 23]
 . . pgf90io_ldw_init [18 26]
 . . pgf90io_ldw [18 26]
 . . pgf90io_ldw_end [18 26]
 . pgf90io_src_info [76 83]
 . pgf90io_ldw_init [76 83]
 . pgf90io_ldw [76 83]
 . pgf90io_ldw_end [76 83]
 . input_ (input.f:11) [80]
 . . pgf90io_src_info [18 20 25 27 37 38 39 45]
 . . pgf90io_ldr_init [18 25]
 . . pgf90io_ldr [18 25]
 . . pgf90io_ldr_end [18 25]
 . . pgf90io_ldw_init [20 27]
 . . pgf90io_ldw [20 27]
 . . pgf90io_ldw_end [20 27]
 . . pgf90io_fmtw_init [37 38 39 45]
 . . pgf90io_fmt_write [37 38 39 45]
 . . pgf90io_fmtw_end [37 38 39 45]
 . header_ (header.f:8) [87]
 . . pgf90io_src_info [12 13 14 15]
 . . pgf90io_fmtw_init [12 13 14 15]
 . . pgf90io_fmtw_end [12 13 14 15]
 . . pgf90io_fmt_write [13 14 15]
 . init_ (init.f:16) [100]
 . timer_ (timer.c:8) [104 113]
 . . gettimeofday [13]
 . solve_ (solve.f:14) [106]
 . error_ (error.f:8) [109]
 . swap_ (swap.f:8) [110]
 . verify_ (verify.f:18) [117]
 . epilog_ (epilog.f:13) [121]
 . . pgf90io_src_info [33 35 36 37 39 41 45 47 50 51]
 . . pgf90io_fmtw_init [33 35 36 37 39 41 45 47 50 51]
 . . pgf90io_fmtw_end [33 35 36 37 39 41 45 47 50 51]
 . . pgf90io_fmt_write [35 36 37 41 50 51]
 . clsfil_ (clsfil.f:3) [124]
 . . pgf90io_src_info [11 12]
 . . pgf90io_close [11 12]

 $pgicg -callers a.out
 clsfil_ (clsfil.f:3) called by laplce_
 epilog_ (epilog.f:13) called by laplce_
 error_ (error.f:8)  called by laplce_
 gettimeofday        called by timer_ (timer.c:8)
 header_ (header.f:8) called by laplce_
 init_ (init.f:16)   called by laplce_
 input_ (input.f:11) called by laplce_
 laplce_
 opnfil_ (opnfil.f:4) called by laplce_
 pgf90io_close       called by clsfil_ (clsfil.f:3)

 pgf90io_ldr         called by input_ (input.f:11)
 pgf90io_ldr_end     called by input_ (input.f:11)
 pgf90io_ldr_init    called by input_ (input.f:11)
 pgf90io_ldw         called by input_ (input.f:11) laplce_ opnfil_ (opnfil.f:4)
 pgf90io_ldw_end     called by input_ (input.f:11) laplce_ opnfil_ (opnfil.f:4)
 pgf90io_ldw_init    called by input_ (input.f:11) laplce_ opnfil_ (opnfil.f:4)
 pgf90io_open        called by opnfil_ (opnfil.f:4)
 solve_ (solve.f:14) called by laplce_
 swap_ (swap.f:8)    called by laplce_
 timer_ (timer.c:8)  called by laplce_
 verify_ (verify.f:18) called by laplce_

●pgicg utility のその他のオプションのヘルプ
$ pgicg -help
Known Switches:
-V                  Display version information
-add={func}         Add function to call graph; -add=func,size to change frame
-addcall={func}     Add calls; -addcall=f1,f2,f3 to add call from f2, f3 to f1
-allcallers[={func}]
                    Print out all callers of function
    -allcallers     Print out all callers of all functions
-callers[={func}]   Print out callers of function in path from main program
    -callers        Print out callers of all functions
    -[no]demangle   Do (don't) demangle C++ names
-graph[={func}]     Print out call graph starting at function
    -graph          Prints out call graph starting at main routine
-help               Print out this help
-nevercalled        Print functions not called
-notcalled          Print functions not called in path from main program
-remove={func}      Remove function from call graph
  • プログラム内の各ルーチンが実行時にどのような順番でコールされるかと言う「コールグラフ」の出力が可能です。また、ルーチン間の呼び出し依存関係を出力します。