Fortran の注釈の書式であるソースコード「指令」を使用して、特殊な最適化または並列化の選択に関する情報をコンパイラに渡すことができます。コンパイラ指令は、プラグマとも呼ばれます。コンパイラは、一連の一般指令および並列化指令を認識します。f95 も OpenMP 共有メモリーマルチプロセッシング指令を処理します。
f95 に固有の指令については、「4.8 指令」で説明します。f95 が認識可能なすべての指令については、表 C–1 を参照してください。
指令は Fortran 規格には含まれていません。
一般的な Fortran 指令は次のような書式で使用します。
C$PRAGMA keyword ( a [ , a ] … ) [ , keyword ( a [ , a ] … ) ] ,…
C$PRAGMA SUN keyword ( a [ , a ] … ) [ , keyword ( a [ , a ] … ) ] ,…
C$PRAGMA SPARC keyword ( a [ , a ] … ) [ , keyword ( a [ , a ] … ) ] ,…
変数 keyword は特定の指令を表します。追加の引数やサブオプションも指定できます。指令によっては、前述に示す SUN や SPARC といった追加のキーワードを指定する必要があります。
一般的な指令の構文は、次のとおりです。
1 カラム目は、注釈指示子の文字 c、C、!、* などです。
f95 の自由形式では、! は認識される唯一の注釈指示子 (!$PRAGMA) です。本章に示す例では、固定形式を想定しています。
次の 7 文字は空白文字を入れず $PRAGMA とします。大文字でも小文字でもかまいません。
自由形式ソースプログラムでは、! という注釈指示子文字を使用する指令は、行のどの位置にも記述できます。
制限事項は、次のとおりです。
Fortran テキストの場合と同様、最初の 8 文字のあとでは、空白は無視され、大文字と小文字は区別されません。
指令は注釈なので、行をまたがって継続することはできません。ただし、1 行で完結している C$PRAGMA 行を複数使うことができます。
注釈が前述の構文条件を満たしていると、コンパイラが認識できる指令が 1 つまたは複数含まれていることになります。前述の構文条件を満たしていない場合は、警告メッセージが出力されます。
C プリプロセッサの cpp は注釈行または指令行の中でマクロシンボル定義を展開します。Fortran プリプロセッサの fpp は注釈行の中でマクロの展開は行いませんが、fpp は正当な f95 の指令は認識し、指令キーワード外の制限付き置換は実行します。ただし、キーワード SUN が必要な指令には注意してください。cpp は、小文字の sun を事前定義した値で置き換えます。また、cpp マクロ SUN を定義すると、SUN 指令キーワードが干渉されます。一般的な規則では、次のようにソースが cpp または fpp で処理される場合、プラグマは大文字と小文字を混在させて指定します。
C$PRAGMA Sun UNROLL=3
Fortran のコンパイラは、次の一般的な指令を認識します。
表 2–2 一般的な Fortran 指令の要約
C 指令 |
C$PRAGMA C(list) 外部関数の名前リストを C 言語のルーチンとして宣言します。 |
IGNORE_TKR Directive |
C$PRAGMA IGNORE_TKR {name {, name} ...} コンパイラは、特定の呼び出しを解釈するとき、一般的な手続きのインタフェースで表示される仮引数名の型、種類、ランクを無視します。 |
UNROLL Directive |
C$PRAGMA SUN UNROLL=n コンパイラに、次のループは長さ n に展開できることを伝えます。 |
WEAK Directive |
C$PRAGMA WEAK(name[=name2]) name を弱いシンボル (weak symbol) または name2 の別名として宣言します。 |
OPT Directive |
C$PRAGMA SUN OPT=n 副プログラムの最適化レベルを n に設定します。 |
PIPELOOP Directive |
C$PRAGMA SUN PIPELOOP=n 次のループでは n 離れた反復間に依存関係があることを宣言します。 |
PREFETCH Directives |
C$PRAGMA SUN_PREFETCH_READ_ONCE(name )C$PRAGMA SUN_PREFETCH_READ_MANY(name )C$PRAGMA SUN_PREFETCH_WRITE_ONCE(name )C$PRAGMA SUN_PREFETCH_WRITE_MANY(name ) 名前の参照のために、先読み命令を生成するようにコンパイラに要求します。(-xprefetch オプションを指定する必要があります。このオプションはデフォルトで有効になっています。PREFETCH 指令は、—xprefetch=no でコンパイルし無効にします。ターゲットアーキテクチャーも PREFETCH 指令をサポートしている必要があり、コンパイラ最適化レベルは -xO2 より上である必要があります)。 |
ASSUME Directives |
C$PRAGMA [BEGIN} ASSUME (expression [,probability]) C$PRAGMA END ASSUME プログラム内の特定の個所において、コンパイラが真であると想定できる条件について表明を行います。 |
C() 指令は、その引数が外部関数であることを指定します。EXTERNAL 宣言と同義です。ただし、通常の外部名とは違って、Fortran コンパイラでは、これらの引数名に下線が付けられません。詳細は、『Fortranプログラミングガイド』の「C と Fortran のインタフェース」の章を参照してください。
特殊な関数の C() 指令は、各副プログラム中にある、その関数への最初の引用よりも前に現れなければなりません。
例: C で ABC と XYZ をコンパイルします。
EXTERNAL ABC, XYZ C$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 指令では、C$PRAGMA のあとに SUN と指定する必要があります。
C$PRAGMA SUN UNROLL=n 指令は、最適化パス中に、次のループを n 回展開するようにコンパイラに指示します。コンパイラは、解析の結果、ループの展開が適切であると判断した場合のみ展開します。
n は正の整数です。次の選択が可能です。
n=1 の場合、オプティマイザは、どのループも展開しない可能性があります。
n>1 の場合、オプティマイザは、ループを n 回展開する可能性があります。
実際に展開されたループがあると、実行可能ファイルのサイズが大きくなります。詳細については、『Fortran プログラミングガイド 』のパフォーマンスと最適化に関する章を参照してください。
例: ループを 2 回展開するときは、次のように指定します。
C$PRAGMA SUN UNROLL=2 |
WEAK 指令は、以前に定義されているよりも低い優先順位で同じシンボルを定義します。この指令は主に、ライブラリを作成する場合にソースファイル中で使用されます。リンカーは弱いシンボルを解決できなくてもエラーメッセージを表示しません。
C$PRAGMA WEAK (name1 [=name2]) |
WEAK (name1) によって、name1 が優先順位の低いシンボルとして定義されます。この場合リンカーは、name1 の定義が見つけられなくてもエラーメッセージを出力しません。
WEAK (name1=name2) によって、name1 が優先順位の低いシンボルとして、また、name2 の別名として定義されます。
プログラムから呼び出された name1 が定義されていない場合、リンカーはライブラリの定義を使用します。ただし、プログラムで name1 の定義が行われている場合は、そのプログラムの定義が使用され、ライブラリ中にある name1 の優先順位が低い大域的な定義は使用されません。プログラムから name2 が直接呼び出されると、ライブラリの定義が使用されます。name2 の定義が重複すると、エラーが発生します。詳細は、Solaris の『リンカーとライブラリ』を参照してください。
OPT 指令では、C$PRAGMA のあとに SUN と指定する必要があります。
OPT 指令は副プログラムの最適化レベルを設定し、コンパイルコマンド行に指定されているレベルは上書きされます。指令は副プログラムの直前に指定する必要があり、その副プログラムだけに適用されます。たとえば、次のようにします。
C$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 指令では、C$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) C$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.157 –xprefetch[= a[,a]]」を参照)、コンパイラに指示した一連の PREFETCH 指令は、先読みをサポートするプロセッサで指定のデータ要素について先読み命令を生成できます。
C$PRAGMA SUN_PREFETCH_READ_ONCE(name) C$PRAGMA SUN_PREFETCH_READ_MANY(name) C$PRAGMA SUN_PREFETCH_WRITE_ONCE(name) C$PRAGMA SUN_PREFETCH_WRITE_MANY(name) |
先読み命令についての詳細は、『C ユーザーズガイド』または『SPARC Architecture Manual, Version 9』も参照してください。
ASSUME 指令は、プログラムの特定地点の条件についてコンパイラにヒントを与えます。これらの表明は、コンパイラへの最適化指示のガイドラインとして役立ちます。また、プログラマは、それらの指令を使用して、実行時にプログラムの妥当性をチェックできます。ASSUME のフォーマットは 2 種類あります。
「単一表明」ASSUME の構文は、次のようになります。
C$PRAGMA ASSUME (expression [,probability]) |
また、「範囲表明」ASSUME は、次のようになります。
C$PRAGMA BEGIN ASSUME [expression [, probability) block of statements C$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 プラグマがある場合の方がすばやく実行されます。
C$PRAGMA BEGIN ASSUME(__tripcount().GE.10000,1) !! a big loop do i = j, n a(i) = a(j) + 1 end do C$PRAGMA END ASSUME |
特に ASSUME 指令の式クローズで使用するために、2 つの組み込み関数が用意されています。それらの名前の前には、2 つの下線が配置されます。
__branchexp() |
ブール制御式を持つ分岐ステートメントの直前に配置された単一表明で使用します。分岐ステートメントを制御するブール式と同じ結果を生成します。 |
__tripcount() |
指令の直後または指令に閉じ込められたループのトリップカウントを生成します。単一表明で使用する場合、指令直後のステートメントは DO の最初の行となる必要があります。範囲表明で使用する場合、もっとも外側の閉じたループに適用します。 |
この特殊な組み込み関数のリストは、将来的なリリースで拡大する可能性があります。
-xassume_control コンパイラオプションとともに使用します。(「3.4.111 –xassume_control[ =keywords]」を参照) 。たとえば、-xassume_control=check を使用してコンパイルした場合、トリップカウントが 10,000 を下回ると警告が発せられます。
-xassume_control=retrospective を使用してコンパイルを実行すると、プログラムの終了時点ですべての表明の真と偽を示す要約レポートが生成されます。-xassume_control の詳細については、f95 のマニュアルページを参照してください。
もう 1 つの例
C$PRAGMA ASSUME(__tripcount.GT.0,1) do i=n0, nx |
-xassume_control=check を使用して前述の例をコンパイルすると、トリップカウントが 0 かマイナスになるため、そのループを使用しないよう実行時の警告が発せられます。
並列化 指令は、コンパイラに対して、指令のあとに続く DO ループまたはコードの範囲を並列化するように明示的に指示します。一般的な指令とは、構文が異なります。並列化指令は、-openmp を使用してコンパイルされる場合にのみ認識されます。Fortran の並列化についての詳細は、『OpenMP API ユーザーズガイド』および『Fortran プログラミングガイド』を参照してください。
Fortran コンパイラは、OpenMP 3.0 共有メモリー並列化モデルをサポートします。従来の Sun および Cray の並列化指令は現在推奨されていないため、使用すべきではありません。
Fortran コンパイラでは、並列プログラミングモデルとして OpenMP Fortran 共有メモリーマルチプロセッシング API を使用することをお勧めします。API は、OpenMP Architecture Review Board (http://www.openmp.org) によって仕様が定められています。
OpenMP 指令を使用可能にするには、コマンド行オプション -openmp を指定してコンパイルする必要があります (「3.4.149 –xopenmp[={ parallel|noopt|none}]」を参照)。
f95 で使用可能な OpenMP 指令についての詳細は、『OpenMP API ユーザーズガイド』を参照してください。
従来の Sun および Cray 形式の並列化指令は非推奨になりました。Open MP 並列化 API が推奨されます。従来の Sun/Cray 指令から OpenMP モデルへの移行方法については、『OpenMP API ユーザーズガイド』を参照してください。