Improve this page Github へのログインが必要です。 簡単な修正は、ここから fork、オンライン編集、pull request ができます。 大きな修正については、 通常の clone で行って下さい。 Page wiki 関連するWikiページを参照・編集 English このページの英語版(原文)

ベクトル演算拡張機能

近年の CPU は、"メディア命令" などと呼ばれる特別なベクトル型とベクトル演算を提供することが多くなっています。 ベクトル型は浮動小数点数や整数の固定長配列で、 ベクトル演算はそれらの要素を同時に演算するもので、 大幅な高速化に貢献します。

普通のD言語のコードで数値データをループ処理していたときに、これらの機能をコンパイラが活用することを、 自動ベクトル化といいます。 自動ベクトル化は、しかしながら、非常に限定された状況でのみ有効で、 ネイティブのベクトル演算の豊富な(そしてしばしば奇妙な) 機能を真に活用することはできていません。

D には配列演算子式という記法があります。例えば:

int[] a,b;
...
a[] += b[];

このコードはコンパイラによってベクトル化されますが、 これも、自動ベクトル化と同様の理由によって効果は限定されています。

ベクトル命令を通常の配列に対して適用することの難しさには、 次のような理由があります:

  1. ベクトル型には非常に厳密なアラインメントの要求が課せられていて、 通常の配列ではこれを満たせません。
  2. C 言語の ABI のベクトル演算は、 特殊な名前マングリング規則とcall/return規約と記号デバッグサポートの下にあります。
  3. ベクトル命令を完全に使いこなす唯一の方法は、インラインアセンブラを使うことです。 しかし、コンパイラがインラインアセンブラのブロックを超えたレジスタ割り当て (やその他の最適化) ができないため、 コードのパフォーマンスは落ちてしまいます。
  4. 通常の配列処理をベクトル演算を同じデータに対して交互に混ぜると、 知らないうちに非常に悪いパフォーマンスをもたらすことがあります。

特殊なベクトル型を提供することで、これらの問題はなくなります。

core.simd

D言語でベクトル型とベクトル演算を使うには、 core.simd を import します:

import core.simd;

ここで定義される型と演算は、コンパイラの生成しているアーキテクチャの定義するものです。 あるCPUファミリのベクトル型のサポートにばらつきがある場合は、 実行時に利用可能性を検査する必要があるかもしれません。 コンパイラは実行時チェックを生成しません; プログラマが気をつける必要があります。

型名は以下の命名規約にしたがっています:

typeNN

type にはベクトルの要素の型が、NN にはその要素数が入ります。 これらの型名は予約語ではありません。

プロパティ

ベクトル型の持つプロパティは次の通りです:

ベクトル型のプロパティ
プロパティ説明
.array静的配列としての表現を返す

静的配列の持つプロパティも全て使うことができます

変換

同じサイズのベクトル型同士は暗黙に変換できます。 ベクトル型は静的配列にキャストすることができます。

整数と浮動小数点数は対応するベクトル型に暗黙変換されます:

int4 v = 7;
v = 3 * v;   // v の各要素に 3 を掛ける

ベクトルの要素への個別アクセス

要素に直接アクセスすることはできませんが、o 配列への変換を通すことで可能になります:

int4 v;
(cast(int*)&v)[3] = 2;   // 4個のintベクトルの第3要素
cast(int[4])v[3] = 2;    // 4個のintベクトルの第3要素
v.array[3] = 2;          // 4個のintベクトルの第3要素
v.ptr[3] = 2;            // 4個のintベクトルの第3要素

条件コンパイル

ベクトル演算拡張が実装されていれば、 バージョン識別子 D_SIMD がセットされます

特定の型が存在するかどうかは、コンパイル時に IsExpression でチェックできます:

static if (is(typeNN))
    ... ある ...
else
    ... ない ...

特定の演算が利用可能かのチェックは、 こうなります:

float4 a,b;
static if (__traits(compiles, a+b))
    ... 使える ...
else
    ... 使えない ...

特定のベクトル命令が使えるかの実行時のチェックには、 core.cpuid の関数を使います。

使えない場合の典型的な回避策は、配列ベクトル演算を代わりに使うことです:

float4 a,b;
static if (__traits(compiles, a/b))
    c = a / b;
else
    c[] = a[] / b[];

X86 と X86_64 でのベクトル演算拡張の実装状況

以下は、X86 と X86_64 アーキテクチャでのベクトル型の対応の説明です。

ベクトル演算は現在のところ OS X での 32bit ターゲットと、 そのほかの 64 bit ターゲットでサポートされています。

core.simd には以下の型が定義されています:

ベクトル型
型名説明gccでの対応物
void1616 バイトの型無しのデータなし
byte1616 個の bytesigned char __attribute((vector_size(16)))
ubyte1616 個の ubyteunsigned char __attribute((vector_size(16)))
short88 個の shortshort __attribute((vector_size(16)))
ushort88 個の ushortushort __attribute((vector_size(16)))
int44 個の intint __attribute((vector_size(16)))
uint44 個の uintunsigned __attribute((vector_size(16)))
long22 個の longlong __attribute((vector_size(16)))
ulong22 個の ulongunsigned long __attribute((vector_size(16)))
float44 個の floatfloat __attribute((vector_size(16)))
double22 個の doubledouble __attribute((vector_size(16)))

注: 32 bit gcc では、long ではなく long long です。

対応済みベクトル演算
演算子void16byte16ubyte16short8ushort8int4uint4long2ulong2float4double2
=×××××××××××
+ +=××××××××××
- -=××××××××××
* *=××××
/ /=××
& &=××××××××
| |=××××××××
^ ^=××××××××
unary~××××××××
unary+××××××××××
unary-××××××××××

リストにない演算子には対応していません。

ベクトル処理用組み込み演算

対応している組み込み演算については core.simd を参照してください