web-dev-qa-db-fra.com

Pourquoi utiliserait-on jamais le modificateur de paramètre "in" en C #?

Donc, je pense (je pense) comprendre ce que fait le modificateur de paramètre in. Mais ce qu'il fait semble être tout à fait redondant.

Généralement, je pense que la seule raison d'utiliser un ref serait de modifier la variable appelante, ce qui est explicitement interdit par in. Donc, passer par in référence semble logiquement équivalent à passer par valeur.

Existe-t-il un avantage en termes de performances? J’étais convaincu que du côté des choses, un paramètre ref doit au moins copier l'adresse physique de la variable, qui devrait avoir la même taille que tout objet typique. référence.

Alors, l'avantage est-il uniquement dans les grandes structures, ou existe-t-il une optimisation en arrière-plan du compilateur qui la rend attrayante ailleurs? Si ce dernier cas, pourquoi ne devrais-je pas créer tous les paramètres d'un in?

48
Travis Reed

in a récemment été introduit dans le langage C #.

in est en fait un ref readonly. D'une manière générale, il n'y a qu'un seul cas d'utilisation où in peut être utile: des applications hautes performances traitant beaucoup de grands readonly struct S.

En supposant que vous ayez:

readonly struct VeryLarge
{
    public readonly long Value1;   
    public readonly long Value2;

    public long Compute() { }
    // etc
}

et

void Process(in VeryLarge value) { }

Dans ce cas, la structure VeryLarge sera passée par référence sans créer de copies défensives lors de l'utilisation de cette structure dans la méthode Process (par exemple lors de l'appel de value.Compute()), et l'immuabilité de la structure est assurée par le compilateur.

Notez que passer un struct non lu avec un modificateur in force le compilateur à créer une copie défensive lorsque l'appel des méthodes de struct et l'accès aux propriétés dans la méthode Process ci-dessus, ce qui affectera négativement les performances!

Il y a un très bon entrée de blog MSDN que je recommande de lire attentivement.

Si vous souhaitez obtenir plus d'informations sur l'historique de in-, vous pouvez lire ceci discussion dans le référentiel GitHub du langage C #.

En général, la plupart des développeurs s'accordent à dire que l'introduction de in pourrait être considérée comme une erreur. C'est un langage plutôt exotique et ne peut être utile que dans les cas Edge très performants.

51
dymanoid

passer par in référence semble logiquement équivalent à passer par valeur.

Correct.

Existe-t-il un avantage en termes de performances?

Oui.

J’étais convaincu que du côté de l’arrière-plan, un paramètre ref devait au moins copier l’adresse physique de la variable, qui devait avoir la même taille que toute référence d’objet typique.

Il n’existe pas de condition qu’une référence à un objet et une référence à une variable aient la même taille et qu’il n’existe pas de exigence soit la taille d'un mot machine, mais oui, en pratique, les deux sont 32 bits sur des machines 32 bits et 64 bits sur des machines 64 bits.

Ce que vous pensez que "l'adresse physique" a à voir avec cela ne me semble pas clair. Sous Windows, nous utilisons des adresses virtuelles , pas adresses physiques dans le code en mode utilisateur. Je suis curieux de savoir dans quelles circonstances possibles imagineriez-vous qu'une adresse physique a une signification dans un programme C #?.

Il n’existe pas non plus d’implémentation d’une référence de quelque type que ce soit en tant qu’adresse virtuelle du stockage. Les références peuvent être des poignées opaques dans des tables GC dans une implémentation conforme de la spécification CLI.

l'avantage est-il juste dans les grandes structures?

Le scénario de motivation (- === -) de la fonctionnalité permet de réduire le coût de passage de structures plus grandes.

Notez qu'il n'y a aucune garantie que in accélère réellement les programmes et puisse les ralentir. Toutes les questions sur les performances doivent être résolues par des recherches empiriques. Il y a très peu d'optimisations qui sont toujours gagnantes ; ce n'est pas une optimisation "toujours gagnante".

existe-t-il une optimisation en arrière-plan du compilateur qui le rend attrayant ailleurs?

Le compilateur et le moteur d'exécution sont autorisés à effectuer les optimisations qu'ils choisissent si cela ne viole pas les règles de la spécification C #. À ma connaissance, il n’ya pas encore d’optimisation de ce type pour les paramètres in, mais cela n’empêche pas de telles optimisations à l’avenir.

pourquoi ne devrais-je pas transformer chaque paramètre en?

Supposons que vous ayez créé un paramètre int à la place un in int paramètre. Quels coûts sont imposés?

  • le site d'appel nécessite maintenant une variable plutôt qu'une valeur
  • la variable ne peut pas être inscrite. Le schéma d'allocation des registres soigneusement réglé de la gigue vient de se voir imposer une clé.
  • le code sur le site d'appels est plus volumineux car il doit prendre une référence à la variable et la placer sur la pile, alors qu'avant il pouvait simplement pousser la valeur sur la pile d'appels
  • un code plus gros signifie que certaines instructions de saut courtes peuvent maintenant être devenues des instructions de saut long. Le code est donc encore plus grand. Cela a des répercussions sur toutes sortes de choses. Les caches se remplissent plus tôt, la gigue a plus de travail à faire, elle peut choisir de ne pas effectuer certaines optimisations sur des tailles de code plus grandes, etc.
  • sur le site appelé, nous avons transformé l'accès à une valeur de la pile (ou à un registre) en indirection en un pointeur. Il est très probable que ce pointeur se trouve dans le cache, mais nous avons néanmoins transformé un accès à une instruction à la valeur en un accès à deux instructions.
  • Etc.

Supposons que c’est un double et que vous le changez en un in double. Encore une fois, la variable ne peut plus être inscrite dans un registre à virgule flottante hautes performances. Cela a non seulement des conséquences sur les performances , mais il peut aussi changer le comportement du programme! C # est autorisé à utiliser l'arithmétique des flottants avec une précision supérieure à 64 bits et ne le fait généralement que si les flottants peuvent être enregister.

Ce n'est pas une optimisation gratuite. Vous devez mesurer ses performances par rapport aux alternatives. Votre meilleur pari est tout simplement de ne pas créer de grandes structures en premier lieu, comme le suggèrent les directives de conception.

20
Eric Lippert

Il y a. Lors du passage d'un struct, le mot clé in permet une optimisation lorsque le compilateur n'a besoin que de passer un pointeur, sans risque de modification du contenu par la méthode. est critique - cela évite une opération de copie. Sur de grandes structures, cela peut faire toute la différence.

6
TomTom

Ceci est fait en raison de l'approche de programmation fonctionnelle. L'un des principes majeurs est que la fonction ne doit pas avoir d'effets secondaires, ce qui signifie qu'elle ne doit pas modifier les valeurs des paramètres et doit renvoyer une valeur. En C #, il n'y avait aucun moyen de passer des structures (et un type de valeur) sans être copié uniquement par référence, ce qui permet de modifier la valeur. Dans Swift, il existe un algorithme de hacky qui copie struct (leurs collections sont des structures BTW) tant que la méthode commence à changer ses valeurs. Les personnes qui utilisent Swift pas toutes Ceci est une fonctionnalité intéressante de c # car sa mémoire est efficace et explicite. Si vous regardez quoi de neuf , vous verrez que de plus en plus de choses sont effectuées autour des structures et des tableaux en pile. Une déclaration est simplement nécessaire pour ces fonctionnalités. Des limitations sont mentionnées dans les autres réponses, mais ce n’est pas essentiel pour comprendre l’orientation de .net.

1
Access Denied