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手法

Is16vec4 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クラスから呼び出される Intel x87 アーキテクチャ浮動小数点命令がプログラムの中で混在していることがあります。次のFvec関数の中には浮動小数点命令が含まれています。

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

ivecA = ivecA & ivecB; /* Ivec logical operation that uses MMX instructions */
empty (); /* clear state */
cout << f32vec4a; /* F32vec4 operation that uses x87 floating-point instructions */

注意

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

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

「EMMS命令を使用する際のガイドライン」に従うことを強くお勧めします。Ivec クラスを使用してコーディングをするときは、こちらをご覧ください。