SIMD (単一命令、複数データ) は、1 つの命令を使用して複数のデータに対して操作を並列で実行するためのハードウェア サポートを提供します。 .NET では、 System.Numerics 名前空間の下に SIMD アクセラレータ型のセットがあります。 SIMD 操作は、ハードウェア レベルで並列化できます。 これによって、数学、科学、グラフィックスのアプリで一般的なベクター化された計算のスループットが向上します。
.NET の SIMD アクセラレータの型
.NET SIMD アクセラレータ型には、次の型が含まれます。
3x2 行列を表す Matrix3x2 の 2 つのマトリックス型と、Matrix4x4値の 4x4 行列を表すSingle。
Quaternion型。Single値を使用して 3 次元の物理回転をエンコードするために使用されるベクトルを表します。
Vector<T>型。これは、指定された数値型のベクトルを表し、SIMD サポートの恩恵を受ける広範な演算子セットを提供します。 Vector<T> インスタンスの数はアプリケーションの有効期間中に固定されますが、その値Vector<T>.Countは、コードを実行しているコンピューターの CPU によって異なります。
注
Vector<T>型は .NET Framework には含まれません。 この型にアクセスするには、 System.Numerics.Vector NuGet パッケージをインストールする必要があります。
SIMD アクセラレータ型は、非 SIMD アクセラレータハードウェアまたは JIT コンパイラで使用できるように実装されます。 SIMD 命令を利用するには、 RyuJIT コンパイラを使用するランタイムによって 64 ビット アプリを実行する必要があります。 RyuJIT コンパイラは、.NET Core および .NET Framework 4.6 以降に含まれています。 SIMD のサポートは、64 ビット プロセッサを対象とする場合にのみ提供されます。
SIMD の使用方法
カスタム SIMD アルゴリズムを実行する前に、Vector.IsHardwareAcceleratedを返すBooleanを使用して、ホスト コンピューターが SIMD をサポートしているかどうかを確認できます。 これは、特定の種類に対して SIMD アクセラレーションが有効になっていることを保証するものではありませんが、一部の型でサポートされていることを示します。
単純なベクトル
.NET で最もプリミティブな SIMD アクセラレータ型は、 Vector2、 Vector3、および Vector4 型であり、2、3、4 の Single 値を持つベクトルを表します。 次の例では、 Vector2 を使用して 2 つのベクトルを追加します。
var v1 = new Vector2(0.1f, 0.2f);
var v2 = new Vector2(1.1f, 2.2f);
var vResult = v1 + v2;
また、.NET ベクターを使用して、 Dot product
、 Transform
、 Clamp
などのベクターの他の数学的プロパティを計算することもできます。
var v1 = new Vector2(0.1f, 0.2f);
var v2 = new Vector2(1.1f, 2.2f);
var vResult1 = Vector2.Dot(v1, v2);
var vResult2 = Vector2.Distance(v1, v2);
var vResult3 = Vector2.Clamp(v1, Vector2.Zero, Vector2.One);
マトリックス
Matrix3x2は 3x2 行列を表し、 Matrix4x4は 4x4 行列を表します。 マトリックス関連の計算に使用できます。 次の例は、SIMD を使用した対応する転置行列への行列の乗算を示しています。
var m1 = new Matrix4x4(
1.1f, 1.2f, 1.3f, 1.4f,
2.1f, 2.2f, 3.3f, 4.4f,
3.1f, 3.2f, 3.3f, 3.4f,
4.1f, 4.2f, 4.3f, 4.4f);
var m2 = Matrix4x4.Transpose(m1);
var mResult = Matrix4x4.Multiply(m1, m2);
Vector<T>
Vector<T>では、長いベクトルを使用できます。 Vector<T> インスタンスの数は固定されていますが、その値Vector<T>.Countは、コードを実行しているコンピューターの CPU によって異なります。
次の例では、 Vector<T>を使用して 2 つの配列の要素ごとの合計を計算する方法を示します。
double[] Sum(double[] left, double[] right)
{
if (left is null)
{
throw new ArgumentNullException(nameof(left));
}
if (right is null)
{
throw new ArgumentNullException(nameof(right));
}
if (left.Length != right.Length)
{
throw new ArgumentException($"{nameof(left)} and {nameof(right)} are not the same length");
}
int length = left.Length;
double[] result = new double[length];
// Get the number of elements that can't be processed in the vector
// NOTE: Vector<T>.Count is a JIT time constant and will get optimized accordingly
int remaining = length % Vector<double>.Count;
for (int i = 0; i < length - remaining; i += Vector<double>.Count)
{
var v1 = new Vector<double>(left, i);
var v2 = new Vector<double>(right, i);
(v1 + v2).CopyTo(result, i);
}
for (int i = length - remaining; i < length; i++)
{
result[i] = left[i] + right[i];
}
return result;
}
注釈
SIMD は、ボトルネックを 1 つ削除し、次のボトルネック (メモリ スループットなど) を公開する可能性が高くなります。 一般に、SIMD を使用するパフォーマンス上の利点は、特定のシナリオによって異なります。場合によっては、より単純な非 SIMD 同等のコードよりもパフォーマンスが低下する可能性があります。
.NET