インテル® Fortran コンパイラー 16.0 ユーザー・リファレンス・ガイド
ユーザー指示または SIMD ベクトル化は、OpenMP* 並列化が自動並列化を補足するように、自動ベクトル化を補足します。下記の図でこの関係を示します。ユーザー指示によるベクトル化は SIMD (Single-Instruction, Multiple-Data) 機能として実装され、SIMD ベクトル化と呼ばれます。
SIMD ベクトル化機能は、インテル製マイクロプロセッサーおよび互換マイクロプロセッサーの両方で利用可能です。ベクトル化により呼び出されるライブラリー・ルーチンは、互換マイクロプロセッサーよりもインテル製マイクロプロセッサーにおいてより優れたパフォーマンスが得られる可能性があります。また、ベクトル化は、/arch (Windows*)、-m (Linux* および OS X*)、[Q]x などの特定のオプションによる影響を受けます。
次の図は、ハードウェアのベクトル化機能を活用するベクトル化コードを生成するためのさまざまなアプローチの中で、SIMD ベクトル化がどこに位置付けされているかを示しています。SIMD ベクトル化を用いて記述されたプログラムは、自動ベクトル化ヒントを使用して記述されたものと似ています。SIMD ベクトル化を使用すると、ベクトル化コードの取得に必要なコードの変更を最小限にすることができます。
SIMD ベクトル化は !DIR$ SIMD 宣言子を使用してループをベクトル化します。ループにこの宣言子を追加して、ループがベクトル化されるように再コンパイルしなければなりません ([Q]simd オプションがデフォルトで有効になります)。
不明なデータ依存性の距離 "X" のために、コンパイラーが自動でループをベクトル化しない Fortran の例ついて考えてみます。自動ベクトル化ヒント !DIR$ IVDEP でデータ依存性のアサーションを追加してループをベクトル化するかどうかをコンパイラーに判断させるか、または !DIR$ SIMD を使ってループのベクトル化を強制実行することができます。
!DIR$ SIMD なしの例 |
---|
[D:/simd] cat example1.f subroutine add(A, N, X) integer N, X real A(N) DO I=X+1, N A(I) = A(I) + A(I-X) ENDDO end |
[D:/simd] ifort example1.f -nologo -Qvec-report2 D:\simd\example1.f(6): (列 9) リマーク: ループはベクトル化されませんでした: ベクトル依存関係が存在しています。 |
!DIR$ SIMD ありの例 |
[D:/simd] cat example1.f subroutine add(A, N, X) integer N, X real A(N) !DIR$ SIMD DO I=X+1, N A(I) = A(I) + A(I-X) ENDDO end |
[D:/simd] ifort example1.f -nologo -Qvec-report2 -Qsimd D:\simd\example1.f(7): (列 9) リマーク: ループがベクトル化されました。 |
SIMD 宣言子と自動ベクトル化ヒントの主な違いは、SIMD 宣言子では、コンパイラーはループをベクトル化できない場合に警告を発行します。自動ベクトル化ヒントでは、!DIR$ VECTOR ALWAYS ヒントを使用した場合でも、実際のベクトル化はコンパイラーの判断にまかせられます。
SIMD 宣言子にはオプション節があり、コンパイラーにベクトル化の方法を指示できます。コンパイラーが正しいベクトル化コードを生成するための十分な情報を得られるように、これらの節を適切に使用してください。節についての詳細は、!DIR$ SIMD の説明を参照してください。
!DIR$ SIMD 宣言子の使用に関して、次の点に注意してください。
次のユーザー定義関数 foo() を使用してシリアル計算とベクトル計算を比較するプログラムの Fortran サンプルコードについて考えてみます。
このセクションで示すコード例はすべて Windows* の Fortran のみが対象です。
ユーザー定義関数がベクトル化しない例 |
---|
!! ファイル: simdmain.f90 program simdtest ! 外部ファイルのベクトル関数をテストします。 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 -Qvec-report simdmain.f90 vecfoo.f90 simdmain.f90 vecfoo.f90 C:\temp\simdmain.f90(3): (列 3) リマーク: ループはベクトル化されませんでした: ベクトル依存関係が存在しています。C:\temp\vecfoo.f90(3): (列 3) リマーク: 関数はベクトル化されませんでした。 |
上記のコードをコンパイルすると、この呼び出しで foo() がインライン展開されていない限り、自動ベクトル化はこの関数が何をするか分からないため、foo() 関数を含むループは自動ベクトル化されません。
関数呼び出しがインライン展開されていない場合は、!DIR$ attributes vector::function-name-list 宣言を使用して、ループと foo() 関数をベクトル化できます。vector 宣言を関数宣言に追加して、コードを再コンパイルするだけです。これで、ループと関数はベクトル化されます。
vector 宣言のあるユーザー定義関数を持つループが自動ベクトル化する例 |
---|
!! ファイル: simdmain.f90 program simdtest ! 外部ファイルのベクトル関数をテストします。 implicit none interface integer function foo(a, b) !dir$ attributes vector :: foo 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) !dir$ attributes vector :: foo implicit none integer, intent(in) :: a, b foo = a - b end function |
[49 C:/temp] ifort -Qvec-report simdmain.f90 vecfoo.f90 simdmain.f90 vecfoo.f90 C:\temp\simdmain.f90(3): (列 3) リマーク: ループがベクトル化されました。C:\temp\vecfoo.f90(3): (列 3) リマーク: 関数がベクトル化されました。 |
ベクトル化は、ハードウェアとソースコードのスタイルという 2 つの主な要因により制約されます。vector 宣言を使用する場合、使用できない機能は次のとおりです。
_Cilk_spawn、_Cilk_for、OpenMP* parallel/for/sections/task、および明示的なスレッド API 呼び出しによるスレッドの生成と join
setjmp、longjmp、EH、SEH の使用
インライン・アセンブリー・コードと VML
非ベクトル関数の呼び出し (すべての SVML 関数はベクトル関数と見なされる)
ロック、バリア、atomic 構文、クリティカル・セクション
GOTO 文
組込み関数 (例: SVML 組込み関数)
関数ポインターと仮想関数による関数呼び出し
ループ/配列表記構造
構造体アクセス
計算型 GOTO 文
仮パラメーターは次のデータ型でなければなりません。