Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
SIMD (singola istruzione, più dati) fornisce supporto hardware per l'esecuzione di un'operazione su più parti di dati, in parallelo, usando una singola istruzione. In .NET è disponibile un set di tipi con accelerazione SIMD nello spazio dei nomi System.Numerics. Le operazioni SIMD possono essere parallelizzate a livello di hardware. Ciò aumenta la velocità effettiva dei calcoli vettorializzati, comuni nelle app matematiche, scientifiche e grafiche.
Tipi .NET con accelerazione SIMD
I tipi con accelerazione SIMD .NET includono i tipi seguenti:
Tipi Vector2, Vector3e Vector4 che rappresentano vettori con valori 2, 3 e 4 Single .
Due tipi di matrice, Matrix3x2, che rappresenta una matrice 3x2 e Matrix4x4, che rappresenta una matrice Single di valori 4x4.
Il tipo Plane, che rappresenta un piano nello spazio tridimensionale utilizzando i Single valori.
Tipo Quaternion , che rappresenta un vettore utilizzato per codificare rotazioni fisiche tridimensionali usando Single valori.
Tipo Vector<T> , che rappresenta un vettore di un tipo numerico specificato e fornisce un ampio set di operatori che traggono vantaggio dal supporto SIMD. Il conteggio di un'istanza Vector<T> è fisso per la durata di un'applicazione, ma il relativo valore Vector<T>.Count dipende dalla CPU del computer che esegue il codice.
Annotazioni
Il Vector<T> tipo non è incluso in .NET Framework. Per ottenere l'accesso a questo tipo, è necessario installare il pacchetto NuGet System.Numerics.Vectors .
I tipi con accelerazione SIMD vengono implementati in modo che possano essere usati con hardware non con accelerazione SIMD o compilatori JIT. Per sfruttare le istruzioni SIMD, le app a 64 bit devono essere eseguite dal runtime che usa il compilatore RyuJIT . Un compilatore RyuJIT è incluso in .NET Core e in .NET Framework 4.6 e versioni successive. Il supporto SIMD viene fornito solo quando è destinata ai processori a 64 bit.
Come usare SIMD?
Prima di eseguire algoritmi SIMD personalizzati, è possibile verificare se il computer host supporta SIMD usando Vector.IsHardwareAccelerated, che restituisce un oggetto Boolean. Questo non garantisce che l'accelerazione SIMD sia abilitata per un tipo specifico, ma indica che è supportata da alcuni tipi.
Vettori semplici
I tipi SIMD accelerati più primitivi in .NET sono Vector2 tipi, Vector3 tipi e Vector4 tipi, che rappresentano vettori con 2, 3 e 4 valori Single. L'esempio seguente usa Vector2 per aggiungere due vettori.
var v1 = new Vector2(0.1f, 0.2f);
var v2 = new Vector2(1.1f, 2.2f);
var vResult = v1 + v2;
È anche possibile usare vettori .NET per calcolare altre proprietà matematiche di vettori, ad Dot productesempio , TransformClamp e così via.
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);
Matrice
Matrix3x2, che rappresenta una matrice 3x2 e Matrix4x4, che rappresenta una matrice 4x4. Può essere usato per i calcoli correlati alla matrice. L'esempio seguente illustra la moltiplicazione di una matrice alla matrice trasposizione corrispondente tramite 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);
Vettore<T>
Il Vector<T> offre la possibilità di usare vettori più lunghi. Il conteggio di un'istanza Vector<T> è fisso, ma il relativo valore Vector<T>.Count dipende dalla CPU del computer che esegue il codice.
Nell'esempio seguente viene illustrato come calcolare la somma a livello di elemento di due matrici usando 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;
}
Osservazioni:
SIMD è più probabile che rimuova un collo di bottiglia ed esponga il successivo, ad esempio la larghezza di banda della memoria. In generale, il vantaggio delle prestazioni dell'uso di SIMD varia a seconda dello scenario specifico e in alcuni casi può persino comportare prestazioni peggiori rispetto al codice equivalente non SIMD più semplice.