Korzystanie z typów liczbowych przyspieszanych przez simd

SimD (pojedyncza instrukcja, wiele danych) zapewnia sprzętową obsługę wykonywania operacji na wielu fragmentach danych, równolegle przy użyciu jednej instrukcji. W programie .NET w przestrzeni nazw znajduje się zestaw typów przyspieszanych przez simd System.Numerics . Operacje SIMD można zrównoleglić na poziomie sprzętu. Zwiększa to przepływność obliczeń wektorowych, które są typowe w aplikacjach matematycznych, naukowych i graficznych.

Typy przyspieszone simd .NET

Typy przyspieszone przez simd .NET obejmują następujące typy:

  • Typy Vector2Vector3, i Vector4 , które reprezentują wektory z wartościami 2, 3 i 4Single.

  • Dwa typy macierzy, Matrix3x2, które reprezentują macierz 3x2, Matrix4x4i , które reprezentują macierz wartości 4x4 Single .

  • Typ Plane , który reprezentuje płaszczyznę w trójwymiarowej przestrzeni przy użyciu Single wartości.

  • Typ Quaternion , który reprezentuje wektor, który jest używany do kodowania trójwymiarowych rotacji fizycznych przy użyciu Single wartości.

  • Typ Vector<T> , który reprezentuje wektor określonego typu liczbowego i udostępnia szeroką gamę operatorów, które korzystają z obsługi simd. Liczba wystąpień jest Vector<T> stała dla okresu istnienia aplikacji, Vector<T>.Count ale jej wartość zależy od procesora CPU maszyny, na których działa kod.

    Uwaga

    Typ Vector<T> nie jest uwzględniony w .NET Framework. Aby uzyskać dostęp do tego typu, należy zainstalować pakiet NuGet System.Numerics.Vectors.

Typy przyspieszone simd są implementowane w taki sposób, że mogą być używane z sprzętem lub kompilatorami JIT, które nie są przyspieszane przez simd. Aby skorzystać z instrukcji SIMD, aplikacje 64-bitowe muszą być uruchamiane przez środowisko uruchomieniowe, które używa kompilatora RyuJIT . Kompilator RyuJIT jest zawarty w programie .NET Core i w .NET Framework 4.6 i nowszych. Obsługa SIMD jest zapewniana tylko w przypadku procesorów 64-bitowych.

Jak używać simd?

Przed wykonaniem niestandardowych algorytmów SIMD można sprawdzić, czy maszyna hosta obsługuje funkcję SIMD Vector.IsHardwareAccelerated, używając metody , która zwraca wartość Boolean. Nie gwarantuje to, że przyspieszanie SIMD jest włączone dla określonego typu, ale jest wskaźnikiem, że jest obsługiwane przez niektóre typy.

Proste wektory

Najbardziej pierwotne typy przyspieszone za pomocą SIMD na .NET to Vector2typy , Vector3i Vector4 , które reprezentują wektory z wartościami 2, 3 i 4 Single . W poniższym przykładzie użyto Vector2 do dodania dwóch wektorów.

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

Można również używać wektorów .NET Dot productdo obliczania innych właściwości matematycznych wektorów, takich jak , TransformClamp i tak dalej.

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

Macierz

Matrix3x2, który reprezentuje macierz 3x2, Matrix4x4i , która reprezentuje macierz 4x4. Może być używany do obliczeń związanych z macierzą. W poniższym przykładzie pokazano mnożenie macierzy do odpowiadającej jej macierzy transponowania przy użyciu 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);

VectorT<>

Zapewnia Vector<T> możliwość używania dłuższych wektorów. Liczba wystąpień jest Vector<T> stała, ale jego wartość Vector<T>.Count zależy od procesora CPU maszyny, na których jest uruchomiony kod.

W poniższym przykładzie pokazano sposób obliczania ilu elementów w dwóch tablicach przy użyciu funkcji Vector<T>.

double[] SimdVectorProd(double[] left, double[] right)
{
    var offset = Vector<double>.Count;
    double[] result = new double[left.Length];
    int i = 0;
    for (i = 0; i < left.Length; i += offset)
    {
        var v1 = new Vector<double>(left, i);
        var v2 = new Vector<double>(right, i);
        (v1 * v2).CopyTo(result, i);
    }

    //remaining items
    for (; i < left.Length; ++i)
    {
        result[i] = left[i] * right[i];
    }

    return result;
}

Uwagi

W przypadku simd istnieje większe prawdopodobieństwo usunięcia jednego wąskiego gardła i uwidocznienia kolejnego, na przykład przepływności pamięci. Ogólnie rzecz biorąc, korzyści z używania simd różnią się w zależności od konkretnego scenariusza, a w niektórych przypadkach może nawet działać gorszy niż prostszy kod równoważny z innym niż SIMD.