Använda SIMD-accelererade numeriska typer
SIMD (enkel instruktion, flera data) ger maskinvarustöd för att utföra en åtgärd på flera datadelar parallellt med hjälp av en enda instruktion. I .NET finns det en uppsättning SIMD-accelererade typer under System.Numerics namnområdet. SIMD-åtgärder kan parallelliseras på maskinvarunivå. Det ökar dataflödet för vektoriserade beräkningar, som är vanliga i matematiska, vetenskapliga och grafikappar.
.NET SIMD-accelererade typer
De .NET SIMD-accelererade typerna innehåller följande typer:
Typerna Vector2, Vector3och Vector4 , som representerar vektorer med värdena 2, 3 och 4 Single .
Två matristyper, Matrix3x2, som representerar en 3x2-matris och Matrix4x4, som representerar en 4x4-matris med Single värden.
Typen Plane , som representerar ett plan i tredimensionellt utrymme med hjälp av Single värden.
Typen Quaternion , som representerar en vektor som används för att koda tredimensionella fysiska rotationer med hjälp av Single värden.
Typen Vector<T> , som representerar en vektor av en angiven numerisk typ och ger en bred uppsättning operatorer som har stöd för SIMD. Antalet instanser Vector<T> har åtgärdats under programmets livslängd, men dess värde Vector<T>.Count beror på processorn för den dator som kör koden.
Kommentar
Typen Vector<T> ingår inte i .NET Framework. Du måste installera NuGet-paketet System.Numerics.Vectors för att få åtkomst till den här typen.
SIMD-accelererade typer implementeras på ett sådant sätt att de kan användas med icke-SIMD-accelererad maskinvara eller JIT-kompilatorer. För att kunna dra nytta av SIMD-instruktioner måste dina 64-bitarsappar köras av den körning som använder RyuJIT-kompilatorn . En RyuJIT-kompilator ingår i .NET Core och i .NET Framework 4.6 och senare. SIMD-stöd tillhandahålls endast när du riktar in dig på 64-bitars processorer.
Hur använder jag SIMD?
Innan du kör anpassade SIMD-algoritmer är det möjligt att kontrollera om värddatorn stöder SIMD med hjälp Vector.IsHardwareAcceleratedav , som returnerar en Boolean. Detta garanterar inte att SIMD-acceleration är aktiverat för en viss typ, men är en indikator på att den stöds av vissa typer.
Enkla vektorer
De mest primitiva SIMD-accelererade typerna i .NET är Vector2, Vector3och Vector4 typer, som representerar vektorer med 2, 3 och 4 Single värden. Exemplet nedan använder Vector2 för att lägga till två vektorer.
var v1 = new Vector2(0.1f, 0.2f);
var v2 = new Vector2(1.1f, 2.2f);
var vResult = v1 + v2;
Det går också att använda .NET-vektorer för att beräkna andra matematiska egenskaper för vektorer som Dot product
, Transform
Clamp
och så vidare.
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);
Matris
Matrix3x2, som representerar en 3x2-matris och Matrix4x4, som representerar en 4x4-matris. Kan användas för matrisrelaterade beräkningar. Exemplet nedan visar multiplikation av en matris till dess korrespondent transponeringsmatris med hjälp av 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>
Ger Vector<T> möjlighet att använda längre vektorer. Antalet instanser Vector<T> är fast, men dess värde Vector<T>.Count beror på processorn för den dator som kör koden.
I följande exempel visas hur du beräknar den elementvisa summan av två matriser med hjälp av 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;
}
Kommentarer
SIMD är mer sannolikt att ta bort en flaskhals och exponera nästa, till exempel minnesdataflöde. I allmänhet varierar prestandafördelarna med att använda SIMD beroende på det specifika scenariot, och i vissa fall kan det till och med prestera sämre än enklare icke-SIMD-ekvivalent kod.