トレーニング
SIMD アクセラレータの数値型を使用する
SIMD (Single Instruction Multiple Data) を使用すると、1 つの命令を使用して、複数のデータに対して並列で操作を実行するためのハードウェア サポートが提供されます。 .NET では、System.Numerics 名前空間の下に SIMD アクセラレータの型のセットがあります。 SIMD 操作は、ハードウェア レベルで並列化できます。 それにより、数学、科学、グラフィックス アプリで一般的な、ベクター化された計算のスループットが向上します。
.NET の SIMD アクセラレータの型には、次の型が含まれます。
2 つのマトリックス型。3x2 行列を表す Matrix3x2 と Single 値の 4x4 行列を表す Matrix4x4。
Single 値を使用して 3 次元物理回転をエンコードするために使用されるベクトルを表す Quaternion 型。
指定の数値型のベクトルを表し、SIMD サポートが活かされる広範囲の演算子セットを提供する Vector<T> 型。 Vector<T> インスタンスの数は、アプリケーションの有効期間にわたって固定されていますが、その値 Vector<T>.Count は、コードを実行しているコンピューターの CPU によって異なります。
注意
Vector<T> 型は .NET Framework に含まれません。 この型にアクセスするには、System.Numerics.Vectors NuGet パッケージをインストールする必要があります。
SIMD アクセラレータの型は、非 SIMD アクセラレータのハードウェアや JIT コンパイラと共に使用できるように実装されています。 SIMD 命令を活用するには、64 ビット アプリを RyuJIT コンパイラを使用するランタイムで実行する必要があります。 RyuJIT コンパイラは、.NET Core と .NET Framework 4.6 以降に含まれています。 SIMD のサポートは、64 ビット プロセッサを対象とする場合にのみ提供されます。
カスタム SIMD アルゴリズムを実行する前に、Boolean を返す Vector.IsHardwareAccelerated を使用して、ホスト コンピューターが 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);
3x2 行列を表す Matrix3x2 と 4x4 行列を表す Matrix4x4。 行列関連の計算に使用できます。 以下の例は、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>.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 に関するフィードバック
.NET はオープンソース プロジェクトです。 フィードバックを提供するにはリンクを選択します。