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.
Importante
Les techniques décrites dans cette section améliorent les performances lorsqu’elles sont appliquées aux chemins d’accès à chaud dans votre code. Les chemins critiques sont ces sections de votre base de code qui sont exécutées souvent et répétées lors des opérations normales. L’application de ces techniques au code qui n’est pas souvent exécuté aura un impact minimal. Avant d’apporter des modifications pour améliorer les performances, il est essentiel de mesurer une base de référence. Ensuite, analysez cette ligne de base pour déterminer où se produisent les goulots d’étranglement de la mémoire. Vous pouvez en savoir plus sur de nombreux outils multiplateformes pour mesurer les performances de votre application dans la section diagnostics et instrumentation. Vous pouvez pratiquer une session de profilage dans le tutoriel pour mesurer l’utilisation de la mémoire dans la documentation Visual Studio.
Une fois que vous avez mesuré l’utilisation de la mémoire et que vous avez déterminé que vous pouvez réduire les allocations, utilisez les techniques de cette section pour réduire les allocations. Après chaque modification successive, mesurez à nouveau l’utilisation de la mémoire. Vérifiez que chaque modification a un impact positif sur l’utilisation de la mémoire dans votre application.
Le travail de performances dans .NET signifie souvent supprimer les allocations de votre code. Chaque bloc de mémoire que vous allouez doit en fin de compte être libéré. Moins d’allocations réduisent le temps passé dans le garbage collection. Il permet un temps d’exécution plus prévisible en supprimant les garbage collections à partir de chemins de code spécifiques.
Une tactique courante pour réduire les allocations consiste à modifier les structures de données critiques de types class
en types struct
. Cette modification a un impact sur la sémantique de l’utilisation de ces types. Les paramètres et les retours sont désormais passés par valeur au lieu de référence. Le coût de la copie d’une valeur est négligeable si les types sont petits, trois mots ou moins (compte tenu d’un mot de taille naturelle d’un entier). Il est mesurable et peut avoir un véritable impact sur les performances pour des types plus larges. Pour lutter contre l’effet de la copie, les développeurs peuvent passer ces types par ref
pour retrouver la sémantique prévue.
Les fonctionnalités C# ref
vous permettent d’exprimer la sémantique souhaitée pour struct
les types sans avoir un impact négatif sur leur facilité d’utilisation globale. Avant ces améliorations, les développeurs devaient recourir à unsafe
des constructions avec des pointeurs et de la mémoire brute pour atteindre le même impact sur les performances. Le compilateur génère du code vérifiable sécurisé pour les nouvelles ref
fonctionnalités associées.
Le code vérifiable sécurisé signifie que le compilateur détecte les dépassements de mémoire tampon possibles ou l’accès à la mémoire non allouée ou libérée. Le compilateur détecte et empêche certaines erreurs.
Passer et retourner par référence
Les variables en C# stockent des valeurs. Dans struct
les types, la valeur est le contenu d’une instance du type. Dans class
les types, la valeur est une référence à un bloc de mémoire qui stocke une instance du type. L’ajout du modificateur ref
signifie que la variable stocke la référence à la valeur. Dans struct
les types, les références pointent vers le stockage contenant la valeur. Dans les types class
, les références pointent vers le stockage contenant la référence au bloc de mémoire.
En C#, les paramètres aux méthodes sont passés par valeur et les valeurs de retour sont retournées par valeur. La valeur de l’argument est passée à la méthode. La valeur de l’argument de retour est la valeur de retour.
Le ref
, in
, ref readonly
ou out
modificateur indique que l'argument est passé par référence. Une référence au lieu de stockage est transmise à la méthode. L’ajout ref
à la signature de méthode signifie que la valeur de retour est retournée par référence. Une référence à l’emplacement de stockage est la valeur de retour.
Vous pouvez également utiliser l’attribution ref pour qu’une variable fasse référence à une autre variable. Une affectation classique copie la valeur du côté droit vers la variable du côté gauche de l’affectation. Une affectation par référence copie l’emplacement de mémoire de la variable du côté droit vers la variable du côté gauche. La ref
fait maintenant référence à la variable d'origine.
int anInteger = 42; // assignment.
ref int location = ref anInteger; // ref assignment.
ref int sameLocation = ref location; // ref assignment
Console.WriteLine(location); // output: 42
sameLocation = 19; // assignment
Console.WriteLine(anInteger); // output: 19
Lorsque vous attribuez une variable, vous modifiez sa valeur. Lorsque vous assignez par référence une variable, vous changez la référence qu'elle désigne.
Vous pouvez travailler directement avec le stockage des valeurs à l’aide de variables ref
, passer par référence et assignation par référence. Les règles de portée appliquées par le compilateur garantissent la sécurité lors du travail direct avec le stockage.
Les modificateurs ref readonly
et in
indiquent tous deux que l’argument doit être passé par référence et ne peut pas être réaffecté dans la méthode. La différence est que ref readonly
la méthode utilise le paramètre comme variable. La méthode peut capturer le paramètre ou renvoyer le paramètre par référence en lecture seule. Dans ces cas, vous devez utiliser le ref readonly
modificateur. Sinon, le in
modificateur offre plus de flexibilité. Vous n’avez pas besoin d’ajouter le in
modificateur à un argument pour un in
paramètre. Vous pouvez donc mettre à jour les signatures API existantes en toute sécurité à l’aide du in
modificateur. Le compilateur émet un avertissement si vous n’ajoutez pas le modificateur ref
ou in
à un argument pour un paramètre ref readonly
.
Contexte ref safe
C# inclut des règles pour ref
les expressions afin de s’assurer qu’une ref
expression n’est plus accessible où le stockage auquel il fait référence n’est plus valide. Prenons l’exemple suivant :
public ref int CantEscape()
{
int index = 42;
return ref index; // Error: index's ref safe context is the body of CantEscape
}
Le compilateur signale une erreur, car vous ne pouvez pas retourner une référence à une variable locale à partir d’une méthode. L’appelant ne peut pas accéder au stockage auquel il est fait référence. Le contexte de sécurité ref définit l’étendue dans laquelle une ref
expression est sécurisée pour accéder ou modifier. Le tableau suivant répertorie les contextes de sécurité ref pour les types de variables.
ref
les champs ne peuvent pas être déclarés dans une class
ou une non-ref struct
. Par conséquent, ces lignes ne se trouvent pas dans la table :
Déclaration | contexte ref safe |
---|---|
non ref local | bloc où la variable locale est déclarée |
paramètre non ref | méthode actuelle |
ref , ref readonly , in paramètre |
méthode d'appel |
Paramètre out |
méthode actuelle |
class champ |
méthode d'appel |
champ struct non-ref |
méthode actuelle |
ref champ de ref struct |
méthode d'appel |
Une variable peut être ref
retournée si son contexte ref safe est la méthode appelante. Si son contexte de sécurité ref est la méthode actuelle ou un bloc, ref
le retour est interdit. L’extrait de code suivant montre deux exemples. Un champ membre est accessible à partir de l’étendue appelant une méthode. Par conséquent, le contexte ref safe d’un champ de classe ou de struct est la méthode appelante. Le contexte de sécurité ref pour un paramètre avec les modificateurs ref
, ou in
est la méthode entière. Les deux peuvent être ref
retournés à partir d’une méthode membre :
private int anIndex;
public ref int RetrieveIndexRef()
{
return ref anIndex;
}
public ref int RefMin(ref int left, ref int right)
{
if (left < right)
return ref left;
else
return ref right;
}
Remarque
Lorsque le modificateur ref readonly
ou in
est appliqué à un paramètre, ce paramètre peut être retourné par ref readonly
, et non par ref
.
Le compilateur garantit qu’une référence ne peut pas échapper à son contexte de sécurité ref. Vous pouvez utiliser ref
des paramètres, ref return
et ref
des variables locales en toute sécurité, car le compilateur détecte si vous avez écrit accidentellement du code dans lequel une ref
expression est accessible lorsque son stockage n’est pas valide.
Contextes safe et ref structs
ref struct
les types nécessitent davantage de règles pour s’assurer qu’elles peuvent être utilisées en toute sécurité. Un ref struct
type peut inclure des ref
champs. Cela nécessite l’introduction d’un contexte sûr. Pour la plupart des types, le contexte sécurisé est la méthode appelante. En d’autres termes, une valeur qui n’est pas une ref struct
valeur peut toujours être retournée à partir d’une méthode.
De façon informelle, le contexte sûr d’un ref struct
est l’étendue dans laquelle tous ses ref
champs sont accessibles. En d'autres termes, il s'agit de l'intersection du contexte ref safe de tous ses ref
champs. La méthode suivante renvoie un ReadOnlySpan<char>
vers un champ membre, de sorte que le contexte sûr soit la méthode :
private string longMessage = "This is a long message";
public ReadOnlySpan<char> Safe()
{
var span = longMessage.AsSpan();
return span;
}
En revanche, le code suivant émet une erreur, car le ref field
membre du Span<int>
fait référence au tableau d'entiers alloué sur la pile. Cela ne peut pas échapper à la méthode :
public Span<int> M()
{
int length = 3;
Span<int> numbers = stackalloc int[length];
for (var i = 0; i < length; i++)
{
numbers[i] = i;
}
return numbers; // Error! numbers can't escape this method.
}
Unifier les types de mémoire
L'introduction de System.Span<T> et System.Memory<T> fournit un modèle unifié pour travailler avec la mémoire.
System.ReadOnlySpan<T> et System.ReadOnlyMemory<T> fournissent des versions en lecture seule pour accéder à la mémoire. Ils fournissent toutes une abstraction sur un bloc de mémoire stockant un tableau d’éléments similaires. La différence est que Span<T>
et ReadOnlySpan<T>
sont des types ref struct
, tandis que Memory<T>
et ReadOnlyMemory<T>
sont des types struct
. Les étendues contiennent un ref field
. Par conséquent, les instances d’une étendue ne peuvent pas quitter son contexte sûr. Le contexte sûr d’un ref struct
est le contexte ref safe de son ref field
. L'implémentation de Memory<T>
et ReadOnlyMemory<T>
supprime cette restriction. Vous utilisez ces types pour accéder directement aux mémoires tampons.
Améliorer les performances avec la sécurité ref
L’utilisation de ces fonctionnalités pour améliorer les performances implique ces tâches :
-
Évitez les allocations : lorsque vous modifiez un type d’un
class
à unstruct
, vous modifiez la façon dont il est stocké. Les variables locales sont stockées sur la pile. Les membres sont stockés inline lorsque l’objet conteneur est alloué. Cette modification signifie moins d’allocations et diminue le travail que fait le garbage collector. Il peut également diminuer la pression de la mémoire afin que le collecteur de déchets s’exécute avec moins souvent. -
Conserver la sémantique de référence : modification d’un type
class
à unestruct
modification de la sémantique de passage d’une variable à une méthode. Le code qui a modifié l’état de ses paramètres a besoin de modification. Maintenant que le paramètre est unstruct
, la méthode modifie une copie de l’objet d’origine. Vous pouvez restaurer la sémantique d’origine en passant ce paramètre en tant queref
paramètre. Après cette modification, la méthode modifie à nouveau l’originalstruct
. -
Évitez de copier des données : la copie de types plus volumineux
struct
peut avoir un impact sur les performances dans certains chemins de code. Vous pouvez également ajouter le modificateurref
pour passer des structures de données plus volumineuses aux méthodes par référence plutôt que par valeur. -
Restreindre les modifications : lorsqu’un
struct
type est passé par référence, la méthode appelée peut modifier l’état du struct. Vous pouvez remplacer le modificateurref
par les modificateursref readonly
ouin
pour indiquer que l’argument ne peut pas être modifié. Préférezref readonly
quand la méthode capture le paramètre ou le retourne par référence en lecture seule. Vous pouvez également créer desreadonly struct
types oustruct
types avecreadonly
membres pour offrir davantage de contrôle sur les membres d’unstruct
pouvant être modifiés. -
Manipulation directe de la mémoire : certains algorithmes sont plus efficaces lors du traitement des structures de données comme un bloc de mémoire contenant une séquence d’éléments. Les
Span
types etMemory
fournissent un accès sécurisé aux blocs de mémoire.
Aucune de ces techniques ne nécessite unsafe
de code. Utilisé avec sagesse, vous pouvez obtenir des caractéristiques de performances à partir du code sécurisé qui était auparavant uniquement possible à l’aide de techniques non sécurisées. Vous pouvez essayer les techniques vous-même dans le tutoriel sur la réduction des allocations de mémoire.