C++ クラスと SIMD 演算

SIMD 演算用の C++ クラスは、配列 (ベクトルデータ) を並列処理するときに使用するのが基本です。例として、2 つのベクトル AB の加算を考えてみます。各ベクトルは 4 つの要素で構成されているとします。整数ベクトル (Ivec) クラスを使用して、各配列から取り出した要素 A[i]B[i] を下の例のように加算します。

ループを使用して複数の要素を加算するときの一般的な方法

short a[4], b[4], c[4];
for (i=0; i<4; i++) /* needs four iterations */
c[i] = a[i] + b[i]; /* returns c[0], c[1], c[2], c[3] *

次の例を見ると、Ivec クラスを使用すれば 1 回の演算で同じ結果が得られることがわかります。

Ivec クラスを使用して複数の要素を加算する SIMD 手法

sIs16vec4 ivecA, ivecB, ivec C; /*needs one iteration */
ivecC = ivecA + ivecB; /*returns ivecC0, ivecC1, ivecC2, ivecC3 */

使用できるクラス

並列処理は、C++ では通常それほど簡単には実装できませんが、インテル® C++ SIMD クラスを使用すればそれが可能です。次の表は、インテル C++ SIMD クラスでクラスとライブラリがどのように使用されるかを列挙したものです。

SIMD ベクトルクラス

命令セット クラス 符号の有無 データ型 サイズ 要素数 ヘッダファイル
MMX® テクノロジ
(IA-32 および Itanium® ベース・システムで利用可)
I64vec1 不定 __m64 64 1 ivec.h
  I32vec2 不定 int 32 2 ivec.h
  Is32vec2 符号付き int 32 2 ivec.h
  Iu32vec2 符号なし int 32 2 ivec.h
  I16vec4 不定 short 16 4 ivec.h
  Is16vec4 符号付き short 16 4 ivec.h
  Iu16vec4 符号なし short 16 4 ivec.h
  I8vec8 不定 char 8 8 ivec.h
  Is8vec8 符号付き char 8 8 ivec.h
  Iu8vec8 符号なし char 8 8 ivec.h
ストリーミング SIMD 拡張命令
(IA-32 および Itanium ベースのシステムで利用可)
F32vec4 符号付き float 32 4 fvec.h
  F32vec1 符号付き float 32 1 fvec.h
ストリーミング SIMD 拡張命令 2
(IA-32 ベースのシステムでのみ利用可)
F64vec2 符号付き double 64 2 dvec.h
  I128vec1 不定 __m128i 128 1 dvec.h
  I64vec2 不定 long int 64 4 dvec.h
  Is64vec2 符号付き long int 64 4 dvec.h
  Iu64vec2 符号なし long int 32 4 dvec.h
  I32vec4 不定 int 32 4 dvec.h
  Is32vec4 符号付き int 32 4 dvec.h
  Iu32vec4 符号なし int 32 4 dvec.h
  I16vec8 不定 int 16 8 dvec.h
  Is16vec8 符号付き int 16 8 dvec.h
  Iu16vec8 符号なし int 16 8 dvec.h
  I8vec16 不定 char 8 16 dvec.h
  Is8vec16 符号付き char 8 16 dvec.h
  Iu8vec16 符号なし char 8 16 dvec.h

ほとんどのクラスは、どのデータ型についても同じような機能を持っていて、利用できるすべての組込み関数で表現されています。ただし一部の機能については、データ型が変わるときにその機能を維持しようとするとパフォーマンスが下がる場合があるため、個々のクラスからは除外しています。

即値をとるためにクラスの中に簡単に表現できない組込み関数は実装していません。
(例えば、_mm_shuffle_ps, _mm_shuffle_pi16, _mm_extract_pi16, _mm_insert_pi16)

ヘッダファイルを使用したクラスへのアクセス

必要なクラス・ヘッダファイルは、インテル C++ コンパイラと一緒にインクルード・ディレクトリにインストールされます。各クラスを有効にするときは、次の表に示すように、プログラム・ファイルの中で #include ディレクティブを使用してください。

クラスを有効にするためのインクルード・ディレクティブ

拡張命令セット インクルード・ディレクティブ
MMX テクノロジ #include <ivec.h>
ストリーミング SIMD 拡張命令 #include <fvec.h>
ストリーミング SIMD 拡張命令 2 #include <dvec.h>

上の表で、dvec.h は fvec.h の内容を含みます。同様に fvec.h は ivec.h の内容を含みます。Ivec クラスと Fvec クラスの両方を使用する場合は、fvec.h をインクルードするだけで済みます。同様に、ストリーミング SIMD 拡張命令 2 を利用する場合に、その 3 つとも含むすべてのクラスを使用するときは、dvec.h ファイルをインクルードするだけで済みます。

使用上の注意事項

C++ クラスの使用方法については、一般的なガイドラインがいくつかあります。クラスごとの使用規則については、「整数ベクトルクラス」および「浮動小数点ベクトルクラス」を参照してください。

MMX テクノロジ・レジスタの消去

Ivec クラスと Fvec クラスを同時に使用している場合は、Ivec クラスから呼び出される MMX 命令と、Fvec クラスから呼び出されるインテル x87 アーキテクチャ浮動小数点命令がプログラムの中で混在していることがあります。次の Fvec 関数の中には浮動小数点命令が含まれています。

MMX テクノロジ・レジスタは浮動小数点レジスタ上にエイリアス化されています。したがって、x87 浮動小数点命令を発行する前には、EMMS 命令の組込み関数を使用して MMX テクノロジ・ステートを消去する必要があります。次に例を示します。

ivecA = ivecA & ivecB; MMX 命令を使用する Ivec 論理演算子
empty (); ステートを消去
cout << f32vec4a; x87 浮動小数点命令を使用する F32vec4 演算

警告

MMX テクノロジ・レジスタを消去しないとレジスタステートが不正な状態になります。そのため、処理が不正確になったり、パフォーマンスが低下したりすることがあります。

EMMS 命令のガイドラインに従う

「EMMS 命令を使用する際のガイドライン」に従うことを強くお勧めします。Ivec クラスを使用してコーディングを行う前に、このトピックを参照してください。