共用方式為


使用 SIMD 加速數值型別

SIMD (單指令多資料) 提供硬體支援,使用單一指令以平行方式在多個資料片段上執行作業。 在 .NET 中,System.Numerics 命名空間下有一組 SIMD 加速型別。 SIMD 作業可以在硬體層級平行處理。 這能提升向量化運算 (常見於數學、科學及圖形應用程式中) 的輸送量。

.NET SIMD 加速型別

.NET SIMD 加速型別包含下列型別:

  • Vector2Vector3Vector4 類型,其代表具有 2、3 及 4 Single 值的向量。

  • 兩種矩陣類型:代表 3x2 矩陣的 Matrix3x2,以及代表 Single 值 4x4 矩陣的 Matrix4x4

  • Plane 類型代表使用 Single 值立體空間中的平面。

  • Quaternion 類型代表用來使用 Single 值編碼 3D 實體旋轉的向量。

  • Vector<T> 類型,其代表指定數值類型的向量,並能提供一組受益於 SIMD 支援的廣泛運算子。 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 和更新版本中。 只有在以 64 位元處理器為目標時,才會提供 SIMD 支援。

如何使用 SIMD?

在執行自訂 SIMD 演算法之前,可以使用 Vector.IsHardwareAccelerated 來檢查主機電腦是否支援 SIMD,這會傳回 Boolean。 這並不保證特定類型已啟用 SIMD 加速,但這是某些類型支援的指標。

簡單向量

.NET 中的最基本 SIMD 加速類型為 Vector2Vector3Vector4 類型,代表具有 2、3 和 4 Single 值的向量。 下列範例會使用 Vector2 來新增兩個向量。

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

您也可以使用 .NET 向量來計算向量的其他數學屬性,例如 Dot productTransformClamp 等等。

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

向量 T<>

Vector<T> 可讓您使用較長的向量。 Vector<T> 執行個體的計數是固定的,但其值 Vector<T>.Count 會相依於執行程式碼之電腦的 CPU。

下列範例示範如何使用 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;
}

備註

SIMD 更可能移除一個瓶頸並公開下一個瓶頸,例如記憶體輸送量。 一般而言,使用 SIMD 的效能優點會因特定情節而有所不同,在某些情況下,甚至可能比更簡單的非 SIMD 對等程式碼執行效果更差。