< 目次

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

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

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

データをアライメント

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

データを 64 バイト境界にアライメントし、コンパイラーにデータがアライメントされていることを知らせます。

float *array; 
array = (float *)_mm_malloc(ARRAY_SIZE*sizeof(float), 32); // Somewhere else __assume_aligned(array, 32); // Use array in loop 
_mm_free(array);

静的データを 64 バイト境界でアライメントします。

__declspec(align(64)) float array[ARRAY_SIZE]

関連情報:

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

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

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

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

void f(int a[], int b[], int c[]) { 
#pragma omp parallel for schedule(static) 
   for (int i = 0; i < n; i++) { 
      a[i] = b[i] + c[i]; 
   } 
}

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

void f(int a[], int b[], int c[]) { 
#pragma omp parallel for simd schedule(simd:static) 
   for (int i = 0; i < n; i++) { 
      a[i] = b[i] + c[i]; 
   } 
}

関連情報:

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

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

add_floats(float *a, float *b, float *c, float *d, float *e, int n) { 
int i; // リマインダー・ループをベクトル化しないようにコンパイラーに強制 
#pragma vector novecremainder 
   for (i=0; i<n; i++) { 
      a[i] = a[i] + b[i] + c[i] + d[i] + e[i]; 
   } 
}

関連情報:

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

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

add_floats(float *a, float *b, float *c, float *d, float *e, int n) { 
int i; // リマインダー・ループをベクトル化するようにコンパイラーに強制 
#pragma vector vecremainder 
   for (i=0; i<n; i++) { 
      a[i] = a[i] + b[i] + c[i] + d[i] + e[i]; 
   } 
}

関連情報:

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

コンパイラーは静的にトリップカウントを認識できません。解決方法: ディレクティブを使用して推測される反復数を指定します

#include <stdio.h> 
int mysum(int start, int end, int a) { 
   int iret=0; // Iterate through a loop a minimum of three, maximum of ten, and average of five times 
#pragma loop_count min(3), max(10), avg(5) 
   for (int i=start;i<=end;i++) 
      iret += a; 
   return iret; 
} 
int main() { 
   int t; 
   t = mysum(1, 10, 3); 
   printf("t1=%d\r\n",t); 
   t = mysum(2, 6, 2); 
   printf("t2=%d\r\n",t); 
   t = mysum(5, 12, 1); 
   printf("t3=%d\r\n",t); 
}

関連情報:

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

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

void f(int a[], int b[], int[c]) { 
// Guarantee a multiple of vector length.#pragma omp parallel for simd schedule(simd: static) 
   for (int i = 0; i < n; i++) { 
      a[i] = b[i] + c[i]; 
   } 
}

関連情報:

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

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

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

関連情報:

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

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

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

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

void nounroll(int a[], int b[], int c[], int d[]) {
// Disable automatic loop unrolling using
  #pragma nounroll
  for (int i = 1; i < 100; i++) {
    b[i] = a[i] + 1;
    d[i] = c[i] + 1;
  } 
}

関連情報:

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

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

void f(int a[], int b[], int c[], int d[]) { 
// Specify vector length using 
  #pragma omp simd simdlen(4)
  for (int i = 1; i < 100; i++) {
    b[i] = a[i] + 1;
    d[i] = c[i] + 1;
  } 
}

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

void f(int a[], int b[], int c[], int d[]) { 
// ベクトル長のリストを指定 
#pragma vector vectorlength(2, 4, 16) 
    for (int i = 1; i < 100; i++) { 
        b[i] = a[i] + 1; 
        d[i] = c[i] + 1; 
    } 
}

関連情報:

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

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

...#pragma vector nodynamic_align 
for (int i = 0; i < len; i++) 
    ... 
void f(float * a, float * b, float * c, int len) { 
#pragma vector nodynamic_align 
    for (int i = 0; i < len; i++) { 
        a[i] = b[i] * c[i]; 
    }
}

関連情報:

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

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

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

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

Windows*

Linux*

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

関連情報:

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

#pragma omp declare simd 
int f (int x) { return x+1; } 
#pragma omp simd 
for (int k = 0; k < N; k++) { 
    a[k] = f(k); 
}

関連情報:

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

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

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

インライン展開はコンパイラーによって無効化されています。解決方法: インライン展開を制御するため、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

    これは浮動小数点の精度を下げます。
  • ディレクティブを使用してループのベクトル化を強制します。#pragma omp simd
void add_floats(float *a, float *b, float *c, float *d, float *e, int n){
  int i; 
#pragma omp simd
  for (i=0; i<n; i++){
    a[i] = a[i] + b[i] + c[i] + d[i] + e[i];
  } 
}

関連情報:

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

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

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

    Windows* OS

    Linux*

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

    これは浮動小数点の精度を下げます。
  • より積極的な最適化を有効にするには、precise浮動小数点モデルを使用して、ディレクティブを使用してループのベクトル化を強制します。#pragma omp simd
gcc program.c -O2 -fopenmp -fp-model precise -fast-transcendentals 
#pragma omp simd collapse(2) 
for (i=0; i<N; i++) { 
    a[i] = b[i] * c[i]; 
    for (i=0; i<N; i++) { 
        d[i] = e[i] * f[i]; 
    } 
}

関連情報:

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コンパイラー・オプションを使用して対象のバージョンを指定します。
gcc program.c -O2 -fopenmp -ffast-math -lrt -lm -mavx2 -I/opt/glibc-2.22/include -L/opt/glibc-2.22/lib -Wl,--dynamic-linker=/opt/glibc-2.22/lib/ld-linux-x86-64.so.2 
#include "math.h" 
#include "stdio.h" 
#define N 100000 
int main() { 
double angles[N], results[N]; 
int i; 

    srand(86456); 
    for (i = 0; i < N; i++) { 
        angles[i] = rand(); 
    }  
#pragma omp simd 
    for (i = 0; i < N; i++) { 
        results[i] = cos(angles[i]); 
    } 
    return 0; 
}

関連情報:

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

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

  • リンク時に SVML ABI 互換ライブラリーを使用するには、-mveclibabi=svmlコンパイラー・オプションを使用して、ベクトル組込み関数にインテル® ショートベクトル数学ライブラリー ABI タイプを指定します。
  • ベクトル数学関数を有効にするには、-ftree-vectorize および -funsafe-math-optimizations コンパイラー・オプションを使用します。
  • リンク時に SVML ABI 互換ライブラリーを使用するには、-L/path/to/intel/lib と -lsvml コンパイラー・オプションを使用します。
gcc program.c -O2 -ftree-vectorize -funsafe-math-optimizations -mveclibabi=svml -L/opt/intel/lib/intel64 -lm -lsvml -Wl,-rpath=/opt/intel/lib/intel64 

#include "math.h" 
#include "stdio.h" 
#define N 100000 

int main() { 
double angles[N], results[N]; 
int i; 

    srand(86456); 
    for (i = 0; i < N; i++) { 
        angles[i] = rand(); 
    } 
// ループは自動ベクトル化されます 
    for (i = 0; i < N; i++) { 
        results[i] = cos(angles[i]); 
    } 
    return 0; 
}

関連情報:

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

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

パターン

説明

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

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

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

変数

パターン

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

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

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

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

しかし、AoS から SoA への移行コストがパフォーマンスの利点を上回る可能性があります。解決方法: インテル® C++ コンパイラーのバージョン 16.1 で導入されたインテル® SIMD Data Layout Templates (SDLT) を使用して、移行のコストを軽減できます。SDLT は、C++11 テンプレート・ライブラリーであり、コードの変更が数行で済みます。

垂直不変パターンのリファクタリング。

// main.cpp 
int a[8] = {1,0,5,7,4,2,6,3}; 
// gather.cpp 
void test_gather(int* a, int* b, int* c, int* d) { 
int i, k; // 非効率なアクセス 
#pragma omp simd 
for (i = 0; i < INNER_COUNT; i++) 
    d[i] = b[a[i%8]] + c[i]; 
int b_alt[8]; 
for (k = 0; k < 8; ++k) 
    b_alt[k] = b[a[k]]; // 効率良いバージョン 
    for (i = 0; i < INNER_COUNT/8; i++) { 
#pragma omp simd 
        for(k = 0; k < 8; ++k) 
            d[i*8+k] = b_alt[k] + c[i*8+k]; 
    } 
}
ベクトル関数節がループ内の呼び出しの引数と一致していることを確認します (存在する場合)。

複数の#pragma declare simdディレクティブを使用して、コンパイラーに複数のベクトル関数を生成を指示できます。

関数呼び出しと宣言を比較します。

// functions.cpp 
#pragma omp declare simd 
int foo1(int* arr, int idx) { 
    return 2 * arr[idx]; 
} 
#pragma omp declare simd uniform(arr) linear(idx) 
int foo2(int* arr, int idx) { 
    return 2 * arr[idx]; 
} 
#pragma omp declare simd linear(arr) uniform(idx) 
int foo3(int* arr, int idx) { 
    return 2 * arr[idx]; 
} 
// gather.cpp 
void test_gather(int* a, int* b, int* c) { 
int i, k; // 複雑なアクセスパターンを関数呼び出しで使用できるよう、ループはベクトル化されます。#pragma omp simd 
    for (i = 0; i < INNER_COUNT; i++) 
        a[i] = b[i] + foo1(c,i); // ループはベクトル化された呼び出しでベクトル化されます 
#pragma omp simd 
    for (i = 0; i < INNER_COUNT; i++) 
        [i] = b[i] + foo2(c,i); // ループはシリアル呼び出しでベクトル化されます 
#pragma omp simd 
    for (i = 0; i < INNER_COUNT; i++) 
        a[i] = b[i] + foo3(c,i); 
}

関連情報:

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

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

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

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

void nounroll(int a[], int b[], int c[], int d[]) {
  #pragma nounroll
  for (int i = 1; i < 100; i++) {
    b[i] = a[i] + 1;
    d[i] = c[i] + 1;
  } 
}

関連情報:

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

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

#define NUM 1024 
void loop_distribution_pragma2(
       double a[NUM], double b[NUM], double c[NUM],
       double x[NUM], double y[NUM], double z[NUM] ) {
  int i;

  // ループの分割または分割後
  for (i=0; i< NUM; i++) {
    a[i] = a[i] + i;
    b[i] = b[i] + i;
    c[i] = c[i] + i;
    x[i] = x[i] + i;
    y[i] = y[i] + i;
    z[i] = z[i] + i;
  } 
}

関連情報:

依存関係が想定されます

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

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

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

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

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

Target

ディレクティブ

#pragma omp simd ループのすべての依存関係を無視。
#pragma ivdep ベクトルの依存関係のみを無視 (最も安全)
#pragma ivdep 
for (i = 0; i < n - 4; i += 4) { 
// ここに簡単なサンプルコードを示すコメント行が 
// もう 1 行あります
    ... 
    a[i + 4] = a[i] * c; 
}

関連情報:

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

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

依存関係の解決

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

  • アンチ依存関係が存在する場合、ディレクティブ#pragma omp simd safelen(length)を使用してベクトル化を有効にします。ここでlengthは、アンチ依存関係がある反復間の距離よりも小さな値です。
    #pragma omp simd safelen(4) 
    for (i = 0; i < n - 4; i += 4) { 
        a[i + 4] = a[i] * c; 
    }
  • ループ内にリダクションによる依存関係が存在する場合、#pragma omp simd reduction(operator:list) ディレクティブを使用します。
    #pragma omp simd reduction(+:sumx) 
    for (k = 0;k < size2; k++) { 
        sumx += x[k]*b[k]; 
    }
  • コードを変更して依存関係を排除します。変数のプライベート化などのプログラミング手法を使用します。

関連情報:

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

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

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

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

例: 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 ディレクティブ
    元のループ #pragma omp simd
    内部関数定義や宣言 #pragma omp declare simd
#pragma omp declare simd 
int f (int x) { return x+1; } 
#pragma omp simd 
for (int k = 0; k < N; k++) { 
    a[k] = f(k); 
}

関連情報:

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

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

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

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

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

関連情報:

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

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

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

関連情報:

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

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

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

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

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

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

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

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

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

    Target

    ディレクティブ

    その他 #pragma omp parallel [clause, clause, ...]
    内部 #pragma omp for [clause, clause, ...]
  2. 可能であれば OpenMP API 呼び出しをループ外部へ移動します。

オリジナルコードの例:

#pragma omp parallel for private(tid, nthreads) 
for (int k = 0; k < N; k++) { 
    tid = omp_get_thread_num(); // ループ内のこの呼び出しはベクトル化を妨げます 
    nthreads = omp_get_num_threads(); // ループ内のこの呼び出しはベクトル化を妨げます 
    ...}

修正されたコードの例:

#pragma omp parallel private(tid, nthreads) { 
// OpenMP* 呼び出しをここへ移動します 
tid = omp_get_thread_num(); 
nthreads = omp_get_num_threads(); 
#pragma omp for nowait 
    for (int k = 0; k < N; k++) { 
        ...} 
}

関連情報:

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

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

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

オリジナルコードの例:

int A[n]; 
list<int> L; 
... 
omp_lock_t lock_obj; 
omp_init_lock(&lock_obj); 
#pragma omp parallel for shared(L, A, lock_obj) default(none) 
for (int i = 0; i < n; ++i) { 
// A[i] calculation ... 
    if (A[i]<1.0) { 
       omp_set_lock(&(lock_obj)); 
       L.insert(L.begin(), A[i]); 
       omp_unset_lock(&(lock_obj)); 
   } 
} 
omp_destroy_lock(&lock_obj);

修正されたコードの例:

int A[n]; 
list<int> L; 
omp_set_num_threads(nthreads_all); 
... 
vector<list<int>> L_by_thread(nthreads_all); 
// スレッドごとの個別のリスト 
#pragma omp parallel shared(L, L_by_thread, A) default(none) { 
int k = omp_get_thread_num(); 
#pragma omp for nowait 
    for (int i = 0; i < n; ++i) { 
    // A[i] calculation ... 
        if (A[i]<1.0) { 
            L_by_thread[k].insert(L_by_thread[k].begin(), A[i]); 
        } 
    } 
} // merge data into single list 
for (int k = 0; k < L_by_thread.size(); k++) { 
    L.splice(L.end(), L_by_thread[k]); 
}

関連情報:

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

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

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

非効率なメモリー・アクセス・パターンをの存在を確認する方法がありません。解決方法: メモリー使用とトラフィックを調査。

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

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

ループの並べ替え

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

オリジナルコードの例:

void matmul(float *a[], float *b[], float *c[], int N) { 
for (int i = 0; i < N; i++) 
    for (int j = 0; j < N; j++) 
        for (int k = 0; k < N; k++) 
            c[i][j] = c[i][j] + a[i][k] * b[k][j]; 
}
修正されたコードの例:
void matmul(float *a[], float *b[], float *c[], int N) { 
for (int i = 0; i < N; i++) 
    for (int k = 0; k < N; k++) 
        for (int j = 0; j < N; j++) 
            c[i][j] = c[i][j] + a[i][k] * b[k][j]; 
}
依存関係があるため、入れ替えが常に可能であるとは限らず、異なる結果を招く可能性があります。

インテル® SDLT を使用します

AoS から SoA への移行コストがパフォーマンスの利点を上回る可能性があります。解決方法: コストを軽減するには、インテル® SIMD データ・レイアウト・テンプレート (インテル® SDLT) を使用します。SDLT は、C++11 テンプレート・ライブラリーであり、コードの変更が数行で済みます。

STL コンテナーに代わり SDLT を使用することで、効率良いベクトル処理向けにメモリー・アクセス・パターンを改善します。

オリジナルコードの例:

struct kValues { float Kx; float Ky; float Kz; float PhiMag; }; 
std::vector<kValues> dataset(count); 
// 初期化ステップ 
for(int i=0; i < count; ++i) { 
    kValues[i].Kx = kx[i]; 
    kValues[i].Ky = ky[i]; 
    kValues[i].Kz = kz[i]; 
    kValues[i].PhiMag = phiMag[i]; 
} 
// 計算ステップ 
for (indexK = 0; indexK < numK; indexK++) { 
    expArg = PIx2 * (kValues[indexK].Kx * x[indexX] + kValues[indexK].Ky * y[indexX] + kValues[indexK].Kz * z[indexX]); 
    cosArg = cosf(expArg); 
    sinArg = sinf(expArg); 
    float phi = kValues[indexK].PhiMag;
    QrSum += phi * cosArg; 
    QiSum += phi * sinArg; 
}

修正されたコードの例:

#include <sdlt/sdlt.h> 
struct kValues { float Kx; float Ky; float Kz; float PhiMag; }; 
SDLT_PRIMITIVE(kValues, Kx, Ky, Kz, PhiMag) sdlt::soa1d_container<kValues> dataset(count); 
// 初期化ステップ 
auto kValues = dataset.access(); 
for (k = 0; k < numK; k++) { 
    kValues [k].Kx() = kx[k]; 
    kValues [k].Ky() = ky[k]; 
    kValues [k].Kz() = kz[k]; 
    kValues [k].PhiMag() = phiMag[k]; 
} 
// 計算ステップ 
auto kVals = dataset.const_access(); 
#pragma omp simd private(expArg, cosArg, sinArg) reduction(+:QrSum, QiSum) 
for (indexK = 0; indexK < numK; indexK++) { 
    expArg = PIx2 * (kVals[indexK].Kx() * x[indexX] + kVals[indexK].Ky() * y[indexX] + kVals[indexK].Kz() * z[indexX]); 
    cosArg = cosf(expArg); 
    sinArg = sinf(expArg); 
    float phi = kVals[indexK].PhiMag(); 
    QrSum += phi * cosArg; 
    QiSum += phi * sinArg; 
}

関連情報:

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

関連情報:

より上位の ISA ターゲット

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

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

コンパイラー・オプションは、CPU のマイクロアーキテクチャーによって異なります。

関連情報:

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 以降にアップグレードします。
  • 環境変数 LD_PREFER_MAP_32BIT_EXEC=1 ディレクティブを使用します。

関連情報:

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

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

仮想メソッド呼び出しを直接呼出しに置き換えます

関数のアドレスがランタイムで計算されるため、仮想メソッド呼び出しは常に間接呼び出しとなります。解決するには次を行います。

  • SIMD 命令を使用して元のループのベクトル化を強制し、および/またはディレクティブを使用して SIMD バージョンの関数を生成します。

    Target

    ディレクティブ

    元のループ #pragma omp simd
    内部関数定義や宣言 #pragma omp declare simd
  • インテル® コンパイラーを17.x以降にアップデートします。または、仮想メソッドを直接関数呼び出しに置き換えます。

オリジナルコードの例:

struct A { virtual double foo(double x) { 
    return x+1; 
    } 
}; 
struct B : public A { double foo(double x) override { 
    return x-1; 
    } 
}; 
...A* obj = new B(); 
double sum = 0.0; 
#pragma omp simd reduction(+:sum) 
for (int k = 0; k < N; ++k) { 
// 仮想間接呼び出し 
    sum += obj->foo(a[k]); 
} 
...

修正されたコードの例:

struct A { 
// インテル® コンパイラー 17.x 以降では、仮想メソッドの呼び出しをベクトル化できる可能性があります 
#pragma omp declare simd 
virtual double foo(double x) { return x+1; } }; 
... 
sum = 0.0; 
#pragma omp simd reduction(+:sum) 
for (int k = 0; k < N; ++k) { 
// インテル® コンパイラー 16.x 以前の手順: // 呼び出されるメソッドが判明している場合、
// 仮想呼び出しを直接呼び出しに置き換えます 
    sum += ((B*)obj)->B::foo(a[k]); 
} 
...

関連情報:

仮想メソッド呼び出しのベクトル化

SIMD 命令を使用して元のループのベクトル化を強制し、および/またはディレクティブを使用してベクトルバージョンの関数を生成します。

Target

ディレクティブ

元のループ #pragma omp simd
内部関数定義や宣言 #pragma omp declare simd

オリジナルコードの例:

struct A { virtual double foo(double x) { 
    return x+1; 
    } 
}; 
struct B : public A { double foo(double x) override { 
    return x-1; 
    } 
}; 
...A* obj = new B(); 
double sum = 0.0; 
#pragma omp simd reduction(+:sum) 
for (int k = 0; k < N; ++k) { 
// 仮想メソッドの間接呼び出し 
    sum += obj->foo(a[k]); 
} 
...

修正されたコードの例:

struct A { 
#pragma omp declare simd 
    virtual double foo(double x) { 
        return x+1; 
    } 
}; 
...

関連情報:

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

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

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

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

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

関連情報:

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

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

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

#pragma ivdep 
for (i = 0; i < n; i++) { 
    a[index[i]] = b[i] * c; 
}

関連情報:

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

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

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

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

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

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

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

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

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

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

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

ターゲット ディレクティブ
外部ループ #pragma omp simd
内部ループ #pragma novector
これは外部ループをベクトル化する可能性に関を示すものであり、利点を立証するためさらに詳しい解析が必要です (MAP、トリップカウント、依存関係)。

#pragma omp simd 
for(i=0; i<N; i++) { 
#pragma novector 
    for(j=0; j<N; j++) { 
        sum += A[i]*A[j]; 
    } 
}

関連情報:

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

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

ICL/ICC/ICPC ディレクティブ
#pragma omp simd

関連情報:

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

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

ICL/ICC/ICPC ディレクティブ
#pragma omp simd

関連情報:

STL アルゴリズムの使用

STL アルゴリズムはアルゴリズム的には最適化されています。Parallel STL によって、アルゴリズム的とプログラム的の両面で最適化されたアルゴリズムを使用して、パフォーマンスを向上させます。Parallel STL は、実行ポリシーをサポートし、インテル® プロセッサー向けに最適化された、C++17 と呼ばれる次期 C++ 標準ライブラリー・アルゴリズムの実装です。アルゴリズムを呼び出す最初のパラメーターとして、次のいずれかの値を渡して実行ポリシーを指定します。

実行ポリシー 意味
seq シーケンシャルに実行。
unseq SIMD を使用。(SIMD セーフ関数が必要)
par マルチスレッドを使用。(スレッドセーフな関数が必要)
par_unseq SIMD とマルチスレッドを使用。(SIMD セーフとスレッドセーフな関数が必要)

Parallel STL は、ランダム・アクセス・イテレーターが提供されている場合、アルゴリズムのサブセットで SIMD およびマルチスレッド実行ポリシーをサポートします。その他すべてのアルゴリズムはシーケンシャルに実行されます。

std::any_of の代わりに Parallel STL を使用する

std::any_of アルゴリズムはシーケンシャルに実行されます。並列実行するには、次の実行ポリシーのいずれかを使用して Parallel STL を使用します。std::execution::unseq

#include "pstl/execution" 
#include "pstl/algorithm" 
void foo(float* a, int n) { 
    std::any_of(std::execution::unseq, a, a+n, [](float elem) { 
        return elem > 100.f; 
    }); 
}

関連情報:

std::copy_if の代わりに Parallel STL を使用する

std::copy_if アルゴリズムはシーケンシャルに実行されます。並列実行するには、次の実行ポリシーのいずれかを使用して Parallel STL を使用します。

  • std::execution::par
  • std::execution::par_unseq

#include "pstl/execution" 
#include "pstl/algorithm" 
void foo(float* a, float* b, int n) { 
    std::copy_if(std::execution::par_unseq, a, a+n, b, [](float elem) { 
        return elem > 10.f; 
    }); 
}

関連情報:

std::for_each の代わりに Parallel STL を使用する

std::for_each アルゴリズムはシーケンシャルに実行されます。並列実行するには、次の実行ポリシーのいずれかを使用して Parallel STL を使用します。

  • std::execution::par
  • std::execution::par_unseq

#include "pstl/execution" 
#include "pstl/algorithm" 
void foo(float* a, int n) { 
    std::for_each(std::execution::par_unseq, a, a+n, [](float elem) { 
        ...     }); 
}

関連情報:

std::sort の代わりに Parallel STL を使用する

std::any_of アルゴリズムはシーケンシャルに実行されます。並列実行するには、次の実行ポリシーのいずれかを使用して Parallel STL を使用します。std::execution::par

#include "pstl/execution" 
#include "pstl/algorithm" 
void foo(float* a, int n) { 
    std::sort(std::execution::par, a, a+n); 
}

関連情報:

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

現在のハードウェアは、単精度と倍精度の逆数および逆数平方根命令が使用できるインテル® アドバンスト・ベクトル・エクステンション 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 命令などの競合検出命令が使用され、ベクトルと生成された競合無しのサブセット内で複製された値が検出された結果によるものです。競合検出命令を排除することでパフォーマンスを改善できます。

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

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

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

#pragma ivdep 
for (i = 0; i < n; i++) { 
    a[index[i]] = b[i] * c; 
}

関連情報:

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

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

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

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

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

関連情報:

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

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

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

関連情報:

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

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

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

このプラグマを指定する場合、単一または複数のスレッド内で正しいメモリーの順序設定が徹底されるように、必要なフェンスを挿入する必要があります。一般的は方法は、コンパイラーがストリーミング・ストアを使用すると思われるループ (初期化ループなど) の直後に、 _mm_sfence 組込み関数呼び出しを挿入することです。

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

float a[1000]; 
void foo(int N){
  int i;
  #pragma vector nontemporal
  for (i = 0; i < N; i++) {
    a[i] = 1;
  } 
}

関連情報:

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

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

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

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

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

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

for (i = 0; i < n; i++) { 
#pragma code_align 32 
    for (j = 0; j < m; j++) { 
        a[i] *= b[i] + c[j]; 
    } 
}

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

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、デフォルトのコンパイルオプションでは、特殊なループのアライメントは無効になります。