Améliorer les performances grâce à ReadOnlySpan<T>
Introduction
Apparu dans la version 2.1 de .NET Core, le type ReadOnlySpan<T> permet de manipuler des données en mémoire de manière performante, sans réaliser de copie des données à manipuler. Contrairement aux approches classiques reposant sur les méthodes Substring() ou Split(), il propose une vue en lecture seule sur une zone mémoire existante.
Pourquoi les performances sont-elles améliorées ? Certes, parce qu'il n'y a pas de copie de données. Mais aussi parce que les allocations mémoire sont réduites et le Garbage Collector a moins de travail à réaliser lors de ses passages.
Contraintes d'utilisation
Le type ReadOnlySpan<T> est un ref struct, c'est-à-dire une structure qui ne peut exister que dans la pile d'exécution (stack). Contrairement aux structures classiques, elle ne peut pas être allouée sur le tas (heap) ni être utilisée dans certains contextes. Ainsi une variable de ce type ne peut pas être :
-
Stocké dans un attribut d'une classe
-
Capturé par une expression lambda, car elle peut être exécutée après la fin du bloc de code dans lequel le
spana été créé. Le compilateur interdit donc cette situation afin de garantir la sécurité mémoire. -
Conservé pendant une opération asynchrone (
await)
Ces restrictions garantissent que le span ne référence jamais une zone mémoire devenue invalide.
Démonstration par l'exemple
Lorsque nous écrivons les instructions suivantes afin d'obtenir l'année, dans une date définie dans une chaîne de caractères :
Ce code est opérationnel. Cependant, la méthode Substring() crée une nouvelle instance de string contenant les quatre premiers caractères, ce qui entraîne une allocation mémoire supplémentaire.
En revanche, si nous écrivons :
Alors la variable year représente une vue sur les quatre premiers caractères de la chaîne d'origine.
En écrivant ces instructions, nous obtenons directement le jour, le mois et l'année de manière performante :
ReadOnlySpan<char> date = "2026-06-30";
int year = int.Parse(date[..4]);
int month = int.Parse(date.Slice(5,2));
int day = int.Parse(date.Slice(8,2));
Découpage d'une chaîne de caractères
Soit la chaîne de caractères suivante :
Pour découper cette chaîne de caractères, le type string propose la méthode Split :
Un tableau avec 4 chaînes de caractères est créé après les allocations mémoire nécessaires. En écrivant :
Nous accédons au prénom de manière performante, sans allocation mémoire.
Accès aux données dans un tableau
Voici un tableau de nombres :
Voici comment accéder aux deux premiers éléments du tableau :
On peut parcourir les données pointées par la variable span :
Si nous accédons à des éléments (ou des caractères) avec des valeurs qui sortent de la plage de valeurs autorisée :
Alors une exception de type ArgumentOutOfRangeException est levée.