Fortran の明示的なベクトル・プログラミング

インテル® Fortran コンパイラーインテル® Parallel Studio XE

この記事は、インテル® デベロッパー・ゾーンに掲載されている「Explicit Vector Programming in Fortran」の日本語参考訳です。


ムーアの法則により、周波数とスカラー・アプリケーションのパフォーマンスが向上する時代は終わりました。代わりに、トランジスター数を増加することで、より多くのコアとよりワイドな SIMD レジスターの両方を通じて並列性を高めます。プロセッサー・テクノロジーの向上を最大限に利用してパフォーマンスを高めるには、アプリケーションは両方の並列性を活用する必要があります。この記事では、SIMD 並列処理に注目します。マルチコア並列処理についてはほかで取り上げられており、OpenMP* や MPI は Fortran アプリケーションで広く使用されています。

インテル® マス・カーネル・ライブラリー (インテル® MKL) など、最適化されたライブラリー関数は、Fortran 開発者がインテル® アーキテクチャー上で SIMD 並列処理を利用する簡単で効果的な方法です。ライブラリー関数が利用できない場合は、コンパイラーによる自動ベクトル化を利用できます。C/C++ 開発者向けの SIMD 組込み関数には、Fortran 用のインターフェイスがなく、使用するには多大な労力を必要とし、アーキテクチャーへの依存性が生じます。

自動ベクトル化には制限があります。コンパイラーは保守的でなければならず、可能性の低い入力データ値であっても、最適化されていないコードと異なる結果を生成する可能性がある最適化は行いません。また、コード変換によってコードが高速になるか低速になるかを推定する必要があります。言語標準には、プログラマーの意図を示したり、コンパイラーがより効果的な決定を下すのに必要なすべての情報を提供する手段がないため、通常、これらの決定は不完全な情報に基づいて下されます。自動ベクトル化の要件については、こちらで説明されています。

コンパイラー・ディレクティブなどのヒントをコンパイラーに提供することで、自動ベクトル化はより効果的になりますが、ループがベクトル化されるかどうかはコンパイラーの内部解析と利用可能な情報に依存します。明示的なベクトル・プログラミングは、その不確実性を取り除きます。プログラマーは、アプリケーションに関する知識を使用して、コンパイラーにループのベクトル化を指示することができます。これは、ディレクティブによりループのスレッド化をコンパイラーに指示する、OpenMP* を使用したスレッド化に似ています。OpenMP* と同様に、プログラマーは競合状態を回避するため、リダクション変数とプライベート変数をコンパイラーに知らせる必要があります。

OpenMP* 4.0 (英語) 以降では、ディレクティブは明示的なベクトル化をサポートする SIMD 構文として利用できます。インテル® C++ および Fortran コンパイラーは、バージョン 15 から OpenMP* SIMD 構文をサポートしています。

インテル® コンパイラーで OpenMP* の SIMD 機能を有効にするには、/Qopenmp または /Qopenmp-simd (Windows*) あるいは -qopenmp または -qopenmp-simd (Linux* または macOS*) を指定してアプリケーションをコンパイルします。インテル® コンパイラー 19 以降では、このコンパイラー・オプションはデフォルトの -O2 に含まれます。アプリケーションが OpenMP* のスレッド API を使用せず、SIMD 構文のみを使用する場合は、/Qopenmp-simd (Windows*) または -qopenmp-simd (Linux* または macOS*) の使用を推奨します。OpenMP* の SIMD 機能を無効にするには、/Qsimd- (Windows*) または -qno-openmp-simd (Linux* または macOS*) を使用します。

Fortran は大文字と小文字を区別しませんが、以下の例では分かりやすくするため、C の変数は小文字で、Fortran の変数は大文字で示します。

配列表記
インテル® C/C++ コンパイラーでは、配列への代入は次の形式で記述されます。

a[0:n] = a[m:n] + b[0:n] * c[n:n]

同等の Fortran の配列への代入は、次のように記述されます。下限は 0 の代わりに 1 となります。

A(1:n) = A(1+m:n+m) + B(1:n) * C(n+1:n+n)

C/C++ と Fortran では、 2 つの重要な違いがあります。

  1. C では、コロンに続く数値は代入するデータ要素の数を指定します。当然、これはすべての配列またはポインターで同じでなければなりません。Fortran では、コロンに続く数値は代入される配列セクションの上限を指定します。つまり、C の x[j:k] は Fortran の X(j:j+k-1) と同じ配列セクションを表します。
  2. C では、配列構文は、右辺のデータ要素と左辺のデータ要素の間に安全ではないベクトル化につながる後方依存関係がないことをコンパイラーに示します。上記の例では、m³0 を暗示する後方依存関係はありません。また、A(1:n) と B(1:n) または C(n+1:n+n) の間に、スカラーループで B または C のオーバーラップする要素が読み取られる前に A の要素が書き込まれるようなオーバーラップはありません。このアサーションが正しくない場合、ユーザーエラーと見なされ、結果は不定です。Fortran ではそのようなアサーションはありません。コンパイラーは、LHS への代入を開始する前に右辺 (RHS) のすべての要素のコピーを一時的に格納する必要があります。この一時配列への格納は、大きなパフォーマンス・オーバーヘッドをもたらす可能性がありますが、ほかの方法ではベクトル化できないループのベクトル化を可能にします。Fortran 配列表記のセマンティクスは、Fortran 77 形式の DO ループのセマンティクスとは異なります。
  3. DO i=1,n
    	A(i) = A(i+m) + B(i) * C(n+i)
    ENDDO
    

    このコードは、m=-1 の場合、前述の配列代入とは異なる結果を生成します (A(0) と B(0) は有効なアドレスであると仮定)。DO ループでは、i=2 の場合、RHS で使用される A(1) の値は前の反復ですでに変更されています。配列表記バージョンでは、代わりに A(1) のオリジナルの値が使用されます。DO ループは安全にベクトル化できませんが、配列表記はできます。

DO ループのように、IVDEP などのコンパイラー・ディレクティブを配列への代入に適用することも可能です。IVDEP ディレクティブは、ベクトル化が安全ではない潜在的な依存関係 (データ依存関係) がないことをコンパイラーが仮定できるようにしますが、一時コピーを作成するセマンティクスは変更しません。また、実証された依存関係はオーバーライドしません。

ループがベクトル化されたかどうか、されなかった場合はその理由を説明した最適化レポートを取得するには、-qopt-report-phase=vec (Linux* または macOS*) または /Qopt-report-phase=vec (Windows*) オプションを使用します。-qopt-report=3、4、または 5 (Linux* または macOS*) または /Qopt-report:3、4、または 5 (Windows*) では追加の詳細が得られます。デフォルトでは、レポートは拡張子が .optrpt のファイルに出力されます。短いレポートは、-qopt-report-file=stderr (Linux* または macOS*) または /Qopt-report-file:stderr (Windows*) オプションを使用して、標準エラー出力にリダイレクトすると便利です。

同様の配列境界を持つ連続する配列代入は、ループのオーバーヘッドや中間結果のメモリートラフィックを軽減するため、単一のループに融合される場合がります。その場合、-qopt-report-phase=loop,vec (Linux* または macOS*) または /Qopt-report-phase:loop,vec (Windows*) で出力される最適化レポートの ‘loop’ フェーズにメッセージが表示されます。

プロシージャー呼び出しを含むループ
プロシージャー呼び出し (サブルーチンまたは関数呼び出し) を含むループは、関数がインライン展開されているか、関数のベクトルバージョンが利用可能な場合を除き、一般にベクトル化できません。小さなプロシージャーへの少数の呼び出しは、ソースコードの変更をほとんどまたは全く必要とせず、プロシージャー呼び出しのオーバーヘッドを排除できる、インライン展開にすることを推奨します。

最適化レベル -O2 以上では、同じソースファイル内のインライン展開がデフォルトで有効になります。複数のソースファイル間のインライン展開は、プロシージャー間の最適化 -ipo (Linux* または macOS*) または /Qipo (Windows*) によって有効になります。デフォルトでは、プロシージャー呼び出しをインライン展開するかどうかは、サイズやその他のヒューリスティックに基づいてコンパイラーが決定します。

ベクトル化を有効にするため確実にインライン展開するには、1 つ以上のプロシージャー呼び出しを含む文の直前に !DIR$ FORCEINLINE ディレクティブを追加します。または、DO ループの前に追加して、ループ内のすべてのプロシージャー呼び出しをインライン展開します。例は、コンパイラーのユーザーガイドを参照してください。

-qopt-report-phase=ipo (Linux* または macOS*) または /Qopt-report-phase=ipo (Windows*) コンパイラー・オプションで最適化レポートを出力して、インライン展開された関数を確認できます。

SIMD 対応プロシージャー (関数とサブルーチン)
多数のプロシージャー呼び出しや大規模なプロシージャーの呼び出しを含むループでは、複雑さ、コードサイズ、コンパイル時間などの理由から、インライン展開できない場合があります。そのようなプロシージャーを SIMD 対応にすると、ループがベクトル化可能になり、プログラマーがベクトル化を細かく制御できるようになります。SIMD 対応プロシージャーに対して、コンパイラーはスカラーバージョンと 1 つ以上のベクトルバージョンの両方を生成します。プロシージャーの SIMD 属性は、呼び出し元と呼び出し先の両方で宣言する必要があります。これを行う最良の方法は、明示的なインターフェイスを使用することです。以下に例を示します。

subroutine  get_proj (rad, theta, proj) 
!$omp declare simd(get_proj)
  real, intent(in)  :: rad, theta 
  real, intent(out) :: proj
  proj = rad*cos(theta) 
end subroutine  get_proj

real, dimension(N) :: p,r,t
interface
  subroutine  get_proj (rad, theta, proj) 
!$omp declare simd(get_proj)
    real, intent(in)  :: rad, theta 
    real, intent(out) :: proj
  end subroutine  get_proj
end interface
…
do i=1,N                                !  or instead of DO loop, make get_proj an elemental 
    call get_proj( r(i), t(i), p(i) )   !  subroutine and use array notation to write   
enddo                                   !  call get_proj( r(:), t(:), p(:) )

インテル® Fortran コンパイラー 15.0 以降では、SIMD 対応プロシージャーをモジュール内に組込むだけで、モジュールを使用するすべての呼び出しルーチンがインターフェイスにアクセスして、関数の SIMD 対応バージョンを利用できます。

OMP DECLARE SIMD または ATTRIBUTES VECTOR ディレクティブは、通常のスカラーバージョンに加えて、ベクトル入力引数 rad と theta を受け取り、ベクトル出力引数 proj を返す、サブルーチン get_proj の SIMD バージョンを作成するようにコンパイラーに指示します。ベクトル長はコンパイラーによって選択され、ターゲット・マイクロアーキテクチャーに依存する場合がありますが、追加の節を使用してプログラマーが指定することもできます。呼び出し元のプロシージャーにはインターフェイス・ブロックが含まれているため、コンパイラーはサブルーチンの SIMD バージョンが利用できることを認識して、サブルーチン呼び出しを含むループをベクトル化できます。これは、コンパイラーが Short Vector Math Library (libsvml) の SIMD 対応関数を使用して、数学関数への直接呼出しを含むループをベクトル化する方法によく似ています。この例の単純なプロシージャーは、簡単にインライン展開できます。SIMD 対応プロシージャーの価値は、異なるソースファイルの複数のプロシージャーの呼び出しを含む、より複雑な例において発揮されます。

デフォルトでは、コンパイラーは SIMD 対応プロシージャーのすべての引数がベクトルであると仮定します。引数が常にスカラーであることが判明している場合は、UNIFORM 節を使用して宣言すべきです。これにより、コンパイラーは UNIFORM 属性の引数のベクトルコードを生成せず、代わりにスカラー値を各ベクトルレーンにブロードキャストします。以下に例を示します。

module mymod
contains
  subroutine  get_proj (rad, theta, proj) 
!$omp declare simd(get_proj)  uniform(rad)
    real, intent(in)  :: rad, theta 
    real, intent(out) :: proj
    proj = rad*cos(theta) 
  end subroutine  get_proj
end module mymod

subroutine caller
  use mymod
  real, dimension(N) :: p,t
  real,   :: r
…
do i=1,N                                !  or instead of DO loop, make get_proj an elemental 
    call get_proj( r, t(i), p(i) )      !  subroutine and use array notation to write 
enddo                                   !  call get_proj( r, t(:), p(:) )

サブルーチン自体のソースコードに変更はありません。サブルーチンの属性のみが異なります。

配列引数を持つ SIMD 対応プロシージャーの呼び出し
個々の配列要素を引数としてループ内からSIMD 対応関数を呼び出す代わりに、配列全体または配列セクションを引数として SIMD 関数を直接呼び出すことができます。そのためには、SIMD 対応関数を ELEMENTAL として宣言する必要があります (ELEMENTAL は Fortran 標準キーワードです)。SIMD 対応関数は自動的に ELEMENTAL ではなく、ELEMENTAL 関数は自動的に SIMD 対応ではない必要があります。以下に例を示します。

module mymod 
contains 
  elemental subroutine  get_proj (rad, theta, proj)  
!$omp declare simd(get_proj) uniform(rad)   
    real, intent(in)  :: rad, theta  
    real, intent(out) :: proj 
    proj = rad*cos(theta)  
  end subroutine  get_proj 
end module mymod 
… 
subroutine caller 
use mymod 
real, dimension(N) :: p,t 
real :: r 
… 
call get_proj( r, t(:), p(:) )  

配列の半分のみを渡す場合は、次のようになります。

call get_proj( r, t(1:N/2), p(1:N/2) )

SIMD 対応プロシージャーの効率に関する考察
C では、スカラー引数はデフォルトで値渡しです。SIMD 対応プロシージャーでは、引数は値のショートベクトルとして渡されます。Fortran のデフォルトの呼び出し規約では、スカラー引数は参照渡しです。SIMD 対応プロシージャーの簡単な拡張は、単一のアドレスの代わりに、アドレスのショートベクトルを渡します。コンパイラーは、アドレスのベクトルからデータを “ギャザー” して、後続のベクトル演算で使用する値のショートベクトルを作成します。このギャザーによるオーバーヘッドは、簡単な Fortran SIMD 対応プロシージャーでは、パフォーマンスの利点を得るため、C の同様の SIMD 対応関数よりも多くのワークが必要なことを意味します。このオーバーヘッドは、入力引数を値渡しにすることで軽減できます。以下に例を示します。

subroutine  get_proj (rad, theta, proj)  bind(C)
!$omp declare simd(get_proj) uniform(rad)   
  real, intent(in), value  :: rad, theta 
  real, intent(out) :: proj
  proj = rad*cos(theta) 
end subroutine  get_proj

VALUE キーワードだけでは十分ではありません。BIND(C) とともに指定します。この 2 つのキーワードの代わりに、ディレクティブを使用することも可能です。

$DIR$ ATTRIBUTES VALUE :: <em>argname</em>

Fortran 言語標準であるキーワードの使用を推奨します。

Fortran 言語標準では、INTENT(OUT) または INTENT(INOUT) 引数は参照渡しする必要があるため、この手法は出力引数には適用できません。しかし、単一のベクトル出力引数を持つ SIMD 対応サブルーチンは、ベクトル結果を持つ SIMD 対応関数に変換できます。ベクトル結果は、呼び出し元のプロシージャーに値として返され、ギャザーのオーバーヘッドを回避します。以下に例を示します。

module mymod
contains  
  real function proj (rad, theta) bind(C)
!$omp declare simd(proj) uniform(rad) 
    real, intent(in), value  :: rad, theta 
    proj = rad*cos(theta) 
  end function proj
end module mymod
…
use mymod
real, dimension(N) :: p, t
real               :: r
…
do i=1,N                           !  or instead of DO loop, make proj an elemental function   
    p(i) = proj( r, t(i) )         !  and use array notation to write
enddo                              !  p(:) = proj( r, t(:) )

INTENT(OUT) または INTENT(INOUT) が指定された追加のベクトル引数は、通常の参照渡しにする必要があります。

LINEAR(REF()) 節
LINEAR 節の REF 修飾子は OpenMP* 4.5 で追加されました。インテル® Fortran コンパイラー 16 Update 1 (16.0.1) 以降でサポートされています。参照渡しのベクトル引数に適用すると、ベクトル引数に格納されているアドレスは連続しているため、ギャザー/スキャッターの代わりに単一のロード/ストアを使用できることをコンパイラーに知らせます。これにより、少ないソース変更で効率良い SIMD コードを生成できます。この新しい機能を使用すると、オリジナルの例は次のようになります。

module mymod
contains  
  subroutine  get_proj (rad, theta, proj)
!$omp declare simd(get_proj) uniform(rad)  linear(ref(theta,proj))
    real, intent(in)  :: rad, theta 
    real, intent(out) :: proj
    proj = rad*cos(theta) 
  end subroutine  get_proj
end module mymod

SIMD ディレクティブ
OpenMP* 4.0 の !$OMP SIMD ディレクティブと同等のインテル® コンパイラーの !DIR$ SIMD は、後に続くループのベクトルコードを生成するようにコンパイラーに指示します。IVDEP や VECTOR ALWAYS などのほかのコンパイラー・ディレクティブとは異なり、SIMD ディレクティブはコンパイラーへヒントではなく、コマンドです。コンパイラーは、ループの依存関係を解析したり、ベクトル化がスピードアップをもたらすか推定せずに、ベクトル化します。安全なベクトル化を妨げる依存関係がないことを保証し、ベクトル化によってパフォーマンスが向上するかどうかを判断するのは、プログラマーの責任です。SIMD ディレクティブの動作は、!$OMP DO ディレクティブに似ています。!$OMP DO ディレクティブは、後に続く DO ループのスレッド化されたコードの生成をコンパイラーに指示します。スレッド化が安全であることを保証し、スレッド化の利点が得られるように十分な並列ワークを提供する責任はプログラマーが負います。

コンパイラーは、確実にベクトル化するため、スカラー呼び出しによるベクトル関数のエミュレーションなど、さまざまなことを試します。しかし、OpenMP* を使用したスレッド化では、SIMD ループは、ループの開始時に反復回数が判明していなければならないなど、いくつかの基本ルールに従う必要があります。詳細は、「#pragma SIMD を使用してループをベクトル化するための条件」を参照してください。

以下の例は、SIMD ディレクティブを使用してループのベクトル化を強制する方法を示します。

module mymod
  real, pointer, dimension(:) :: a, b, c, d, e
end module mymod

subroutine add4
  use mymod
  implicit none
  integer :: i

!$omp simd  
  do i=1,1000
     e(i) = a(i) + b(i) + c(i) + d(i)
  enddo

end subroutine add4

> ifort add4.f90 -c -qopt-report-phase=vec,loop  -qopt-report-file=stderr

…

LOOP BEGIN at add4.f90(12,34)
<Multiversioned v1>
remark #15344: loop was not vectorized: vector dependence prevents vectorization. First dependence is shown below. Use level 5 report for details
remark #15346: vector dependence: assumed FLOW dependence between  line 12 and  line 12
remark #25438: unrolled without remainder by 4
LOOP END … 

ヒントなしでは、コンパイラーは e の一部が a、b、c、d のいずれかのポインターでエイリアスされる (つまり、オーバーラップするメモリー位置をポイントする) 可能性があるかどうか判断できないため、自動ベクトル化しません。実行時にテストするには、可能性が多すぎます。IVDEP コンパイラー・ディレクティブは、実際には依存関係が存在せず、e は a、b、c、d から独立しており、ループを安全にベクトル化できることを示します。!DIR$ IVDEP を追加して再コンパイルすると、「LOOP WAS VECTORIZED (ループはベクトル化されました。)」と「vectorization possible but seems inefficient (ベクトル化は可能ですが非効率です。)」という 2 つのメッセージが表示されます。オリジナルのメッセージ「 (<マルチバージョン v1>)」がヒントになります。-qopt-report-phase=vec,loop を指定して、さらに詳しい最適化レポートを生成すると、以下のメッセージが表示されます。

remark #25233: Loop multiversioned for stride tests on Assumed shape arrays

コンパイラーは、ポインターがメモリーの連続するチャンクを指しているのか、非ユニットストライドの配列セクションを指しているのか判断できません。ケースごとに個別のコードを生成し、連続するメモリー(ユニットストライド) の場合はベクトル化する価値があると判断しますが、そうでない場合は価値がないと判断します。!DIR$ VECTOR ALWAYS コンパイラー・ディレクティブを追加することで、コンパイラーによるスピードアップできるかどうかの判断に関係なく、各ケースをベクトル化するようにコンパイラーに促します。これにより、両方のループバージョンがベクトル化されます。

インテル® コンパイラー 15 で最適化レポートが強化され、より詳細な情報が提供されるようになりました。「ピールループ」や「リマインダー・ループ」などの用語を含む詳しい説明は、https://software.intel.com/en-us/videos/getting-the-most-out-of-the-intel-compiler-with-new-optimization-reports または Parallel Universe 19 号の記事を参照してください。

ベクトル化を確実にする簡単な方法は、/Qopenmp-simd (Windows*) または -qopenmp-simd (Linux* または macOS*) で有効になる、単一の !$OMP SIMD ディレクティブを使用してコンパイルすることです。これにより、依存関係やパフォーマンスを考慮せずに、ループをベクトル化するようにコンパイラーに (促すのではなく) 指示します。コンパイルすると、両方のループバージョンについて、次のメッセージが表示されます。

remark #15301: SIMD LOOP WAS VECTORIZED

SIMD ディレクティブは、ポインターのターゲットがユニットストライドかどうかをコンパイラーに知らせないため、2 つのループバージョンが生成されます。ターゲットが常にユニットストライドであることが判明している場合、ポインター宣言に CONTIGUOUS キーワードを追加してコンパイラーに知らせると、ストライド 1 (ユニットストライド) のループバージョンのみが生成されます。

SIMD ディレクティブは、コンパイラーによるすべての依存関係チェックを無効にする可能性があります。実際の依存関係が存在する場合に使用すると、予期しない結果や不具合が生じる可能性があります。

SIMD ディレクティブは、上記の例に示すように、IVDEP と VECTOR ALWAYS の両方の機能を備えており、より強力です。次の例では、IVDEP と VECTOR ALWAYS だけではループをベクトル化できません。

real function vec_sum(x,nx)
  implicit none
  integer, intent(in)                :: nx
  real,    intent(in), dimension(nx) :: x
  integer                            :: i

  interface
    real function func(x)
      !$OMP DECLARE SIMD (func)   
      real, intent(in) :: x
    end function func
  end interface
      
  vec_sum = 0.      
! !$OMP SIMD reduction(+:vec_sum)  
  do i = 1,nx
     vec_sum = vec_sum + func(x(i))
  enddo

end function vec_sum

自動ベクトル化は、リダクション・ループとベクトル関数呼び出しの組み合わせを認識してベクトル化することはできませんが、それぞれを個別に自動ベクトル化することはできます。

> ifort -c -qopenmp-simd -qopt-report-phase=vec -qopt-report-file=stderr vec_sum.f90

vec_sum.f90(17): (col. 3) remark: loop was not vectorized: existence of vector dependence.

IVDEP ディレクティブを指定しても、コンパイラーに自動ベクトル化させることはできません。しかし、SIMD ディレクティブのコメントを外すと、コンパイラーは指示したとおりに動作します。

> ifort -c -qopenmp-simd -qopt-report-phase=vec -qopt-report-file=stderr vec_sum.f90
…
LOOP BEGIN at vec_sum.f90(17,3)
    remark #15301: SIMD LOOP WAS VECTORIZED
LOOP END

この例は、SIMD ディレクティブでの REDUCTION 節の使用法も示しています。!$OMP DO などの OpenMP* ワークシェア構造と同様に、同じリダクション変数 (上記の例では vec_sum) に書き込む異なる反復間の競合を回避するには、この節を使用する必要があります。

PRIVATE 節も 競合状態を回避するため、OpenMP* と同様に動作します。例えば、上記のループをわずかに変更して x 上の func の積分にすると、PRIVATE 節が必要になります。

!$omp simd reduction(+:vec_sum) private(xi)
  do i = 1,nx
     xi = x0 + bin_width*(i)
     vec_sum = vec_sum + func(xi)
  enddo

SIMD ディレクティブは、LINEAR 節と ALIGNED 節もサポートしています。LINEAR 節は、ループに 1 つ以上の 2 次誘導変数が含まれていることをコンパイラ―に知らせます。ALIGNED 節は、1 つ以上の配列がメモリー内で指定された境界でアライメントされていることをコンパイラ―に知らせます。

ALIGNED(X:64) は、配列 X の開始が常にメモリー内で 64 バイト境界でアライメントされており、アライメントされたロード命令を安全に生成できることをコンパイラ―に知らせます。配列をアライメントするのはプログラマーの責任です。多くの場合、-align array64byte コマンドライン・オプションでアライメントすることができます。!$OMP SIMD および !DIR$ SIMD ディレクティブでサポートされる節の詳細は、『インテル® Fortran コンパイラ―・デベロッパー・ガイドおよびリファレンス』 (英語) の「SIMD ディレクティブ (OpenMP* API)」と「SIMD ループ・ディレクティブ」を参照してください。

前述の条件を満たさないため !$OMP SIMD または !DIR$ SIMD ディレクティブの後に続くループをベクトル化できない場合、コンパイラーは警告メッセージを出力します。

remark #13379: loop was not vectorized with "simd".

!DIR$ SIMD のみ: ディレクティブに ASSERT 節が追加されている場合、これは致命的なエラーになります。これは、将来の変更が意図せずループのベクトル化を妨げないように、プログラマーに注意を促すのに役立ちます。

-no-simd オプションを指定してアプリケーションをコンパイルすると、!DIR$ SIMD ディレクティブは無視されます。これは比較テストに役立ちます。アプリケーションが -qopenmp または -qopenmp-simd オプションを指定してビルドされていない限り、!$OMP SIMD ディレクティブはデフォルトで無視されます。

インテル® C/C++ コンパイラーの明示的なベクトル・プログラミングの関連情報

コンパイラーの最適化に関する詳細は、最適化に関する注意事項を参照してください。

タイトルとURLをコピーしました