インテル® Fortran コンパイラー 18.0 デベロッパー・ガイドおよびリファレンス

ユーザー指示または SIMD ベクトル化

ユーザー指示または SIMD ベクトル化は、OpenMP* 並列化が自動並列化を補足するように、自動ベクトル化を補足します。下記の図でこの関係を示します。ユーザー指示によるベクトル化は SIMD (Single-Instruction, Multiple-Data) 機能として実装され、SIMD ベクトル化と呼ばれます。

SIMD ベクトル化機能は、インテル製マイクロプロセッサーおよび互換マイクロプロセッサーの両方で利用可能です。ベクトル化により呼び出されるライブラリー・ルーチンは、互換マイクロプロセッサーよりもインテル製マイクロプロセッサーにおいてより優れたパフォーマンスが得られる可能性があります。また、ベクトル化は、/arch (Windows*)、-m (Linux* および macOS*)、[Q]x などの特定のオプションによる影響を受けます。

SIMD ベクトル化は !$OMP SIMD ディレクティブを使用してループをベクトル化します。ループにこのプラグマディレクティブを追加して、ループがベクトル化されるように -qopenmp-simd (Linux* または macOS*) または /Qopenmp-simd (Windows*) オプションを指定して再コンパイルしなければなりません。

I2*I 間のデータ依存性の距離が不明なため、コンパイラーが自動でループをベクトル化しない Fortran の例ついて考えてみます。X が、データ A(I)A(2*I) が合理的な反復回数内 (例えば 64) でオーバーラップしない十分な大きさであることが分かっている場合、!$OMP SIMD を使ってループのベクトル化を強制実行することができます。また、少なくとも 8 反復内でオーバーラップしないことが分かっている場合、追加で !$OMP SIMD SIMDLEN(8) を指定することで、オーバーラップにつながる可能性のある 8 反復を超えるベクトル化を回避できます

!$OMP SIMD なしの例

[D:/simd] cat example1.f 
subroutine add(A, N, X)
integer N, X 
real    A(N) 
DO I=X, N
  A(I) = A(I) + A(2*I) 
ENDDO 
End
[D:/simd] ifort example1.f -c -nologo -Qopt-report2 -Qopt-report-phase=vec -Qopt-report-file=stderr

最適化レポート開始: ADD

    レポート: ベクトルの最適化 [vec]


ループの開始 example1.f(5,9)
<マルチバージョン v1>
   リマーク #15344: ループはベクトル化されませんでした: ベクトル依存関係がベクトル化を妨げています。最初の依存関係を以下に示します。詳細については、レベル 5 のレポートを使用してください。
   リマーク #15346: ベクトル依存関係: FLOW の依存関係が A(I) (6:11) と A(I*2) (5:11) の間に仮定されました。
ループの終了

ループの開始 example1.f(5,9)
<剰余, マルチバージョン v1>
ループの終了

ループの開始 example1.f(5,9)
<マルチバージョン v2>
  リマーク #15304: ループはベクトル化されませんでした: マルチバージョンのベクトル化できないループ・インスタンスです。
ループの終了

ループの開始 example1.f(5,9)
<剰余, マルチバージョン v2>
ループの終了
===========================================================================

!$OMP SIMD ありの例

[D:/simd] cat example1.f 
subroutine add(A, N, X) 
integer N, X 
real    A(N) 
!X は 8 以上なので少なくとも 8 反復とオーバーラップ
!$OMP SIMD SIMDLEN(8)
DO I=X, N
  A(I) = A(I) + A(2*I) 
ENDDO 
End
[D:/simd] ifort example1.f -c -nologo -Qopt-report2 -Qopt-report-phase=vec -Qopt-report-file=stderr -Qopenp-simd

最適化レポート開始: ADD

    レポート: ベクトルの最適化 [vec]


ループの開始 example1.f(6,9)
<ベクトル化のピールループ>
ループの終了

ループの開始 example1.f(6,9)
   リマーク #15301: OpenMP* SIMD ループがベクトル化されました。
ループの終了

ループの開始 example1.f(6,9)
<ベクトル化の剰余ループ>
ループの終了
===========================================================================

!$OMP SIMD と自動ベクトル化ヒントの主な違いは、!$OMP SIMD では、コンパイラーはループをベクトル化できない場合に警告を発行します。自動ベクトル化ヒントでは、!DIR$ VECTOR ALWAYS ヒントを使用した場合でも、実際のベクトル化はコンパイラーの判断にまかせられます。

!$OMP SIMD にはオプション節があり、コンパイラーにベクトル化の方法を指示できます。コンパイラーが正しいベクトル化コードを生成するための十分な情報を得られるように、これらの節を適切に使用してください。節についての詳細は、!$OMP SIMD の説明を参照してください。

追加のセマンティクス

!$OMP SIMDディレクティブの使用に関して、次の点に注意してください。

vector 宣言の使用

次のユーザー定義関数 foo() を使用してシリアル計算とベクトル計算を比較するプログラムの Fortran サンプルコードについて考えてみます。

このセクションで示すコード例はすべて Windows* の Fortran のみが対象です。

ユーザー定義関数がベクトル化しない例

!! ファイル simdmain.f90 
program simdtest
use IFPORT
! 外部ファイルのベクトル関数をテストします。
implicit none
interface
   integer function foo(a, b)
   integer a, b
   end function foo
end interface

integer, parameter :: M = 48, N = 64

  integer  i, j
  integer, dimension(M,N) :: a1  
  integer, dimension(M,N) :: a2
  integer, dimension(M,N) :: s_a3
  integer, dimension(M,N) :: v_a3 
logical :: err_flag = .false.

! 配列用の乱数を計算します。
do j = 1, N
  do i = 1, M
   a1(i,j) = rand() * M
   a2(i,j) = rand() * M
  end do
end do

! シリアル結果を計算します。
do j = 1, N 
!dir$ novector 
  do i = 1, M
   s_a3(i,j) = foo(a1(i,j), a2(i,j))
  end do
end do

! ベクトル結果を計算します。
  do j = 1, N 
   do i = 1, M
    v_a3(i,j) = foo(a1(i,j), a2(i,j))
   end do
  end do

! シリアル結果とベクトル結果を比較します。
do j = 1, N 
  do i = 1, M
   if (s_a3(i,j) .ne. v_a3(i,j)) then
    err_flag = .true. 
    print *, s_a3(i, j), v_a3(i,j)
   end if
  end do 
 end do
if (err_flag .eq. .true.) then
  write(*,*) "FAILED"
   else
  write(*,*) "PASSED"
end if 
end program

!! ファイル: vecfoo.f90 
integer function foo(a, b)
implicit none
integer, intent(in) :: a, b
  foo = a - b 
end function 
[49 C:/temp] ifort -nologo -qopt-report2 -qopt-report-phase=vec -qopt-report-file=stderr simdmain.f90 vecfoo.f90

最適化レポート開始: SIMDTEST

    レポート: ベクトルの最適化 [vec]


ループの開始 simdmain.f90(33,3)
    リマーク #15319: ループはベクトル化されませんでした: novector ディレクティブが使用されています。
ループの終了

ループの開始 simdmain.f90(54,2)
   リマーク #15541: 外部ループは自動ベクトル化されませんでした: SIMD ディレクティブの使用を検討してください。

   ループの開始 simdmain.f90(47,3)
      リマーク #15344: ループはベクトル化されませんでした: ベクトル依存関係がベクトル化を妨げています。最初の依存関係を以下に示します。
詳細については、レベル 5 のレポートを使用してください。
      リマーク #15346: ベクトル依存関係: OUTPUT の依存関係が (50:5) と (50:5) の間に仮定されました。
   ループの終了
ループの終了


最適化できないループ:


ループの開始 simdmain.f90(28,2)
   リマーク #15543: ループはベクトル化されませんでした: 関数の呼び出しを持つループは最適化の候補と見なされません。

   ループの開始 simdmain.f90(27,3)
      リマーク #15543: ループはベクトル化されませんでした: 関数の呼び出しを持つループは最適化の候補と見なされません。
   ループの終了
ループの終了

ループの開始 simdmain.f90(36,2)
   リマーク #15543: ループはベクトル化されませんでした: 関数の呼び出しを持つループは最適化の候補と見なされません。
ループの終了

ループの開始 simdmain.f90(43,3)
   リマーク #15543: ループはベクトル化されませんでした: 関数の呼び出しを持つループは最適化の候補と見なされません。

   ループの開始 simdmain.f90(42,4)
      リマーク #15543: ループはベクトル化されませんでした: 関数の呼び出しを持つループは最適化の候補と見なされません。
   ループの終了
ループの終了
===========================================================================

上記のコードをコンパイルすると、この呼び出しで foo() がインライン展開されていない限り、自動ベクトル化はこの関数が何をするか分からないため、foo() 関数を含むループは自動ベクトル化されません。

関数呼び出しがインライン展開されていない場合は、!DIR$ attributes vector::function-name-list 宣言を使用して、ループと foo() 関数をベクトル化できます。vector 宣言を関数宣言に追加して、コードを再コンパイルするだけです。これで、ループと関数はベクトル化されます。

vector 宣言のあるユーザー定義関数を持つループが自動ベクトル化する例

!! ファイル simdmain.f90 
program simdtest 
!外部ファイルのベクトル関数をテストします。
use IFPORT
implicit none
interface
   integer function foo(a, b) 
!$omp declare simd
   integer a, b
   end function foo
end interface

 integer, parameter :: M = 48, N = 64

  integer  i, j
  integer, dimension(M,N) :: a1  
  integer, dimension(M,N) :: a2
  integer, dimension(M,N) :: s_a3
  integer, dimension(M,N) :: v_a3 
logical :: err_flag = .false.

! 配列用の乱数を計算します。
do j = 1, N
  do i = 1, M
   a1(i,j) = rand() * M
   a2(i,j) = rand() * M
  end do
end do

 ! シリアル結果を計算します。
do j = 1, N 
!dir$ novector 
  do i = 1, M
   s_a3(i,j) = foo(a1(i,j), a2(i,j))
  end do
end do

 ! ベクトル結果を計算します。
  do j = 1, N 
   do i = 1, M
    v_a3(i,j) = foo(a1(i,j), a2(i,j))
   end do
  end do

 ! シリアル結果とベクトル結果を比較します
do j = 1, N 
  do i = 1, M
   if (s_a3(i,j) .ne. v_a3(i,j)) then
    err_flag = .true. 
    print *, s_a3(i, j), v_a3(i,j)
   end if
  end do 
 end do
if (err_flag .eq. .true.) then
  write(*,*) "FAILED"
   else
  write(*,*) "PASSED"
end if 
end program

!! ファイル: vecfoo.f90 
integer function foo(a, b) 
!$omp declare simd
implicit none
integer, intent(in) :: a, b
  foo = a - b 
end function 
[49 C:/temp] ifort -nologo -qopt-report2 -qopt-report-phase=vec -qopt-report-file=stderr simdmain.f90 vecfoo.f90 -qopenmp

最適化レポート開始: SIMDTEST

    レポート: ベクトルの最適化 [vec]


ループの開始 simdmain.f90(32,2)
   リマーク #15541: 外部ループは自動ベクトル化されませんでした: SIMD ディレクティブの使用を検討してください。

   ループの開始 simdmain.f90(34,3)
      リマーク #15319: ループはベクトル化されませんでした: novector ディレクティブが使用されています。
   ループの終了
ループの終了

ループの開始 simdmain.f90(40,3)
   リマーク #15542: ループはベクトル化されませんでした: 内部ループがすでにベクトル化されています。

   ループの開始 simdmain.f90(41,4)
      remark #15300: LOOP WAS VECTORIZED
   ループの終了
ループの終了

ループの開始 simdmain.f90(55,2)
   リマーク #15541: 外部ループは自動ベクトル化されませんでした: SIMD ディレクティブの使用を検討してください。

   ループの開始 simdmain.f90(48,3)
      リマーク #15344: ループはベクトル化されませんでした: ベクトル依存関係がベクトル化を妨げています。最初の依存関係を以下に示します。
詳細については、レベル 5 のレポートを使用してください。
      リマーク #15346: ベクトル依存関係: OUTPUT の依存関係が (51:5) と (51:5) の間に仮定されました。
   ループの終了
ループの終了


最適化できないループ:


ループの開始 simdmain.f90(29,2)
   リマーク #15543: ループはベクトル化されませんでした: 関数の呼び出しを持つループは最適化の候補と見なされません。

   ループの開始 simdmain.f90(28,3)
      リマーク #15543: ループはベクトル化されませんでした: 関数の呼び出しを持つループは最適化の候補と見なされません。
   ループの終了
ループの終了
===========================================================================

最適化レポート開始: FOO..xN4vv

    レポート: ベクトルの最適化 [vec]

リマーク #15347: 関数がベクトル化されました: xmm、simdlen=4、マスクなし、仮引数の型: (vector,vector)
===========================================================================

最適化レポート開始: FOO..xM4vv

    レポート: ベクトルの最適化 [vec]

リマーク #15347: 関数がベクトル化されました: xmm、simdlen=4、マスクなし、仮引数の型: (vector,vector)
===========================================================================.

!$omp declare simd 宣言の使用に関する制約

ベクトル化は、ハードウェアとソースコードのスタイルという 2 つの主な要因により制約されます。vector 宣言を使用する場合、使用できない機能は次のとおりです。

非ベクトル関数呼び出しは、一般にベクトル関数内で許可されますが、そのような関数への呼び出しはレーン単位でシリアル化されるため、パフォーマンスが低下します。また、SIMD 対応関数では、引数による書き込みを除く副作用があってはなりません。非ベクトル関数は、この規則に反するため、SIMD 対応関数とサブルーチンで実行する場合には注意が必要です。

仮引数は次のデータ型でなければなりません。

関連情報