Bagikan melalui


Menggunakan tipe numerik yang dipercepat SIMD

SIMD (Instruksi tunggal, beberapa data) menyediakan dukungan perangkat keras untuk melakukan operasi pada beberapa bagian data, secara paralel, menggunakan satu instruksi. Di .NET, ada sekumpulan jenis yang dipercepat SIMD di bawah namespace System.Numerics. Operasi SIMD dapat diparalelkan pada tingkat perangkat keras. Itu meningkatkan throughput komputasi vektorisasi, yang umum dalam aplikasi matematika, ilmiah, dan grafis.

Jenis Tipe .NET yang Diakselerasi SIMD

Jenis .NET SIMD-accelerated mencakup jenis berikut:

  • Jenis Vector2, Vector3, dan Vector4 , yang mewakili vektor dengan nilai 2, 3, dan 4 Single .

  • Dua jenis matriks, Matrix3x2, yang mewakili matriks 3x2, dan Matrix4x4, yang mewakili matriks nilai 4x4 Single .

  • Tipe Plane, yang mewakili bidang dalam ruang tiga dimensi menggunakan nilai-nilai Single.

  • Jenis Quaternion, yang mewakili vektor yang digunakan untuk mengodekan rotasi fisik tiga dimensi menggunakan nilai Single.

  • Jenis Vector<T> yang mewakili vektor dari jenis numerik tertentu dan menyediakan rangkaian operator luas yang memanfaatkan dukungan SIMD. Jumlah instance Vector<T> tetap selama masa pakai aplikasi, tetapi nilainya Vector<T>.Count tergantung pada CPU mesin yang menjalankan kode.

    Nota

    Jenis Vector<T> tidak disertakan dalam .NET Framework. Anda harus menginstal paket NuGet System.Numerics.Vectors untuk mendapatkan akses ke jenis ini.

Jenis yang dipercepat SIMD diimplementasikan sedemikian sehingga dapat digunakan dengan perangkat keras tanpa percepatan SIMD atau kompilator JIT. Untuk memanfaatkan instruksi SIMD, aplikasi 64-bit Anda harus dijalankan oleh runtime yang menggunakan pengkompilasi RyuJIT . Pengkompilasi RyuJIT disertakan dalam .NET Core dan di .NET Framework 4.6 dan yang lebih baru. Dukungan SIMD hanya disediakan saat menargetkan prosesor 64-bit.

Bagaimana cara menggunakan SIMD?

Sebelum menjalankan algoritma SIMD kustom, dimungkinkan untuk memeriksa apakah komputer host mendukung SIMD dengan menggunakan Vector.IsHardwareAccelerated, yang mengembalikan Boolean. Ini tidak menjamin bahwa akselerasi SIMD diaktifkan untuk jenis tertentu, tetapi merupakan indikator bahwa akselerasi tersebut didukung oleh beberapa jenis.

Vektor Sederhana

Jenis-jenis akselerasi SIMD paling primitif di .NET adalah Vector2, Vector3, dan Vector4, yang mewakili vektor dengan komponen 2, 3, dan 4 Single. Contoh di bawah ini menggunakan Vector2 untuk menambahkan dua vektor.

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

Dimungkinkan juga untuk menggunakan vektor .NET untuk menghitung sifat matematika vektor lainnya seperti , , Dot productTransform dan sebagainyaClamp.

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

Matriks

Matrix3x2, yang mewakili matriks 3x2, dan Matrix4x4, yang mewakili matriks 4x4. Dapat digunakan untuk perhitungan terkait matriks. Contoh di bawah ini menunjukkan perkalian matriks ke matriks transpose korespondennya menggunakan 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>

memberikan Vector<T> kemampuan untuk menggunakan vektor yang lebih panjang. Jumlah suatu Vector<T> instans tetap, tetapi nilainya Vector<T>.Count bergantung pada CPU dari mesin yang menjalankan kode.

Contoh berikut menunjukkan cara menghitung jumlah per-elemen dari dua array menggunakan 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;
}

Komentar

SIMD lebih mungkin untuk menghapus satu hambatan dan mengekspos hambatan berikutnya, seperti throughput memori. Secara umum manfaat performa penggunaan SIMD bervariasi tergantung pada skenario tertentu, dan dalam beberapa kasus bahkan dapat berkinerja lebih buruk daripada kode yang setara non-SIMD yang lebih sederhana.