SIMD 拡張命令

Visual C++ は、OpenMP 2.0 標準を現在サポートしています。ただし、Visual Studio 2019 は、SIMD 機能も提供するようになりました。

Note

SIMD を使用するには、-openmp スイッチを使用するときに追加の OpenMP 機能を使用不可にできる -openmp:experimental スイッチでコンパイルします。

-openmp:experimental スイッチには -openmp が含まれています。これはすべての OpenMP 2.0 機能がその使用に含まれているという意味です。

詳細については、「Visual Studio の C++ OpenMP の SIMD 拡張機能」をご覧ください。

Visual C++ の OpenMP SIMD

OpenMP 4.0 標準で導入された OpenMP SIMD は、ベクター対応のループを作成することを目的としています。 ループの前にディレクティブを simd 使用することで、コンパイラはベクターの依存関係を無視し、ループを可能な限りベクターフレンドリにし、複数のループイテレーションを同時に実行するというユーザーの意図を尊重することができます。

    #pragma omp simd
    for (i = 0; i < count; i++)
    {
        a[i] = a[i-1] + 1;
        b[i] = *c + 1;
        bar(i);
    }

Visual C++ は、#pragma vector#pragma ivdep のような OpenMP 以外のループ プラグマを提供します。ただし、OpenMP SIMD では、コンパイラは次のようにさらに多くのことを実行できます。

  • 常に、存在するベクターの依存関係を無視できます。
  • /fp:fast がループ内で有効です。
  • 外側のループと関数呼び出しを使用したループはベクトル対応です。
  • 入れ子になったループは 1 つのループに結合して、ベクター対応にできます。
  • #pragma omp for simd を使用したハイブリッド アクセラレーションにより、粒度の粗いマルチスレッドおよび粒度の細かいベクトルが可能になります。

ベクター対応のループの場合、ベクター サポート ログ スイッチを使用しない限り、コンパイラはサイレント状態のままです。

    cl -O2 -openmp:experimental -Qvec-report:2 mycode.cpp
    mycode.cpp(84) : info C5002: Omp simd loop not vectorized due to reason '1200'
    mycode.cpp(90) : info C5002: Omp simd loop not vectorized due to reason '1200'
    mycode.cpp(96) : info C5001: Omp simd loop vectorized

ベクター対応でないループの場合、コンパイラは各メッセージを発行します。

    cl -O2 -openmp:experimental mycode.cpp
    mycode.cpp(84) : info C5002: Omp simd loop not vectorized due to reason '1200'
    mycode.cpp(90) : info C5002: Omp simd loop not vectorized due to reason '1200'

OpenMP SIMD ディレクティブでは、次の句を使用してベクター サポートを強化することもできます。

ディレクティブ 説明
simdlen(length) ベクター レーンの数を指定します。
safelen(length) ベクターの依存関係の距離を指定します。
linear(list[ : linear-step]) ループ誘導変数から配列サブスクリプションへの線形マッピング。
aligned(list[ : alignment]) データの位置揃えです。
private(list) データのプライベート化を指定します。
lastprivate(list) 最後のイテレーションの最終値を使用して、データのプライベート化を指定します。
reduction(reduction-identifier:list) カスタマイズされた削減操作を指定します。
collapse(n) 合体ループの入れ子です。

Note

有効でない SIMD 句は、コンパイラによって解析され、無視され、警告が出されます。

たとえば、次のコードを使用すると、警告が発行されます。

   #pragma omp simd simdlen(8)
   for (i = 0; i < count; i++)
   {
       a[i] = a[i-1] + 1;
       b[i] = *c + 1;
       bar(i);
   }
   warning C4849: OpenMP 'simdlen' clause ignored in 'simd' directive

OpenMP SIMD ディレクティブを使用すると、ループをベクトル対応にするようにコンパイラに指示できます。 OpenMP SIMD ディレクティブを使用してループに注釈を付けることで、ユーザーは複数のループ イテレーションを同時に実行させることができます。

たとえば、次のループには OpenMP SIMD ディレクティブで注釈が付きます。 [i] から [i-1] への後方依存関係があるため、ループ イテレーション間に完全な並列処理はありません。ただし、SIMD ディレクティブにより、コンパイラは最初のステートメントの連続する反復処理を 1 つのベクター命令にパックし、並列で実行することができます。

    #pragma omp simd
    for (i = 0; i < count; i++)
    {
        a[i] = a[i-1] + 1;
        b[i] = *c + 1;
        bar(i);
    }

したがって、次の変換されたベクター形式のループは、コンパイラが元のループの各イテレーションのシーケンシャル動作を維持するため、有効です。 つまり、a[i]a[-1] の後に実行され、b[i]a[i] の後に実行され、bar への呼び出しは最後に実行されます。

    for (i = 0; i < count; i+=4)
    {
        a[i:i+3] = a[i-1:i+2] + 1;
        b[i:i+3] = *c + 1;
        bar(i);
        bar(i+1);
        bar(i+2);
        bar(i+3);
    }

a[i] または b[i] を使用して別名を指定する可能性がある場合、メモリ参照 *c をループから移動することは無効です。 また、シーケンシャルな依存関係が壊れる場合は、元の 1 つのイテレーション内でステートメントを並べ替えるのも無効です。 たとえば、次の変換されたループは無効です。

    c = b;
    t = *c;
    for (i = 0; i < count; i+=4)
    {
        a[i:i+3] = a[i-1:i+2] + 1;
        bar(i);            // illegal to reorder if bar[i] depends on b[i]
        b[i:i+3] = t + 1;  // illegal to move *c out of the loop
        bar(i+1);
        bar(i+2);
        bar(i+3);
    }

関連項目

/openmp (OpenMP 2.0 サポートの有効化)