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 existuje sada SIMD-akcelerovaných typů ve jmenném prostoru System.Numerics. 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, Matrix3x2, který představuje matici 3x2, a Matrix4x4, který představuje matici 4x4 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 neakcelerovaným hardwarem nebo s kompilátory JIT. Chcete-li zjistit, zda je akcelerace SIMD k dispozici za běhu, použijte Vector.IsHardwareAccelerated. Pokud se tato vlastnost vrátí true, alespoň některá rozhraní API používají hardwarově akcelerované operace SIMD. Pokud se vrátí false, žádná rozhraní API nejsou hardwarově akcelerovaná.

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

Nejprimitivnější typy akcelerované SIMD v .NET jsou Vector2, Vector3 a Vector4 typy, které představují vektory s 2, 3 a 4 hodnotami 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);

*Matrix*

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 spočítat součet dvou polí po prvcích 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.