Oracle® Developer Studio 12.5: Fortran ユーザーズガイド

印刷ビューの終了

更新: 2016 年 6 月
 
 

2.3 ディレクティブ

Fortran の注釈の書式であるソースコードディレクティブを使用して、特殊な最適化または並列化の選択に関する情報をコンパイラに渡すことができます。コンパイラディレクティブはプラグマと呼ばれることもあります。コンパイラは、一般的なディレクティブと並列化ディレクティブ (OpenMP ディレクティブを含む) のセットを認識します。

f95 に固有の指令については、ディレクティブで説明します。f95 によって認識されるすべてのディレクティブについてのサマリーは、Fortran ディレクティブのサマリーを参照してください。


注 - 指令は Fortran 規格には含まれていません。

2.3.1 一般的なディレクティブ

一般的な Fortran 指令は次のような書式で使用します。

!$PRAGMA keyword ( a [ , a ] ) [ , keyword ( a [ , a ] ) ] ,

!$PRAGMA SUN keyword ( a [ , a ] … ) [ , keyword ( a [ , a ] … ) ] ,…

変数 keyword は特定の指令を表します。追加の引数やサブオプションも指定できます。指令によっては、前述に示す追加のキーワード SUN を指定する必要があります。

一般的な指令の構文は、次のとおりです。

  • 1 桁目は、注釈指示子の文字 cC!* などです。

  • 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  一般的な Fortran 指令のサマリー
C ディレクティブ
!$PRAGMA C(list)
外部関数の名前リストを C 言語のルーチンとして宣言します。
IGNORE_TKR ディレクティブ
!$PRAGMA IGNORE_TKR {name {, name} ...}
コンパイラは、特定の呼び出しを解釈するとき、一般的な手続きのインタフェースで表示される仮引数名の型、種類、ランクを無視します。
UNROLL ディレクティブ
!$PRAGMA SUN UNROLL=n
コンパイラに、次のループは長さ n に展開できることを伝えます。
WEAK ディレクティブ
!$PRAGMA WEAK(name[=name2])
name を弱いシンボル (weak symbol) または name2 の別名として宣言します。
OPT ディレクティブ
!$PRAGMA SUN OPT=n
副プログラムの最適化レベルを n に設定します。
PIPELOOP ディレクティブ
!$PRAGMA SUN PIPELOOP=n
次のループでは n 離れた反復間に依存関係があることを宣言します。
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)
名前の参照のために、先読み命令を生成するようにコンパイラに要求します。(-xprefetch オプションを指定する必要があります。このオプションはデフォルトで有効になっています。PREFETCH ディレクティブは、—xprefetch=no でコンパイルし無効にします。ターゲットアーキテクチャーも PREFETCH 指令をサポートしている必要があり、コンパイラ最適化レベルは -xO2 より上である必要があります)。
ASSUME ディレクティブ
!$PRAGMA [BEGIN} ASSUME (expression [,probability])
!$PRAGMA END ASSUME
プログラム内の特定の個所において、コンパイラが真であると想定できる条件について表明を行います。

2.3.1.1 C ディレクティブ

C() ディレクティブは、その引数が外部関数であることを指定します。 これは EXTERNAL 宣言と同義ですが、ただし通常の外部名とは異なり、Fortran コンパイラではこれらの引数名に下線が付けられません

特殊な関数の C() 指令は、各副プログラム中にある、その関数への最初の引用よりも前に現れなければなりません。

例: CABCXYZ をコンパイルします。

       EXTERNAL ABC, XYZ
!$PRAGMA C(ABC, XYZ)

2.3.1.2 IGNORE_TKR 指令

このディレクティブでは、コンパイラは、特定の呼び出しを解釈するとき、総称手続きのインタフェースで表示される仮引数名の型、種別、次元数を無視します。

たとえば、次の手続きのインタフェースでは、SRC はどのようなデータ型でもよく、LENKIND=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 ポインタ、割り当て可能な配列の仮引数を除いて、手続きのすべての仮引数に指令が適用されます。

2.3.1.3 UNROLL ディレクティブ

UNROLL ディレクティブでは、!$PRAGMA のあとに SUN と指定する必要があります。

!$PRAGMA SUN UNROLL=n ディレクティブは、最適化パス中に、次のループを n 回展開するようにコンパイラに指示します。コンパイラは、解析の結果、ループの展開が適切であると判断した場合のみ展開します。

n は正の整数です。次の選択が可能です。

  • n=1 の場合、オプティマイザは、どのループも展開しない可能性があります

  • n>1 の場合、オプティマイザはループを n 回展開する可能性があります

実際に展開されたループがあると、実行可能ファイルのサイズが大きくなります。

例: ループを 2 回展開するときは、次のように指定します。

!$PRAGMA SUN UNROLL=2

2.3.1.4 WEAK ディレクティブ

WEAK ディレクティブは、以前に定義されているよりも低い優先順位で同じシンボルを定義します。この指令は主に、ライブラリを作成する場合にソースファイル中で使用されます。リンカーは弱いシンボルを解決できなくてもエラーメッセージを表示しません。

!$PRAGMA WEAK (name1 [=name2])

WEAK (name1) によって、name1 が優先順位の低いシンボルとして定義されます。リンカーは、name1 の定義が見つけられなくてもエラーメッセージを出力しません。

WEAK (name1=name2) によって、name1 が優先順位の低いシンボルとして、また、name2 の別名として定義されます。

プログラムから呼び出された name1 が定義されていない場合、リンカーはライブラリの定義を使用します。ただし、プログラムで name1 の定義が行われている場合は、そのプログラムの定義が使用され、ライブラリ中にある name1 の優先順位が低い大域的な定義は使用されません。プログラムから name2 が直接呼び出されると、ライブラリの定義が使用されます。name2 の定義が重複すると、エラーが発生します。詳細は、Oracle Solaris 11.3 リンカーとライブラリガイドを参照してください。

2.3.1.5 OPT ディレクティブ

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 レベルが使用されます。

2.3.1.6 PIPELOOP[= n] ディレクティブ

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

2.3.1.7 PREFETCH ディレクティブ

-xprefetch オプションフラグを使用すると (–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)

先読み命令についての詳細は、Oracle Developer Studio 12.5: C ユーザーズガイドまたは『SPARC Architecture Manual, Version 9』も参照してください。

2.3.1.8 ASSUME ディレクティブ

ASSUME ディレクティブは、プログラムの特定地点の条件についてコンパイラにヒントを与えます。これらの表明は、コンパイラへの最適化指示のガイドラインとして役立ちます。また、プログラマは、それらの指令を使用して、実行時にプログラムの妥当性をチェックできます。ASSUME のフォーマットは 2 種類あります。

「単一表明」ASSUME の構文は、次のようになります。

!$PRAGMA ASSUME (expression [,probability])

また、「範囲表明」ASSUME は、次のようになります。

!$PRAGMA BEGIN ASSUME [expression [, probability)
     block of statements
!$PRAGMA END ASSUME

単一表明形式を使用すると、プログラムのその地点でコンパイラが想定できる条件を示すことができます。範囲表明形式を使用すると、ステートメントの範囲内を通して成立する条件を示すことができます。範囲表明の BEGINEND のペアは正しくネストされる必要があります。

必要なは、上にリストされている以外でユーザー定義の演算子や関数呼び出しを含まないプログラムの特定地点で評価可能なブール式です。

オプションの 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 つの下線が配置されます。

__branchexp()
ブール制御式を持つ分岐ステートメントの直前に配置された単一表明で使用します。分岐ステートメントを制御するブール式と同じ結果を生成します。
__tripcount()
指令の直後または指令に閉じ込められたループのトリップカウントを生成します。単一表明で使用する場合、指令直後のステートメントは DO の最初の行となる必要があります。範囲表明で使用する場合、もっとも外側の閉じたループに適用します。

この特殊な組み込み関数のリストは、将来的なリリースで拡大する可能性があります。

-xassume_control コンパイラオプションとともに使用します。(–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 かマイナスになるため、そのループを使用しないよう実行時の警告が発せられます。

2.3.2 並列化ディレクティブ

OpenMP 並列化ディレクティブは、-openmp を使用してコンパイルされる場合にのみ認識されます。OpenMP 並列化に関する詳細は、Oracle Developer Studio 12.5: OpenMP API ユーザーズガイドから見つけることができます。

Fortran コンパイラは、共有メモリー並列化用の OpenMP API の Version 4.0 をサポートしています。従来の Sun および Cray の並列化指令は現在推奨されていないため、使用すべきではありません。

2.3.2.1 OpenMP 並列化ディレクティブ

Fortran コンパイラは、共有メモリー並列化用の OpenMP API を、優先される並列プログラミングモデルとして認識します。API は、OpenMP Architecture Review Board (http://www.openmp.orghttp://www.openmp.org) によって仕様が定められています。

OpenMP ディレクティブを使用可能にするには、コマンド行オプション -openmp を指定してコンパイルする必要があります (–xopenmp[={parallel|noopt|none}]を参照)。

f95 で使用可能な OpenMP ディレクティブについての詳細は、Oracle Developer Studio 12.5: OpenMP API ユーザーズガイドを参照してください。

2.3.2.2 従来の Sun および Cray 並列指令


注 -  従来の Sun および Cray 形式の並列化指令は非推奨になりました。Open MP 並列化 API が推奨されます。

2.3.3 IVDEP ディレクティブ

!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 コンパイラオプション (–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 構文にも適用できます。特定のループに対して複数のディレクティブが存在する場合 (IVDEPUNROLL など)、コンパイラは可能なかぎりそれらすべてのディレクティブに従います。