Notes
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
SIMD (instruction unique, plusieurs données) fournit une prise en charge matérielle pour effectuer une opération sur plusieurs éléments de données, en parallèle, à l’aide d’une seule instruction. Dans .NET, il existe un ensemble de types accélérés par SIMD dans l’espace de noms System.Numerics. Les opérations SIMD peuvent être parallélisées au niveau matériel. Cela augmente le débit des calculs vectorisés, qui sont courants dans les applications mathématiques, scientifiques et graphiques.
Types .NET accélérés par SIMD
Les types .NET accélérés par SIMD incluent les types suivants :
Les types Vector2, Vector3 et Vector4 représentent des vecteurs avec 2, 3 et 4 valeurs de Single.
Deux types de matrices, Matrix3x2qui représente une matrice 3x2 et Matrix4x4, qui représente une matrice 4x4 de Single valeurs.
Type Plane, qui représente un plan dans un espace tridimensionnel en utilisant des valeurs de Single.
Type Quaternion , qui représente un vecteur utilisé pour encoder des rotations physiques tridimensionnelles à l’aide Single de valeurs.
Type Vector<T> , qui représente un vecteur d’un type numérique spécifié et fournit un large ensemble d’opérateurs qui bénéficient de la prise en charge SIMD. Le nombre d’instances Vector<T> est fixe pour la durée de vie d’une application, mais sa valeur Vector<T>.Count dépend du processeur de la machine exécutant le code.
Remarque
Le Vector<T> type n’est pas inclus dans le .NET Framework. Vous devez installer le package NuGet System.Numerics.Vectors pour accéder à ce type.
Les types SIMD-accélérés sont implémentés de manière à ce qu'ils puissent être utilisés avec du matériel non accéléré par SIMD ou des compilateurs JIT. Pour tirer parti des instructions SIMD, vos applications 64 bits doivent être exécutées par le runtime qui utilise le compilateur RyuJIT . Un compilateur RyuJIT est inclus dans .NET Core et dans .NET Framework 4.6 et versions ultérieures. La prise en charge SIMD est fournie uniquement lors du ciblage de processeurs 64 bits.
Comment utiliser SIMD ?
Avant d’exécuter des algorithmes SIMD personnalisés, il est possible de vérifier si la machine hôte prend en charge SIMD à l’aide de Vector.IsHardwareAccelerated, qui retourne un Boolean. Cela ne garantit pas que l’accélération SIMD est activée pour un type spécifique, mais indique qu’elle est prise en charge par certains types.
Vecteurs simples
Les types SIMD les plus primitifs accélérés dans .NET sont les types Vector2, Vector3 et Vector4, qui représentent des vecteurs de 2, 3 et 4 valeurs de Single. L’exemple ci-dessous utilise Vector2 pour ajouter deux vecteurs.
var v1 = new Vector2(0.1f, 0.2f);
var v2 = new Vector2(1.1f, 2.2f);
var vResult = v1 + v2;
Il est également possible d’utiliser des vecteurs .NET pour calculer d’autres propriétés mathématiques de vecteurs tels que Dot product
, Transform
Clamp
etc.
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, qui représente une matrice 3x2 et Matrix4x4, qui représente une matrice 4x4. Peut être utilisé pour les calculs liés à la matrice. L’exemple ci-dessous illustre la multiplication d’une matrice à sa matrice de transposition correspondante à l’aide de 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);
Vecteur<T>
Le Vector<T> permet d’utiliser des vecteurs plus longs. Le nombre d’instances Vector<T> est fixe, mais sa valeur Vector<T>.Count dépend de l’UC de la machine exécutant le code.
L’exemple suivant montre comment calculer la somme des éléments de deux tableaux à l’aide de 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;
}
Remarques
SIMD est plus susceptible de supprimer un goulot d’étranglement et d’exposer le prochain, par exemple le débit de mémoire. En général, l’avantage en termes de performances de l’utilisation de SIMD varie en fonction du scénario spécifique et, dans certains cas, il peut même être pire que le code équivalent non SIMD plus simple.