< 目次

Fortran 向けのベクトル化推奨事項

非効率なピール/リマインダー・ループがあります

すべてまたは一部の元のループ反復が、ループ本体を実行していません。ピール/リマインダーループをループ本体へ移動することでパフォーマンスが改善されます。

データをアライメント

元のループ内のメモリーアクセスの一つが、 適切にアライメントされた境界から開始されていません。解決方法: データをアライメントし、コンパイラーにデータがアライメントされていることを知らせます。データをアライメントするには、__declspec(align()) を使用します。元のループの前で use __assume_aligned() を使用して、コンパイラーにデータがアライメントされていることを知らせます。

関連情報:

スレッド化と SIMD 命令の両方を使用してループを並列化

ループはスレッド化と自動ベクトル化されていますが、トリップカウントがベクトル長の倍数ではありません。解決方法: 次のすべてを行います。

  • リンク時に SVML ABI 互換ライブラリーを使用するには、!$omp parallel do simd ディレクティブを使用して、スレッドと SIMD 命令の両方でループを並列化します。このディレクティブは、ループ反復をチャンク (サブセット) に分割してスレッドに分配し、SIMD 命令を使用してチャンクの反復を同時に実行します。
  • schedule(simd: [kind]) 修飾子を追加して、チャンクサイズ (チャンクごとの反復数) がベクイトル長の倍数であることを保証します。

オリジナルのコードサンプル:

!$omp parallel do schedule(static) 
do i = 1,1000 
   c(i) = a(i)*b(i) 
end do !$omp end parallel do

修正されたのコードサンプル:

!$omp parallel do simd schedule(simd: static) 
do i = 1,1000 
   c(i) = a(i)*b(i) 
end do !$omp end parallel do simd

関連情報:

スカラーのリマインダー生成を強制します

コンパイラーが生成したマスク付きのベクトル化されたリマインダーループは、効率良いベクトル化には反復数が少なすぎます。スカラーループがより適しています。解決方法: ディレクティブを使用してスカラー・リマインダーの生成を強制します。!DIR$ VECTOR NOVECREMAINDER。

subroutine add(A, N, X) integer N, X real A(N) ! リマインダー・ループをベクトル化しないことをコンパイラーに強制します 
!DIR$ VECTOR NOVECREMAINDER 
do i=x+1, n 
   a(i) = a(i) + a(i-x) 
enddo end

関連情報:

ベクトルリマインダーの生成を強制します

コンパイラーはリマインダー・ループをベクトル化しませんでしたが、パフォーマンスは向上します。解決方法: ディレクティブを使用してベクトル化を強制します。!DIR$ VECTOR VECREMAINDER。

subroutine add(A, N, X) integer N, X real A(N) ! リマインダー・ループのベクトル化をコンパイラーに強制します 
!DIR$ VECTOR VECREMAINDER 
do i=x+1, n 
   a(i) = a(i) + a(i-x) 
enddo end

関連情報:

推測されるループの反復数を指定

コンパイラーは静的にトリップカウントを認識できません。解決方法: ディレクティブ!DIR$ LOOP COUNT。

推測されるループの反復を最小 3、最大 10、平均 5 に設定:

!DIR$ LOOP COUNT MAX(10), MIN(3), AVG(5) 
do i =1, m 
   b(i) = a(i) + 1 d(i) = c(i) + 1 
enddo

関連情報:

チャンクサイズを変更します

ループは、!$omp parallel for simdディレクティブを使用してスレッド化とベクトル化されています。特に、このディレクティブはループ反復をチャンク (サブセット) に分割して、チャンクをスレッドに分配することで、チャンクの反復は SIMD 命令を使用して同時に実行されます。この場合、チャンクサイズ (チャンクごとの反復数) がベクトル長の倍数ではありません。解決方法: ベクトル宣言にschedule (simd: [kind])修飾子を!$omp parallel for simdディレクティブに追加します。

最大ベクトル長を保証します。

!$omp parallel do simd schedule(simd: static) 
do i = 1,1000 
   c(i) = a(i)*b(i) 
end do !$omp end parallel do simd

関連情報:

データのパディングを行います

トリップカウントがベクトル長の倍数ではありません。解決方法: 次のいずれかを行います。

  • オブジェクトのサイズを増やして反復数を追加し、反復数がベクトル長の倍数になるようにします。
  • 静的および動的オブジェクトのサイズを増やし、データパディングを行うコンパイラー・オプションを使用します。

関連情報:

トリップ・カウント・データを収集

サーベイレポートで、正確な推奨を作成するにはトリップ・カウント・データが不足しています。

アンロールを無効にします

ループアンロール後のトリップカウントは、ベクトル長と比べると小さすぎです。解決方法: ループのアンロールを行わないか、ディレクティブを使用してアンロール係数を減らします。!DIR$ NOUNROLL または !DIR$ UNROLL

!DIR$ NOUNROLL でループの自動アンロールを無効化します。

!DIR$ NOUNROLL 
 do i = 1, m 
    b(i) = a(i) + 1 d(i) = c(i) + 1 
 enddo

関連情報:

小さなベクトル長を使用します

コンパイラーはベクトル長を選択しましたが、トリップカウントはベクトル長よりも小さめです。解決方法: ディレクティブを使用して小さなベクトル長を指定します。!$OMP SIMD SIMDLEN

!$OMP SIMD SIMDLEN(4) 
do i = 1, m 
    b(i) = a(i) + 1 d(i) = c(i) + 1 
enddo

インテル® Fortran コンパイラーのバージョン 19.0 以降には、コンパイラーがコストに基づいて最適なベクトル長を選択できるように指示する新しい vectorlength 句があります。!DIR$ VECTOR VECTORLENGTH (vl1, vl2, ..., vln)説明:vl は、2 の整数乗です。

!DIR$ VECTOR VECTORLENGTH(2, 4, 16) 
do i = 1, m 
    b(i) = a(i) + 1 d(i) = c(i) + 1 
enddo

関連情報:

動的なアライメントを無効にします

コンパイラーは、ベクトルループをメモリー参照に合わせて、反復をベクトルループからスカラーループに自動的にピールします。ただし、この最適化が常に最適であるとは限りません。パフォーマンスを向上するには、次のディレクティブを使用して自動ピールを無効にします。!DIR$ VECTOR NODYNAMIC_ALIGN

...!DIR$ VECTOR NODYNAMIC_ALIGN 
do i = 1, len 
   a(i) = b(i) * c(i) 
enddo

関連情報:

シリアル化されたユーザー関数があります

ユーザー定義関数内のループ本体はベクトル化されません。

インライン展開を有効にします

ユーザー定義関数のインライン展開はコンパイラー・オプションで無効化されています。解決方法: インライン展開を制御するため、Obまたはinline-levelコンパイラー・オプションを使用している場合、引数0を引数1に置き換えて、inlineキーワードや属性で指定されるインライン展開を有効にするか、引数2で、すべての関数のインライン展開をコンパイラーの判断に任せます。

Windows*

Linux*

/Ob1 または /Ob2 -inline-level=1 または -inline-level=2

関連情報:

ループ内のシリアル化された関数をベクトル化します

  • SIMD 命令を使用してループのベクトル化を強制したり、ディレクティブを使用して関数の SIMD バージョンを生成します。
    Target ディレクティブ
    元のループ !$OMP SIMD
    内部関数定義や宣言 !$OMP DECLARE SIMD
  • インライン展開を制御するためObまたはinline-levelコンパイラー・オプションを引数1、キーワードを使用してインライン展開を有効にするか、引数12に置き換えて、すべての関数のインライン展開の判断をコンパイラーに任せます。

real function f (x) 
!DIR$ OMP DECLARE SIMD 
real, intent(in), value :: x 
   f= x + 1 
end function f 
!DIR$ OMP SIMD 
   do k = 1, N a(k) = f(k) 
enddo

関連情報:

スカラー数学関数呼び出しがあります

ループ本体内の数学関数呼び出しは、コンパイラーがループを効率良くベクトル化するのを妨げます。ベクトル数学関数を使用することでパフォーマンスを改善します。

インライン展開を有効にします

インライン展開はコンパイラーによって無効化されています。解決方法: インライン展開を制御するため、Obまたはinline-levelコンパイラー・オプションを使用している場合、引数0を引数1に置き換えて、inlineキーワードや属性で指定されるインライン展開を有効にするか、引数2で、すべての関数のインライン展開をコンパイラーの判断に任せます。

Windows*

Linux*

/Ob1 または /Ob2 -inline-level=1 または -inline-level=2

あるいは、#include <mathimf.h>ヘッダーを標準の#include <math.h>に置き替えて、浮動小数点計算を行うアプリケーションで一般的に使用される最適化された高精度の数学関数を呼び出します。

関連情報:

ループ内の数学関数呼び出しをベクトル化します

浮動小数点モデル precise を使用すると、アプリケーションはシリアル化された数学関数を呼び出します。解決方法: 次のいずれかを行います。

  • fast-transcendentals コンパイラー・オプションを追加して、高速な超越関数呼び出しに置き換えます。

    Windows*

    Linux*

    /Qfast-transcendentals -fast-transcendentals

    これは浮動小数点の精度を下げます。
  • ディレクティブを使用してループのベクトル化を強制します。!$OMP SIMD
subroutine add(A, N, X) 
integer N, X real A(N) 
!DIR$ OMP SIMD 
do i=x+1, n 
    a(i) = a(i) + a(i-x) 
enddo end

関連情報:

浮動小数点モデルを変更します

strict 浮動小数点モデルを使用すると、アプリケーションはシリアル化された数学関数を呼び出します。strict浮動小数点モデルを使用すると、アプリケーションはシリアル化された数学関数を呼び出します。解決方法: 次のいずれかを行います。

  • より積極的な最適化を有効にするには、fast浮動小数点モデルを使用するか、precise浮動小数点モデルを使用して、高速な超越関数で安全ではない最適化を無効にします。

    Windows*

    Linux*

    /fast -fp-model fast
    /fp:precise /Qfast-transcendentals -fp-model precise -fast-transcendentals

    これは浮動小数点の精度を下げます。
  • より積極的な最適化を有効にするには、precise浮動小数点モデルを使用して、ディレクティブを使用してループのベクトル化を強制します。!$OMP SIMD
gfortran program.for -O2 -fopenmp -fp-model precise -fast-transcendentals 
!DIR$ OMP SIMD COLLAPSE(2) 
do i = 1, N 
    a(i) = b(i) * c(i) 
    do j = 1, N 
        d(j) = e(j) * f(j) 
    enddo 
enddo

関連情報:

Glibc ライブラリーとベクトル化された SVML 関数を使用します

アプリケーションは、ベクトル化されたバージョンの数学関数ではなく、スカラー関数を呼び出しています。解決方法: 次のすべてを行います。

  • Glibc ライブラリーをバ―ション 2.22 以降にアップグレードします。OpenMP* 4.0 以降で利用可能な SIMD ディレクティブをサポートします。
  • GNU* gcc コンパイラーをバージョン 4.9 以降にアップグレードします。ベクトル化された数学関数操作をサポートしています。
  • リンク時に SVML ABI 互換ライブラリーを使用するには、-fopenmp-ffast-mathコンパイラー・オプションを使用します。
  • ベクトル化を可能にする適切な OpenMP* SIMD ディレクティブ使用します。

ホスト上に複数の Glibc ライブラリーがインストールされている場合、-I/path/to/glibc/install/include-L/path/to/glibc/install/libコンパイラー・オプションを使用して対象のバージョンを指定します。
gfortran PROGRAM.FOR -O2 -fopenmp -ffast-math -lrt -lm -mavx2 
program main parameter (N=100000000) 
real*8 angles(N), results(N) integer i 
call srand(86456) 
do i=1,N 
    angles(i) = rand() 
enddo 
!$OMP SIMD 
do i=1,N 
    results(i) = cos(angles(i)) 
enddo 
end

関連情報:

ベクトル組込み関数にインテルの SVML を使用

アプリケーションは、ベクトル化されたバージョンの数学関数ではなく、スカラー関数を呼び出しています。解決方法: 次のすべてを行います。

  • コンパイラー・オプションを使用して、ベクトル組込み関数にインテルのショートベクトル数学ライブラリー ABI タイプを指定します。
  • ベクトル数学関数を有効にするには、-ftree-vectorize および -funsafe-math-optimizations コンパイラー・オプションを使用します。
  • リンク時に SVML ABI 互換ライブラリーを使用するには、-L/path/to/intel/lib と -lsvml コンパイラー・オプションを使用します。
gfortran PROGRAM.FOR -O2 -ftree-vectorize -funsafe-math-optimizations -mveclibabi=svml -L/opt/intel/lib/intel64 -lm -lsvml -Wl,-rpath=/opt/intel/lib/intel64 
program main parameter (N=100000000) 
real*8 angles(N), results(N) integer i 
call srand(86456) 
do i=1,N 
   angles(i) = rand() 
enddo ! the loop will be auto-vectorized 
do i=1,N 
   results(i) = cos(angles(i)) 
enddo end

関連情報:

非効率な gather/scatter 命令があります

コンパイラーがベクトル操作で使用するデータの間接、または不規則なストライドアクセスを想定しました。次のような通常のストライド・アクセス・パターンを検出するため、コンパイラーに通知することでメモリーアクセスを改善します。

パターン

説明

不変 命令はループ全体で同じメモリーの値にアクセスします。
均一 (水平方向は不変) 命令はベクトル反復で同じメモリーの値にアクセスします。
垂直方向は不変 命令はすべてのベクトル反復間で同じオフセットを使用してメモリー位置にアクセスします。
ユニット 命令はループ全体でベクトル反復 = ベクトル長のストライドで連続したメモリーの値をアクセスします。
一定 (非ユニット) 命令は反復間で同じストライドを使用してメモリー位置にアクセスします。

検出された通常のストライド・アクセス・パターンでコードをリファクタリングします

メモリー・アクセス・パターン・レポートは次の通常ストライドアクセスを示します。

変数

パターン

block 0x2b05c877040 allocated at main.cpp:14 ユニット
block 0x2b05c877040 allocated at main.cpp:14 一定 (非ユニット)

メモリー・アクセス・パターン・レポートのソース詳細表示を参照します。

メモリーアクセスを改善するには、次の操作を行います。コードをリファクタリングして、コンパイラーに通常のストライドアクセスであることを通知します。時に、複数ファイルにわたるプロシージャー間の最適化 (IPO) を有効にする、ipo/Qipo コンパイラー・オプションから利点が得られることがあります。

関連情報:

ベクトル・レジスターがスピンされている可能性があります

レジスターのスピルが検出されました。すべてのベクトルレジスターが使用中です。これは、スピルされた変数はメインメモリーに退避/復帰されるため、パフォーマンス上悪影響があります。ベクトルレジスターへの圧力を緩和してパフォーマンスを改善します。

アンロール係数を減らします

現在のディレクティブのアンロールの係数は、レジスターへの圧力を高めます。解決方法: ディレクティブを使用してアンロール係数を減らします。!DIR$ NOUNROLL または !DIR$ UNROLL

!DIR$ UNROLL 
do i = 1, m 
    b(i) = a(i) + 1 d(i) = c(i) + 1 
enddo

関連情報:

ループを小さなループに分割します

高いレジスターへの圧力は、レジスタースピルの可能性を高め、効率良いベクトル化を妨げます。解決方法: !DIR$ DISTRIBUTE POINT ディレクティブを使用するか、コードを書き換えてループを分割します。これは、レジスターへの圧力を低くしてソフトウェア・パイプライン処理を有効にすることで、命令とデータキャッシュ両方の使用を改善できます。

!DIR$ DISTRIBUTE POINT 
do i = 1, m 
    b(i) = a(i) + 1 ... 
    c(i) = a(i) + b(i) 
! コンパイラーがここで分割することを決定 ! ! データの依存関係があります ... 
    d(i) = c(i) + 1 
enddo 
do i =1, m 
    b(i) = a(i) + 1 ...!DIR$ DISTRIBUTE POINT 
call sub(a, n)
! ここから分散を開始。 
! すべてのループ伝搬依存を無視 
    c(i) = a(i) + b(i) ... 
    d(i) = c(i) + 1 
enddo

関連情報:

依存関係が想定されます

コンパイラーが、ループ内でアンチ依存関係 (読み取り後の書き込み - WAR) または真の依存関係 (書込み後の読み取り - RAW) を想定しました。想定を調査して適切に処理することでパフォーマンスを改善します。

実際に依存関係があるか確認します

実際に依存関係がループ中にあるか確認できません。確認方法: 依存関係解析を実行します。

ベクトル化を有効にします

依存関係解析は与えられたワークロードでループの依存関係を検出しませんでした。キーワードrestrict または、ディレクティブを使用してベクトル化が安全であることをコンパイラーに通知します。

Target

ディレクティブ

!$OMP SIMD ループのすべての依存関係を無視。
!DIR$ IVDEP ベクトルの依存関係のみを無視 (最も安全)。
!DIR$ IVDEP 
do i = 1, N-4, 4 
    a(i+4) = b(i) * c 
enddo

関連情報:

(実際の) 依存関係があります

コンパイラーが、ループ内でアンチ依存関係 (読み取り後の書き込み - WAR) または真の依存関係 (書込み後の読み取り - RAW) を想定しました。想定を調査して適切に処理することでパフォーマンスを改善します。

依存関係の解決

依存性解析は、ループ内に実際の依存関係があることを示しています。解決方法: 次のいずれかを行います。

  • アンチ依存関係が存在する場合、!$OMP SIMD SAFELEN(length) ディレクティブを使用します。ここで、レングス(長さ) は、アンチ依存関係がある反復間の距離よりも小さな値です。
    !$OMP SIMD SAFELEN(4) 
    do i = 1, N-4, 4 
        a(i+4) = b(i) * c 
    enddo
  • ループ内にリダクションによる依存関係が存在する場合、!$OMP SIMD REDUCTION(operator:list)を使用します。
    !$OMP SIMD REDUCTION(+:SUMX) 
    do k = 1, size2 
        sumx = sumx + x(k) * b(k) 
    enddo
  • コードを変更して依存関係を排除します。変数のプライベート化などのプログラミング手法を使用します。

関連情報:

データ型の変換があります

ループ内に複数のデータ型が混在しています。データ型の変換を防ぐことでハードウェアのベクトル化サポートの効率を高めます。

最も小さなデータ型を使用します

ループは異なる幅のデータ型を含んでいます。解決方法: ベクトルレジスター幅全体を使用するため、必要な精度を提供する最小のデータ型を使用します。

例: 16 ビットのみ必要な場合、int より short を使用すると、4 ウェイと 8 ウェイの SIMD 並列処理のそれぞれ違いが実感できます。

ユーザー定義関数呼び出しがあります

ループ本体内のユーザー関数呼び出しは、コンパイラーがループをベクトル化するのを妨げます。

インライン展開を有効にします

ユーザー定義関数のインライン展開はコンパイラー・オプションで無効化されています。解決方法: インライン展開を制御するため、Ob または inline-level コンパイラー・オプションを使用している場合、引数 0 を引数 1 に置き換えて、inline キーワードや属性で指定されるインライン展開を有効にするか、引数2で、すべての関数のインライン展開をコンパイラーの判断に任せます。

Windows*

Linux*

/Ob1 または /Ob2 -inline-level=1または-inline-level=2

関連情報:

ループ内のユーザー関数をベクトル化します

コンパイラーはこれらのユーザー定義関数をベクトル化またはインライン展開できません:my_calc()解決方法: 次のいずれかを行います。

  • SIMD 命令を使用してループのベクトル化を強制したり、ディレクティブを使用して関数の SIMD バージョンを生成します。
    Target ディレクティブ
    元のループ !$OMP SIMD
    内部関数定義や宣言 !$OMP DECLARE SIMD
real function f (x) 
!DIR$ OMP DECLARE SIMD 
real, intent(in), value :: x 
   f= x + 1 
end function f 
!DIR$ OMP SIMD 
   do k = 1, N a(k) = f(k) 
enddo

関連情報:

Fortran SIMD 対応関数への書き換え

配列をELEMENTAL関数/サブルーチンに渡すと、ベクトル化を妨げる依存関係が生じます。解決方法:

  • SIMD 命令を使用してループのベクトル化を強制し、および/またはディレクティブを使用して SIMD バージョンの関数を生成します。
    Target ディレクティブ
    元のループ !$OMP SIMD
    内部関数定義や宣言 !$OMP DECLARE SIMD
  • DO ループからの呼び出し。

オリジナルコードの例:

elemental subroutine callee(t,q,r) 
real, intent(in) :: t, q real, intent(out) :: r 
    r = t + q 
end subroutine callee
 ... 
do k = 1,nlev 
    call callee(a(:,k), b(:,k), c(:,k)) 
end do
 ...

修正されたコードの例:

subroutine callee(t,q,r) 
!$OMP DECLARE SIMD(callee) 
real, intent(in) :: t, q 
real, intent(out) :: r 
    r = t + q 
end subroutine callee
 ... 
do k = 1,nlev 
!$OMP SIMD 
    do i = 1,n 
        call callee(a(i,k), b(i,k), c(i,k)) 
    end do 
end do
 ...

関連情報:

コンパイラーにはループのベクトル化する情報が不足しています

原因: インテル® コンパイラーを使用していないか、古いバージョンのインテル® コンパイラーを使用しています。それにもかかわらず、ベクトル化を妨げる問題はなく、ベクトル化が有益であるように示されることがあります。

ベクトル化の可能性を調査

自動ベクトル化を有効にしてコンパイルしても、コンパイラーはコードをベクトル化できないことがあります。ベクトル化の可能性を調査:

  • ベクトル化の強制が安全ではない真のデータ依存関係を特定するため、依存関係解析を実行します。
  • 適用されなかった最適化の可能性を出力する自動ベクトル化レポートレベル。
  • GNU* Fortran コンパイラーでは、次のいずれかを行います。
    • 適用されなかった最適化の可能性を出力するには、fopt-info-vec-missed コンパイラー・オプションを使用します。
    • OpenMP* omp simd ディレクティブを使用して、コンパイラーにベクトル化が安全であることを知らせます。
    • その他の自動ベクトル化ディレクティブを使用します。

関連情報:

自動ベクトルを有効にします

自動ベクトル化を無効にしてコンパイルしています; 自動ベクトル化を有効にします。

  • インテル® コンパイラー 14.x 以前: 最適化レベルを O2 または O3 にします。
  • GNU* Fortran コンパイラーでは、次のいずれかを行います。
    • 最適化レベルを O2 または O3 にします。
    • ftree-vectorize コンパイラー・オプションを使用します。

関連情報:

システム関数呼び出しがあります

ループ本体内のシステム関数呼び出しは、コンパイラーがループをベクトル化するのを妨げます。

システム関数呼び出しをループから排除します

一般にシステム関数やサブルーチン呼び出しはベクトル化されません; print 文はベクトル化を妨げる代表例です。解決方法: ループ内ではシステム関数の呼び出しを避けます。

OpenMP* 関数呼び出しがあります

ループ本体内の OpenMP* 関数呼び出しは、コンパイラーがループを効率良くベクトル化するのを妨げます。

OpenMP* API 呼び出しをループ本体の外へ移動します

不変でないなどの理由によって、コンパイラーが OpenMP* 呼び出しをループ本体の外へ移動できない場合、OpenMP* 呼び出しは自動ベクトル化を妨げます。解決方法:

  1. OpenMP* 並列ループを 2 つのディレクティブに分割します。

    Target

    ディレクティブ

    その他 !$OMP PARALLEL [節[[,] 節] ...]
    内部 !$OMP SIMD [節[[,] 節]...]
  2. 可能であれば OpenMP API 呼び出しをループ外部へ移動します。

オリジナルコードの例:

!$OMP PARALLEL DO PRIVATE(tid, nthreads) 
do k = 1, N 
    tid = omp_get_thread_num() 
! ループ内のこの呼び出しはベクトル化を妨げます 
    nthreads = omp_get_num_threads() 
! ループ内のこの呼び出しはベクトル化を妨げます 
     ... 
enddo

修正されたコードの例:

!$OMP PARALLEL PRIVATE(tid, nthreads) 
! OpenMP* 呼び出しをここに移動 
tid = omp_get_thread_num() 
nthreads = omp_get_num_threads() 
!$OMP DO NOWAIT 
do k = 1, N 
    ... 
enddo 
!$OMP END PARALLEL

関連情報:

OpenMP* ロック関数を排除します

オブジェクトのロックはループの実行を低速にします。解決方法: OpenMP* ロック関数を使用しないようにコードを書き換えます。

それぞれのスレッドに個別の配列を割り当てて、並列処理の後にそれらをマージすることで、速度が向上する可能性があります (ただし、メモリー消費量が増加します)。

関連情報:

非効率なメモリー・アクセス・パターンが存在する可能性があります

非効率なメモリー・アクセス・パターンは、重要なベクトルコードの実行速度を低下させたり、コンパイラーによる自動ベクトル化の妨げとなります。調査してパフォーマンスを改善します。

非効率なメモリー・アクセス・パターンを確認します

非効率なメモリー・アクセス・パターンをの存在を確認する方法がありません。解決方法: メモリー・アクセス・パターン解析を実行します。

非効率なメモリー・アクセス・パターンがあります

不規則な (可変またはランダム) ストライドアクセスのメモリーアクセス命令の比率が高いことが観測されました。調査して適切に処理することでパフォーマンスを改善します。

ループの並べ替え

このループは近接する外部ループほどメモリーのアクセス効率は良くありません。解決方法: 可能であればループを並べ替えます。

オリジナルコードの例:

subroutine matrix_multiply(arrSize, aMatrix, bMatrix, cMatrix) 
implicit none real, intent(inout) :: cMatrix(:,:) 
real, intent(in) :: aMatrix(:,:), bMatrix(:,:) 
integer, intent(in) :: arrSize 
integer :: i,j,k; 
do j=1,arrSize 
    do i=1,arrSize 
        do k=1,arrSize 
            cMatrix(i,j) = cMatrix(i,j) + aMatrix(i,k) * bMatrix(k,j) 
        end do 
    end do 
end do 
end subroutine matrix_multiply
修正されたコードの例:
subroutine matrix_multiply(arrSize, aMatrix, bMatrix, cMatrix) 
implicit none real, intent(inout) :: cMatrix(:,:) 
real, intent(in) :: aMatrix(:,:), bMatrix(:,:) 
integer, intent(in) :: arrSize 
integer :: i,j,k; 
do j=1,arrSize 
    do k=1,arrSize 
        do i=1,arrSize 
            cMatrix(i,j) = cMatrix(i,j) + aMatrix(i,k) * bMatrix(k,j) 
        end do 
    end do 
end do 
end subroutine matrix_multiply
依存関係があるため、入れ替えが常に可能であるとは限らず、異なる結果を招く可能性があります。

Fortran 2008 の CONTIGUOUS 属性を使用します

形状引き継ぎ配列やポインターでループがユニットストライドと非ユニットストライド向けにマルチバージョン化されているが、マークされたループはユニットストライドなアクセスのみを行っている。CONTIGUOUS 属性は、形状引き継ぎ配列やポインターのターゲットが連続していることを示します。これにより、メモリーの連続したブロックを占有するオブジェクトのメモリーレイアウトに関連する最適化を容易にします。

real, pointer, contiguous :: ptr(:) 
real, contiguous :: arrayarg(:, :)

複数の呼び出しルーチンが関係する場合、形状引継ぎ配列およびポインターがメモリー内で常に連続していることをコンパイラーに知らせるには、インテル® Fortran コンパイラーのバージョン 18 以降で利用可能な次のオプションを使用します。

タイプ Windows* Linux*
形状引継ぎ配列 /assume:contiguous_assumed_shape -assume contiguous_assumed_shape
ポインター /assume:contiguous_pointer -assume contiguous_pointer

アサーションの使い方が誤っていたり、実行時にデータが連続していない場合、結果は不確定となり誤った結果やセグメンテーション違反につながる可能性があります。連続したポインター割り当てのターゲットがメモリー上で実際に連続しているか確認するには、インテル® Fortran コンパイラーのバージョン 18 以降で利用可能な次のオプションを使用します。
Windows* Linux*
/check:contiguous -check contiguous

$ ifort -DCONTIG -check contiguous -traceback 
forrtl: severe (408): 
fort: (32): CONTIGUOUS 属性を持つポインターが、連続しないターゲットで作成されています。
この例では、コンパイラーは連続しないターゲットへの連続ポインターの割り当てを検出します。-traceback(Linux*)、/traceback(Windows* ) オプションは、不正な割り当てが発生した関数とソースファイルの行番号を特定します。このトレースバックを取得するには、デバッグオプション-g(Linux* および macOS*)、/Zi(Windows*) を使用してコンパイルする必要はありません。

関連情報:

AoS の代わりに SoA を使用します

配列は、通常のインデックスでアクセス可能な、連続したデータ項目の集合を含む最も一般的なデータ構造です。構造体配列 (AoS)、または配列構造体 (SoA) として構成することができます。AoS はカプセル化には最適ですが、ベクトル化処理の妨げになることがあります。解決方法: AoS の代わりに SoA を使用するデータ構成にコードを変更します。

関連情報:

FMA 命令が効率良く使用されていない可能性があります

現在のハードウェアは、乗算加算融合 (FMA) 命令が利用できる AVX2 命令セットアーキテクチャー (ISA) をサポートします。FMA 命令を使用してパフォーマンスを改善します。

可能であればベクトル化を強制します

ループには FMA 命令 (ベクトル化の利点が考えられる) が含まれていますが、ベクトル化されていません。解決するには、以下を確認します。

  • 対応するコンパイラーの診断メッセージを確認し、ベクトル化が可能であり、かつ利点があるかチェックします。
  • コンパイラーが想定する依存関係と実際の依存関係を区別する依存関係解析

関連情報:

strict 浮動小数点モデルを適用する場合、FMA 命令の生成を可能にします

静的解析は、ループが AVX2 ISA で使用可能な FMA 命令から利益が得られると推測しましたが、strict浮動小数点モデルは、デフォルトで FMA 命令の生成を無効にします。解決方法: この動作を fma コンパイラー・オプションを使用して変更します。

Windows* Linux*
/Qfma -fma または /Qfma

関連情報:

インテル® AVX2 ISA ターゲット

静的解析は、ループがインテル® AVX2 以降で使用可能な FMA 命令から利点が得られると推測しましたが、このループでは FMA 命令が実行されていません。解決方法: インテル® AVX2 固有コードを生成するには、

  • xCORE-AVX2 を使用します。または、インテル® AVX2 を含む複数の機能固有の自動ディスパッチ・コードを生成するには、
  • axCORE-AVX2 コンパイラー・オプションを使用します。インテル® AVX-512 固有コードを生成するには、
  • xCOMMON-AVX512 を使用します。または、インテル® AVX-512 を含む複数の機能固有の自動ディスパッチ・コードを生成するには、
  • axCOMMON-AVX512 コンパイラー・オプションを使用します。

コンパイラー・オプションは、CPU のマイクロアーキテクチャーによって異なります。
Windows* Linux*
/QxCORE-AVX2 または /QaxCORE-AVX2 -xCORE-AVX2 または -axCORE-AVX2
/QxCOMMON-AVX512 または /QaxCOMMON-AVX512 -xCOMMON-AVX512 または -axCOMMON-AVX512

関連情報:

xHost オプションの代わりに固有の ISA をターゲットにします

静的解析は、ループがインテル® AVX2 以降で使用可能な FMA 命令から利点が得られると推測しましたが、このループでは FMA 命令が実行されていません。解決方法: ホスト ISA による最適化の可能性を制限する xHost コンパイラー・オプションを使用する代わりに、次のコンパイラー・オプションを使用します。

  • xCORE-AVX2 を使用します。または、インテル® AVX2 を含む複数の機能固有の自動ディスパッチ・コードを生成するには、
  • axCORE-AVX2 コンパイラー・オプションを使用します。インテル® AVX-512 固有コードを生成するには、
  • xCOMMON-AVX512 を使用します。または、インテル® AVX-512 を含む複数の機能固有の自動ディスパッチ・コードを生成するには、
  • axCOMMON-AVX512 コンパイラー・オプションを使用します。

コンパイラー・オプションは、CPU のマイクロアーキテクチャーによって異なります。
Windows* Linux*
/QxCORE-AVX2 または /QaxCORE-AVX2 -xCORE-AVX2 または -axCORE-AVX2
/QxCOMMON-AVX512 または /QaxCOMMON-AVX512 -xCOMMON-AVX512 または -axCOMMON-AVX512

関連情報:

間接関数呼び出しがあります

ループ本体内の間接関数呼び出しは、コンパイラーがループをベクトル化するのを妨げます。間接呼び出しは、間接ジャンプとも呼ばれ、レジスターまたはメモリーから呼び出し先のアドレスを取得するのに対し、直接呼出しは引数から呼び出し先のアドレスを取得します。ループのベクトル化を強制しても間接呼び出しはシリアル化されます。

分岐予測の改善

64 ビット・アプリケーションでは、分岐ターゲットが分岐から 4 GB 以上離れている場合、分岐予測のパフォーマンスに悪影響を与えます。これは、アプリケーションが共有ライブラリーと分離されている場合に発生する可能性があります。解決方法: 次のことを行います。

  • Glibc ライブラリーをバ―ション 2.23 以降にアップグレードします。
  • 環境変数 export LD_PREFER_MAP_32BIT_EXEC=1 を使用します。

関連情報:

ループ内の間接呼び出しを排除します

間接関数やサブルーチン呼び出しはベクトル化できません。解決方法: ループ内では間接呼び出しを避けます。

SIMD 対応関数が効率良く使用されていません

SIMD 対応関数のデフォルトのベクトル宣言では、余分な計算や非効率なメモリー・アクセス・パターンをもたらす可能性があります。適切な節を追加してパフォーマンスを改善します。

参照値を linear として指定します

Fortran アプリケーションでは、デフォルトでスカラー引数は参照渡しです。そのため、SIMD 対応関数では、引数は単一アドレスではなく、アドレスのショートベクトルとして渡されます。そして、ショートベクトルからデータをギャザーして、後続のベクトル演算で使用するショートベクトル値を作成します。このギャザーはパフォーマンスを低下します。解決方法: ベクトル宣言に REF 修飾子を持つ LINEAR 節を指定します (OpenMP* 4.5 でサポート)。指示節 LINEAR (REF(linear-list[: linear-step])) !$OMP DECLARE SIMD ディレクティブに追加します。

関連情報:

固有のプロセッサー・タイプをターゲットとします

SIMD 対応関数のデフォルト命令セット・アーキテクチャー (ISA) は、レジスター間との余分なメモリーアクセスが生じる可能性があるため、ホスト・プロセッサーでは非効率です。解決方法: 次のいずれかを追加してコンパイラーにベクトル関数の拡張セットを生成することを通知します。

Windows* Linux*
!$OMP DECLARE SIMD の PROCESSOR(cpuid) !$OMP DECLARE SIMD の PROCESSOR(cpuid)
/Qvecabi:cmdtarget 注意: コンパイラー・オプション /Qx または /Qax によって指定されるターゲットに対応する複数のベクトル関数が生成されます。 -vecabi=cmdtarget 注意: コンパイラー・オプション -x または -ax で指定されるターゲットに対応する複数のベクトル関数が生成されます

関連情報:

ベクトル依存関係を無視するようにコンパイラーに指示します

実際の依存関係は検出されなかったため競合検出命令を使用する必要がありません。解決方法: !DIR$ IVDEP ディレクティブを使用して、ベクトル化が安全であることをコンパイラーに知らせます。

この解決方法は他の状況では危険である可能性があります。不正な結果を避けるため慎重に使用してください。

!DIR$ IVDEP 
do i = 1, N 
    a(index(i)) = b(i) * c 
enddo

関連情報:

外部ループのベクトル化の可能性

これは外部 (最も内側ではない) ループです。通常外部ループは自動ベクトル化の対象とはありません。外部ループのベクトル化は可能であり有益なこともありますが、OpenMP* API やインテル® Cilk™ Plus を使用した明示的なベクトル化が必要です。

トリップ・カウント・データを収集

サーベイレポートでは、外部ループのベクトル化の有益性を証明するトリップカウントデータが不足しています。解決方法: トリップカウント解析を実行します。

外部ループの依存関係を調査します

依存関係の有無を認識せずにベクトル化を強制するのは安全とは言えません。依存関係を調査する前に内部ループのベクトル化を無効にします。確認するには: 依存関係解析を実行します。

外部ループのメモリー・アクセス・パターンをチェックします

外部ループが適切なメモリー・アクセス・パターンを持っているか確認するには、メモリー・アクセス・パターン解析を実行します。

外部ループのベクトル化を検討してください

コンパイラーは最内ループ以外をベクトル化のターゲットとしないため、内部ループがベクトル化できれば外部ループはベクトル化されません。しかし、より適切なメモリー・アクセス・パターン、髙いトリップカウント、またはより良い依存関係構成により、外部ループのベクトル化が有益である可能性があります。

外部ループをベクトル化します。

ターゲット ディレクティブ
外部ループ !$OMP SIMD
内部ループ !$OMP NOVECTOR

!$OMP SIMD 
DO I=1,N 
!$OMP NOVECTOR 
    DO J=1,N 
        SUM = SUM + A(i)*A(j) 
    ENDDO 
ENDDO

関連情報:

外部ループのベクトル化を検討してください

コードの複雑性がコンパイラーが判断できる基準を超えているため、コンパイラーはループをベクトル化することができませんでした。ループのベクトル化を実施できれば高いパフォーマンスを得られる可能性があります。ソースのループブロックの前で、適切なディレクティブを使用します。

ICL/ICC/ICPC ディレクティブ
!$OMP SIMD

関連情報:

外部ループのベクトル化を検討してください

依存関係の可能性が検出されたため、コンパイラーは内部ループをベクトル化できませんでした。依存関係がなければ外部ループをベクトル化します。ソースのループブロックの前で、適切なディレクティブを使用します。

ICL/ICC/ICPC ディレクティブ
!$OMP SIMD

関連情報:

逆数近似命令が効率良く使用されていない可能性があります

現在のハードウェアは、単精度と倍精度の逆数および逆数平方根命令が使用できるインテル® アドバンスト・ベクトル・エクステンション 512 (インテル® AVX-512) 命令をサポートしています。これらの命令を使用してパフォーマンスを改善します。これらの命令を使用してパフォーマンスを改善します。

可能であればベクトル化を強制します

ループには SQRT/DIV 命令 (ベクトル化の利点が考えられる) が含まれていますが、ベクトル化されていません。解決するには、以下を確認します。

  • 対応するコンパイラーの診断メッセージを確認し、ベクトル化が可能であり、かつ利点があるかチェックします。
  • コンパイラーが想定する依存関係と実際の依存関係を区別する依存関係解析

関連情報:

インテル® AVX-512 ISA ターゲット

静的解析は、インテル® AVX-512 の指数および逆数 (インテル® AVX-512ER) 命令からループが利点を得られると推測しましたが、これらの命令は使用されていません。解決方法: 以下のいずれかのコンパイラー・オプションを使用します。

  • xCOMMON-AVX512- インテル® AVX-512 を含む生成する命令セットと最適化、ターゲットとするプロセッサー機能をコンパイラーに指示します。
  • axCOMMON-AVX512- パフォーマンス上の利点がある場合、インテル® プロセッサー向けに複数の機能固有の自動ディスパッチ・コード・パスを生成するようにコンパイラーに指示します。

Windows* Linux*
/QxCOMMON-AVX512 または /QaxCOMMON-AVX512 -xCOMMON-AVX512 または -axCOMMON-AVX512

関連情報:

インテル® AVX-512 指数および逆数命令 ISA をターゲットにします

静的解析は、インテル® AVX-512 指数および逆数 (インテル® AVX-512ER) 命令からループが利点を得られると推測しました。この命令は現在インテル® Xeon Phi™ プロセッサーでのみサポートされていますが、このループでは使用されていませんでした。解決方法: 以下のいずれかのコンパイラー・オプションを使用します。

  • xMIC-AVX512- インテル® AVX-512ER を含む生成する命令セットと最適化、ターゲットとするプロセッサー機能をコンパイラーに指示します。
  • axMIC-AVX512- パフォーマンス上の利点がある場合、インテル® プロセッサー向けに複数の機能固有の自動ディスパッチ・コード・パスを生成するようにコンパイラーに指示します。

Windows* Linux*
/QxMIC-AVX512 または /QaxMIC-AVX512 -xMIC-AVX512 または -axMIC-AVX512

関連情報:

精度と浮動小数点モデルのコンパイラー・オプションを調整することで、逆数近似命令の使用を可能にします

静的解析は、逆数近似命令からループが恩恵を得られることを推測しましたが、精度と浮動小数点モデルを設定するコンパイラー・オプションがこの命令の生成を妨げている可能性があります。解決方法: 次のコンパイラー・オプションで調整します。

Windows* Linux* コメント
/fp -fp-model -fp-model=preciseは、逆数近似命令の使用を抑制します。
/Qimf-precision -fimf-precision 代わりに-fimf-precision=mediumまたは-fimf-precision=lowの利用を検討してください。
/Qimf-accuracy-bits -fimf-accuracy-bits この設定を減らすことを考慮してください。
/Qimf-max-error -fimf-max-error

この設定を減らすことを考慮してください。

同様のオプションもあります。-fimf-absolute-errorディレクティブを使用します。同時に両方のオプションの使用しないでください。

/Qimf-absolute-error -fimf-absolute-error 代わりに-fimf-max-errorを使用して、-fimf-absolute-error=0に設定するか (デフォルト)、この設定を-fimf-max-errorと同時に指定して設定を増やすことを考慮してください。
/Qimf-domain-exclusion -fimf-domain-exclusion この設定を減らすことを考慮してください。クラスを除外するとコードの最適化が可能となります。注意して使用してください。アプリケーションが使用する計算がドメインから除外されていると、このオプションにより不正確な動作となる可能性があります。
/Qimf-arch-consistency -fimf-arch-consistency -fimf-arch-consistency=trueは、逆数近似命令の使用を抑制します。
/Qprec-div -prec-div -prec-divは、逆数近似命令の使用を抑制します。
/Qprec-sqrt -prec-sqrt -prec-sqrtは、逆数近似命令の使用を抑制します。

関連情報:

非効率な競合検出命令が存在する可能性があります

間接アドレスによるストアで、コンパイラーは潜在的な依存関係を想定しました。

これは SIMD 処理において、AVX-512 vpconflict 命令などの競合検出命令が使用され、ベクトルと生成された競合無しのサブセット内で複製された値が検出された結果によるものです。競合検出命令を排除することでパフォーマンスを改善できます。

ベクトル依存関係を無視するようにコンパイラーに指示します

実際の依存関係は検出されなかったため競合検出命令を使用する必要がありません。解決方法: !DIR$ IVDEP ディレクティブを使用して、ベクトル化が安全であることをコンパイラーに知らせます。

この解決方法は他の状況では危険である可能性があります。不正な結果を避けるため慎重に使用してください。

!DIR$ IVDEP 
do i = 1, N 
    a(index(i)) = b(i) * c 
enddo

関連情報:

最適化されていない浮動小数点操作の可能性があります

近似操作命令を有効にしてパフォーマンスを改善します。

近似除算命令の使用を可能にします

静的解析は、ループで近似計算を使用することで利点が得られると推測しました。個々の除算は事前計算され乗算に置き換えられます。解決方法: 次のコンパイラー・オプションで調整します。

Windows* Linux* コメント
/Qprec-div -no-prec-div -no-prec-div近似除算を使用した最適化を有効にします。

関連情報:

近似平方根命令の使用を可能にします

静的解析は、近似平方根 (sqrt) 命令からループが恩恵を得られることを推測しましたが、精度と浮動小数点モデルを設定するコンパイラー・オプションがこの命令の生成を妨げている可能性があります。解決方法: 次のコンパイラー・オプションで調整します。

Windows* Linux* コメント
/Qprec-sqrt -no-prec-sqrt -no-prec-sqrt近似平方根最適化の使用を可能にします。

関連情報:

過剰なキャッシュ使用の可能性があります

非テンポラルなストアを有効にします

!DIR$ vector nontemporal を使用して、非テンポラルなストアを有効にします。nontemporal 節は、特に明記されていない限り、サポートされているすべてのアーキテクチャー・ベースのシステムで、一時的ではない (ストリーミング) ストアを使用するようにコンパイラーに指示します。オプションでカンマ区切りの変数リストも指定できます。

ストリーミング・ストアは、特定のプロセッサーにおいて非ストリーミング・ストアよりも、大幅なパフォーマンスの向上をもたらす可能性があります。ただし、ストリーミング・ストアの使用を誤ると、パフォーマンスが大幅に低下します。

!DIR$ vector nontemporal 
do i=1,N 
    arr1(i) = 0 
end do

関連情報:

アライメントされていないループがあります

メモリー内にある現在のループ配置では、CPU フロントエンドの効率が低下する可能性があります。ループコードをアライメントすることでパフォーマンスを向上します。

コンパイラーにループコードのアライメントを強制します

コードを過度にアライメントすると、アプリケーションのバイナリーサイズが増加し、パフォーマンス低下につながる可能性があります。

静的解析は、ループのコード・アライメントから利点が得られると推測しました。解決方法: より細かな制御のためコンパイラー・ディレクティブを使用して、コンパイラーにループを 2 のべき乗バイトへのアライメントを強制します。!DIR$ CODE_ALIGN [:n]

内部ループを 32 バイト境界で配置します。

!DIR$ CODE_ALIGN :64 
do i = 1, n, 1 
    do j = 1, m, 1 
        a(i) = a(i) * (b(i) + c(j)) 
    enddo 
enddo

次のコンパイラー・オプションも指定する必要があります。

Windows* Linux* と macOS*
/Qalign-loops[:n] -falign-loops[=n]

n は、1 から 4096 の範囲内の 2 の累乗の値でなければなりません (例: 1、2、4、8、16、32、64 など)。n = 1 はアライメントを行わないことを指示します。n が省略されると、16 バイトのアライメントが使用されます。推奨: 16 と 32 を最初に試します。

/Qalign-loops--fno-align-loops、デフォルトのコンパイルオプションでは、特殊なループのアライメントは無効になります。