Použití akcelerovaných číselných typů SIMD

SIMD (jedna instrukce, více dat) poskytuje hardwarovou podporu pro provádění operací na více částech dat paralelně pomocí jediné instrukce. V .NET je v oboru názvů sada akcelerovaných typů System.Numerics SIMD. Operace SIMD je možné paralelizovat na úrovni hardwaru. To zvyšuje propustnost vektorizovaných výpočtů, které jsou běžné v matematických, vědeckých a grafických aplikacích.

Akcelerované typy .NET SIMD

Akcelerované typy .NET SIMD zahrnují následující typy:

  • Vector2, Vector3a Vector4 typy, které představují vektory s hodnotami 2, 3 a 4Single.

  • Dva typy matic, Matrix3x2které představují matici 3x2, a Matrix4x4, která představuje 4x4 matice Single hodnot.

  • Typ Plane , který představuje rovinu v trojrozměrném prostoru pomocí Single hodnot.

  • Typ Quaternion , který představuje vektor, který se používá ke kódování trojrozměrných fyzických otočení pomocí Single hodnot.

  • Typ Vector<T> , který představuje vektor zadaného číselného typu a poskytuje širokou sadu operátorů, které využívají podporu SIMD. Počet Vector<T> instancí je pevný po celou dobu životnosti aplikace, ale jeho hodnota Vector<T>.Count závisí na procesoru počítače, na kterém běží kód.

    Poznámka:

    Tento Vector<T> typ není součástí rozhraní .NET Framework. Abyste získali přístup k tomuto typu, musíte nainstalovat balíček NuGet System.Numerics.Vectors .

Akcelerované typy SIMD se implementují tak, aby se mohly používat s nerychlovaným hardwarem nebo kompilátory JIT. Aby bylo možné využít pokyny SIMD, musí být vaše 64bitové aplikace spuštěny modulem runtime, který používá kompilátor RyuJIT . Kompilátor RyuJIT je součástí .NET Core a rozhraní .NET Framework 4.6 a novější. Podpora SIMD se poskytuje pouze při cílení na 64bitové procesory.

Jak používat SIMD?

Před spuštěním vlastních algoritmů SIMD je možné zkontrolovat, jestli hostitelský počítač podporuje SIMD pomocí , Vector.IsHardwareAcceleratedkterý vrací Boolean. To nezaručuje, že u konkrétního typu je povolená akcelerace SIMD, ale je indikátorem, že je podporována některými typy.

Jednoduché vektory

Nej primitivnější typy simd akcelerované v .NET jsou Vector2, Vector3a Vector4 typy, které představují vektory s hodnotami 2, 3 a 4 Single . Následující příklad používá Vector2 k přidání dvou vektorů.

var v1 = new Vector2(0.1f, 0.2f);
var v2 = new Vector2(1.1f, 2.2f);
var vResult = v1 + v2;

Je také možné použít vektory .NET k výpočtu dalších matematických vlastností vektorů, jako Dot productje , TransformClamp a tak dále.

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);

Matice

Matrix3x2, který představuje matici 3x2 a Matrix4x4, která představuje 4x4 matici. Lze použít pro výpočty související s maticí. Následující příklad ukazuje násobení matice na odpovídající transponovanou matici pomocí 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);

Vektor<T>

Dává Vector<T> možnost používat delší vektory. Počet Vector<T> instancí je pevný, ale jeho hodnota Vector<T>.Count závisí na procesoru počítače, na kterém běží kód.

Následující příklad ukazuje, jak vypočítat prvek-moudrý součet dvou polí pomocí Vector<T>.

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;
}

Poznámky

SIMD pravděpodobně odebere jeden kritický bod a zpřístupní další, například propustnost paměti. Obecně platí, že výhoda výkonu při používání SIMD se liší v závislosti na konkrétním scénáři a v některých případech může dokonce fungovat ještě horší než jednodušší ekvivalentní kód než SIMD.