ユーザー指示または 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

Begin optimization report for: ADD

    Report from: Vector optimizations [vec]


LOOP BEGIN at example1.f(5,9)
<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 A(I) (6:11) and A(I*2) (5:11)
LOOP END

LOOP BEGIN at example1.f(5,9)
<Remainder, Multiversioned v1>
LOOP END

LOOP BEGIN at example1.f(5,9)
<Multiversioned v2>
  remark #15304: loop was not vectorized: non-vectorizable loop instance from multiversioning
LOOP END

LOOP BEGIN at example1.f(5,9)
<Remainder, Multiversioned v2>
LOOP END
===========================================================================

!$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

Begin optimization report for: ADD

    Report from: Vector optimizations [vec]


LOOP BEGIN at example1.f(6,9)
<Peeled loop for vectorization>
LOOP END

LOOP BEGIN at example1.f(6,9)
   remark #15301: OpenMP SIMD LOOP WAS VECTORIZED
LOOP END

LOOP BEGIN at example1.f(6,9)
<Remainder loop for vectorization>
LOOP END
===========================================================================

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

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

追加のセマンティクス

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

  • 変数は private、linear、reduction のいずれかに属します (またはいずれにも属しません)。

  • ベクトルループ内では、private、linear、reduction の場合、式はベクトル値として評価されます。または、ベクトル値に評価される部分式があります。そうでない場合、スカラー値として評価されます (つまり、同じ値をすべての反復にブロードキャストします)。スカラー値は、よくループ不変として使用されますが、必ずしもループ不変であるわけではありません。

  • ベクトル値はスカラー型の左辺値へ割り当てられません。エラーになります。

  • スカラー型の左辺値はベクトル条件下では割り当てられません。エラーになります。

  • 計算型 GOTO 文はサポートされていません。

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

Begin optimization report for: SIMDTEST

    Report from: Vector optimizations [vec]


LOOP BEGIN at simdmain.f90(33,3)
   remark #15319: loop was not vectorized: novector directive used
LOOP END

LOOP BEGIN at simdmain.f90(54,2)
   remark #15541: outer loop was not auto-vectorized: consider using SIMD directive

   LOOP BEGIN at simdmain.f90(47,3)
      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 OUTPUT dependence between at(50:5) and at (50:5)
   LOOP END
LOOP END


Non-optimizable loops:


LOOP BEGIN at simdmain.f90(28,2)
   remark #15543: loop was not vectorized: loop with function call not considered an optimization candidate.

   LOOP BEGIN at simdmain.f90(27,3)
      remark #15543: loop was not vectorized: loop with function call not considered an optimization candidate.
   LOOP END
LOOP END

LOOP BEGIN at simdmain.f90(36,2)
   remark #15543: loop was not vectorized: loop with function call not considered an optimization candidate.
LOOP END

LOOP BEGIN at simdmain.f90(43,3)
   remark #15543: loop was not vectorized: loop with function call not considered an optimization candidate.

   LOOP BEGIN at simdmain.f90(42,4)
      remark #15543: loop was not vectorized: loop with function call not considered an optimization candidate.
   LOOP END
LOOP END
===========================================================================

上記のコードをコンパイルすると、この呼び出しで 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

Begin optimization report for: SIMDTEST

    Report from: Vector optimizations [vec]


LOOP BEGIN at simdmain.f90(32,2)
   remark #15541: outer loop was not auto-vectorized: consider using SIMD directive

   LOOP BEGIN at simdmain.f90(34,3)
      remark #15319: loop was not vectorized: novector directive used
   LOOP END
LOOP END

LOOP BEGIN at simdmain.f90(40,3)
   remark #15542: loop was not vectorized: inner loop was already vectorized

   LOOP BEGIN at simdmain.f90(41,4)
      remark #15300: LOOP WAS VECTORIZED
   LOOP END
LOOP END

LOOP BEGIN at simdmain.f90(55,2)
   remark #15541: outer loop was not auto-vectorized: consider using SIMD directive

   LOOP BEGIN at simdmain.f90(48,3)
      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 OUTPUT dependence between at (51:5) and at (51:5)
   LOOP END
LOOP END


Non-optimizable loops:


LOOP BEGIN at simdmain.f90(29,2)
   remark #15543: loop was not vectorized: loop with function call not considered an optimization candidate.

   LOOP BEGIN at simdmain.f90(28,3)
      remark #15543: loop was not vectorized: loop with function call not considered an optimization candidate.
   LOOP END
LOOP END
===========================================================================

Begin optimization report for: FOO..xN4vv

    Report from: Vector optimizations [vec]

remark #15347: FUNCTION WAS VECTORIZED with xmm, simdlen=4, unmasked, formal parameter types: (vector,vector)
===========================================================================

Begin optimization report for: FOO..xM4vv

    Report from: Vector optimizations [vec]

remark #15347: FUNCTION WAS VECTORIZED with xmm, simdlen=4, masked, formal parameter types: (vector,vector)
===========================================================================.

!$OMP DECLARE SIMD 宣言の使用に関する制約

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

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

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

関連情報