ナビゲーションリンクをスキップ | |
印刷ビューの終了 | |
Oracle Solaris Studio 12.3: Fortran ユーザーズガイド Oracle Solaris Studio 12.3 Information Library (日本語) |
Fortran の注釈の書式であるソースコード「指令」を使用して、特殊な最適化または並列化の選択に関する情報をコンパイラに渡すことができます。コンパイラ指令はプラグマと呼ばれることもあります。コンパイラは一般的な指令と並列化指令のセットを認識します。f95 は、OpenMP 共有メモリーマルチプロセッシング指令も処理します。
f95 に固有の指令については、「4.8 指令」で説明します。f95 によって認識されるすべての指令については、付録 C Fortran 指令の要約を参照してください。
注 - 指令は Fortran 規格には含まれていません。
一般的な Fortran 指令は次のような書式で使用します。
!$PRAGMA keyword ( a [ , a ] … ) [ , keyword ( a [ , a ] … ) ] ,…
!$PRAGMA SUN keyword ( a [ , a ] … ) [ , keyword ( a [ , a ] … ) ] ,…
変数 keyword は特定の指令を表します。追加の引数やサブオプションも指定できます。指令によっては、前述に示す追加のキーワード SUN を指定する必要があります。
一般的な指令の構文は、次のとおりです。
1 カラム目は、注釈指示子の文字 c、C、!、* などです。
f95 の自由形式では、! は認識される唯一の注釈指示子 (!$PRAGMA) です。この章の例では、Fortran 95 自由形式を想定しています。
次の 7 文字は空白文字を入れず $PRAGMA とします。大文字でも小文字でもかまいません。
自由形式ソースプログラムでは、! という注釈指示子文字を使用する指令は、行のどの位置にも記述できます。
制限事項は、次のとおりです。
Fortran テキストの場合と同様、最初の 8 文字のあとでは、空白は無視され、大文字と小文字は区別されません。
これは注釈であるため、指令は継続できませんが、必要に応じて多くの !$PRAGMA 行を順番に使用できます。
注釈が前述の構文条件を満たしていると、コンパイラが認識できる指令が 1 つまたは複数含まれていることになります。前述の構文条件を満たしていない場合は、警告メッセージが出力されます。
C プリプロセッサの cpp は注釈行または指令行の中でマクロシンボル定義を展開します。Fortran プリプロセッサの fpp は注釈行の中でマクロの展開は行いませんが、fpp は正当な f95 の指令は認識し、指令キーワード外の制限付き置換は実行します。ただし、キーワード SUN が必要な指令には注意してください。cpp は、小文字の sun を事前定義した値で置き換えます。また、cpp マクロ SUN を定義すると、SUN 指令キーワードが干渉されます。一般的な規則では、次のようにソースが cpp または fpp で処理される場合、プラグマは大文字と小文字を混在させて指定します。
!$PRAGMA Sun UNROLL=3
別の解決策として、.F ファイルをコンパイルするときに、-Usun を追加する場合もあります。
Fortran のコンパイラは、次の一般的な指令を認識します。
表 2-2 一般的な Fortran 指令の要約
|
C() 指令は、その引数が外部関数であることを指定します。EXTERNAL 宣言と同義です。ただし、通常の外部名とは違って、Fortran コンパイラでは、これらの引数名に下線が付けられません。詳細は、『Fortranプログラミングガイド』の「C と Fortran のインタフェース」の章を参照してください。
特殊な関数の C() 指令は、各副プログラム中にある、その関数への最初の引用よりも前に現れなければなりません。
例: C で ABC と XYZ をコンパイルします。
EXTERNAL ABC, XYZ !$PRAGMA C(ABC, XYZ)
この指令では、コンパイラは、特定の呼び出しを解釈するとき、総称手続きのインタフェースで表示される仮引数名の型、種別、次元数を無視します。
たとえば、次の手続きのインタフェースでは、SRC はどのようなデータ型でもよく、LEN は KIND=4 または KIND=8 のいずれかであることが可能です。インタフェースブロックは、汎用的な手順名に対し 2 つの特定の手順を定義します。この例は、Fortran 95 自由形式で示されます。
INTERFACE BLCKX SUBROUTINE BLCK_32(LEN,SRC) REAL SRC(1) !$PRAGMA IGNORE_TKR SRC INTEGER (KIND=4) LEN END SUBROUTINE SUBROUTINE BLCK_64(LEN,SRC) REAL SRC(1) !$PRAGMA IGNORE_TKR SRC INTEGER (KIND=8) LEN END SUBROUTINE END INTERFACE The subroutine call: INTEGER L REAL S(100) CALL BLCKX(L,S)
BLCKX の呼び出しによって、一般的なコンパイルでは BLCK_32 が呼び出され、-xtypemap=integer:64 を使用してコンパイルした場合は BLCK_64 が呼び出されます。S の実際の型は、どのルーチンを呼び出すかを定義しません。これによって、引数の型、種別、次元数に基づいてライブラリルーチンを呼び出すラッパーの一般的なインタフェースの記述を単純化できます。
形状引き継ぎの配列、Fortran ポインタ、割り当て可能な配列の仮引数は、指令では指定できません。名前が指定されていない場合は、形状引き継ぎの配列、Fortran ポインタ、割り当て可能な配列の仮引数を除いて、手続きのすべての仮引数に指令が適用されます。
UNROLL 指令では、!$PRAGMA のあとに SUN と指定する必要があります。
!$PRAGMA SUN UNROLL=n 指令は、最適化パス中に、次のループを n 回展開するようにコンパイラに指示します。コンパイラは、解析の結果、ループの展開が適切であると判断した場合のみ展開します。
n は正の整数です。次の選択が可能です。
n=1 の場合、オプティマイザは、どのループも展開しない可能性があります。
n>1 の場合、オプティマイザは、ループを n 回展開する可能性があります。
実際に展開されたループがあると、実行可能ファイルのサイズが大きくなります。詳細については、『Fortran プログラミングガイド 』のパフォーマンスと最適化に関する章を参照してください。
例: ループを 2 回展開するときは、次のように指定します。
!$PRAGMA SUN UNROLL=2
WEAK 指令は、以前に定義されているよりも低い優先順位で同じシンボルを定義します。この指令は主に、ライブラリを作成する場合にソースファイル中で使用されます。リンカーは弱いシンボルを解決できなくてもエラーメッセージを表示しません。
!$PRAGMA WEAK (name1 [=name2])
WEAK (name1) によって、name1 が優先順位の低いシンボルとして定義されます。この場合リンカーは、name1 の定義が見つけられなくてもエラーメッセージを出力しません。
WEAK (name1=name2) によって、name1 が優先順位の低いシンボルとして、また、name2 の別名として定義されます。
プログラムから呼び出された name1 が定義されていない場合、リンカーはライブラリの定義を使用します。ただし、プログラムで name1 の定義が行われている場合は、そのプログラムの定義が使用され、ライブラリ中にある name1 の優先順位が低い大域的な定義は使用されません。プログラムから name2 が直接呼び出されると、ライブラリの定義が使用されます。name2 の定義が重複すると、エラーが発生します。詳細は、Solaris の『リンカーとライブラリ』を参照してください。
OPT 指令では、!$PRAGMA のあとに SUN と指定する必要があります。
OPT 指令は副プログラムの最適化レベルを設定し、コンパイルコマンド行に指定されているレベルは上書きされます。指令は副プログラムの直前に指定する必要があり、その副プログラムだけに適用されます。例:
!$PRAGMA SUN OPT=2 SUBROUTINE smart(a,b,c,d,e) ...etc
上記の例を、-O4 を指定する f95 コマンドでコンパイルする場合、指令はこのレベルを上書きして -O2 でサブルーチンをコンパイルします。このルーチンのあとに別の指令がないかぎり、次の副プログラムは -O4 でコンパイルされます。
ルーチンを -xmaxopt[ =n] オプションでコンパイルして、指令が認識されるようにする必要があります。このコンパイラオプションは PRAGMA OPT 指令の最適化の最大値を指定します。PRAGMA OPT に指定した最適化レベルが -xmaxopt レベルよりも大きいと、-xmaxopt レベルが使用されます。
PIPELOOP=n 指令では、!$PRAGMA の後に SUN と指定する必要があります。
この指令は DO ループの直前に指定する必要があります。n には正の整定数かゼロを指定し、ループの反復間の依存関係をオプティマイザに指示します。ゼロの値は反復間の依存関係 (ループの伝達性) がないことを示し、オプティマイザで自由にパイプラインできます。正の値の n はループの I 番目の反復が (I-n) 番目の反復に依存していることを意味し、一度に n 反復だけパイプラインできます。(n が指定されない場合のデフォルトは 0 です)
C We know that the value of K is such that there can be no C cross-iteration dependencies (E.g. K>N) !$PRAGMA SUN PIPELOOP=0 DO I=1,N A(I)=A(I+K) + D(I) B(I)=B(I) + A(I) END DO
最適化についての詳細は、『Fortran プログラミングガイド』を参照してください。
-xprefetchオプションフラグを使用すると (「3.4.153 -xprefetch[ =a[,a]]」 を参照)、コンパイラに指示した一連の PREFETCH 指令は、先読みをサポートするプロセッサで指定のデータ要素について先読み命令を生成できます。
!$PRAGMA SUN_PREFETCH_READ_ONCE(name) !$PRAGMA SUN_PREFETCH_READ_MANY(name) !$PRAGMA SUN_PREFETCH_WRITE_ONCE(name) !$PRAGMA SUN_PREFETCH_WRITE_MANY(name)
先読み命令についての詳細は、『C ユーザーズガイド』または『SPARC Architecture Manual, Version 9』も参照してください。
ASSUME 指令は、プログラムの特定地点の条件についてコンパイラにヒントを与えます。これらの表明は、コンパイラへの最適化指示のガイドラインとして役立ちます。また、プログラマは、それらの指令を使用して、実行時にプログラムの妥当性をチェックできます。ASSUME のフォーマットは 2 種類あります。
「単一表明」ASSUME の構文は、次のようになります。
!$PRAGMA ASSUME (expression [,probability])
また、「範囲表明」ASSUME は、次のようになります。
!$PRAGMA BEGIN ASSUME [expression [, probability) block of statements !$PRAGMA END ASSUME
単一表明形式を使用すると、プログラムのその地点でコンパイラが想定できる条件を示すことができます。範囲表明形式を使用すると、ステートメントの範囲内を通して成立する条件を示すことができます。範囲表明の BEGIN と END のペアは正しくネストされる必要があります。
必要な式は、上にリストされている以外でユーザー定義の演算子や関数呼び出しを含まないプログラムの特定地点で評価可能なブール式です。
オプションの probability 値は、0.0 から 1.0 までの実数、つまり整数の 0 または 1 であり、式が真となる可能性を示します。0.0 (または 0) の可能性は絶対に真にならないことを意味し、1.0 (または 1) は常に真になることを意味します。数値の指定がない場合、式は高い可能性で真とみなされますが、絶対ではありません。0 または 1 以外の可能性を持つ表明は「非確定表明」です。同様に、0 または 1 の可能性を持つ表明は「確定表明」です。
たとえば、DO ループが常に 10,000 より長いことがわかっている場合は、コンパイラにこれを示しておくと、より良いコードを生成できます。通常、次のループは、ASSUME プラグマがある場合の方がすばやく実行されます。
!$PRAGMA BEGIN ASSUME(__tripcount().GE.10000,1) !! a big loop do i = j, n a(i) = a(j) + 1 end do !$PRAGMA END ASSUME
特に ASSUME 指令の式クローズで使用するために、2 つの組み込み関数が用意されています。それらの名前の前には、2 つの下線が配置されます。
|
この特殊な組み込み関数のリストは、将来的なリリースで拡大する可能性があります。
-xassume_control コンパイラオプションとともに使用します。(「3.4.105 -xassume_control[ =keywords]」を参照) 。たとえば、-xassume_control=check を使用してコンパイルした場合、トリップカウントが 10,000 を下回ると警告が発せられます。
-xassume_control=retrospective を使用してコンパイルを実行すると、プログラムの終了時点ですべての表明の真と偽を示す要約レポートが生成されます。-xassume_control の詳細については、f95 のマニュアルページを参照してください。
もう 1 つの例
!$PRAGMA ASSUME(__tripcount.GT.0,1) do i=n0, nx
-xassume_control=check を使用して前述の例をコンパイルすると、トリップカウントが 0 かマイナスになるため、そのループを使用しないよう実行時の警告が発せられます。
並列化 指令は、コンパイラに対して、指令のあとに続く DO ループまたはコードの範囲を並列化するように明示的に指示します。一般的な指令とは、構文が異なります。並列化指令は、-openmp を使用してコンパイルされる場合にのみ認識されます。Fortran の並列化についての詳細は、『OpenMP API ユーザーズガイド』および『Fortran プログラミングガイド』を参照してください。
Fortran コンパイラは、OpenMP 3.1 共有メモリー並列化モデルをサポートします。従来の Sun および Cray の並列化指令は現在推奨されていないため、使用すべきではありません。
Fortran コンパイラでは、並列プログラミングモデルとして OpenMP Fortran 共有メモリーマルチプロセッシング API を使用することをお勧めします。API は、OpenMP Architecture Review Board (http://www.openmp.org) によって仕様が定められています。
OpenMP 指令を使用可能にするには、コマンド行オプション -openmp を指定してコンパイルする必要があります (「3.4.145 -xopenmp[={ parallel|noopt|none}]」を参照)。
f95 で使用可能な OpenMP 指令についての詳細は、『OpenMP API ユーザーズガイド』を参照してください。
注 - 従来の Sun および Cray 形式の並列化指令は非推奨になりました。Open MP 並列化 API が推奨されます。従来の Sun/Cray 指令から OpenMP モデルへの移行方法については、『OpenMP API ユーザーズガイド』を参照してください。
!DIR$ IVDEP 指令は、ループ内で検出された一部またはすべての配列参照のループがもたらす依存関係を無視し、特にほかの方法では実行できないマイクロベクトル化、配布、ソフトウェアパイプラインなどのさまざまなループの最適化を実行するように、コンパイラに指示します。これは、依存関係が重要ではない、または依存関係が実際に発生しないことをユーザーが把握している状況で使用されます。
例:
DO I = 1, N A(V(I)) = A(V(I)) + C(I) END DO
このループでは、A(V(I)) へのいくつかのループがもたらす依存関係があります。V(I) には、インデックス A への複製値が含まれている可能性があり、ループの順序を変更すると、異なる結果になる可能性があります。ただし、V に固有の値のみ含まれていることがわかっている場合は、ループの順序を安全に変更でき、IVDEP 指令を使用して最適化を実行できます。
-xivdep コンパイラオプション (「3.4.126 -xivdep[ =p]」 を参照) を使用して、IVDEP 指令の解釈を無効にするか、指定することができます。
IVDEP 指令の従来の解釈の中には、後方へのループがもたらす依存関係がないことを表明するだけのものがあります。Fortran コンパイラのデフォルトは —xivdep=loop です。これは、IVDEP 指令で推測されるループがもたらす依存関係がないことを表明することを示します。
次に、後方および前方への依存関係の例を示します。
do i = 1, n ! BACKWARD LOOP-CARRIED DEPENDENCE ... = a(i-1) ! S1 a(i) = ... ! S2 end do
do i = 1, n ! FORWARD LOOP-CARRIED DEPENDENCE a(i) = ... ! S3 ... = a(i-1) ! S4 end do
最初のループは S2 から S1 への後方へのループがもたらす依存関係で、2 つ目のループには、S3 から S4 への前方へのループがもたらす依存関係があります。2 つ目のループには前方への依存関係しかないため、このループは安全に配布やマイクロベクトル化が行えますが、最初のループは行えません。
次に、デフォルト値 -xivdep=loop を指定した IVDEP の使用例を示します。
integer target a(n) integer, pointer :: p(:), q(:) !DIR$ IVDEP do i = 1, n p(i) = q(i) a(i) = a(i-1) end do
p(i) と q(i) 間、および p(i) と a(*) 間の推測される依存関係は無視されますが、a(i) と a(i-1) 間の明確な依存関係は無視されません。ループは 2 つのループに分割でき、その結果の p(i) = q(i) ループはマイクロベクトル化できます。
IVDEP 指令は、DO ループの直後に適用されます。この指令とループとの間にほかのコードを使用することはできません。!DIR$ IVDEP は、配列代入、FORALL、または WHERE 構文にも適用できます。特定のループに対して複数の指令が存在する場合 (IVDEP や UNROLL など)、コンパイラは可能な限りそれらすべての指令に従います。