2009-05-27 7 views
18

Pour une raison quelconque, j'ai toujours supposé que les champs readonly étaient associés à un surcoût, ce que je considérais comme le suivi par CLR si un champ readonly avait été initialisé ou non. L'overhead ici serait une utilisation supplémentaire de la mémoire pour garder une trace de l'état et une vérification lors de l'attribution d'une valeur. Peut-être que j'ai supposé cela parce que je ne savais pas qu'un champ readonly pouvait seulement être initialisé à l'intérieur d'un constructeur ou dans la déclaration de champ lui-même et sans une vérification d'exécution, vous ne seriez pas en mesure de garantir qu'il n'est pas assigné à plusieurs reprises dans diverses méthodes. Mais maintenant je sais cela, il pourrait facilement être vérifié statiquement par le compilateur C#, non? Alors est-ce le cas? Une autre raison est que j'ai lu que l'utilisation de readonly a un impact «léger» sur les performances, mais ils ne sont jamais allés dans cette affirmation et je ne trouve pas d'informations sur ce sujet, d'où ma question. Je ne sais pas quel autre impact sur les performances il pourrait y avoir en dehors des chèques d'exécution.Y a-t-il un surcoût d'exécution en lecture seule?

Une troisième raison est que j'ai vu que readonly est conservée dans la compilation IL comme initonly, alors quelle est la raison de cette information est dans l'IL si readonly est rien de plus qu'une garantie par le compilateur C# que le champ n'est jamais assigné à l'extérieur d'un constructeur ou d'une déclaration? D'autre part, j'ai découvert que vous pouvez définir la valeur d'un readonly int par réflexion sans que le CLR ne lève une exception, ce qui ne devrait pas être possible si readonly était un contrôle d'exécution.

Donc, ma conjecture est la suivante: la 'readonlyness' est seulement une fonctionnalité de compilation, quelqu'un peut-il confirmer/infirmer cela? Et si c'est le cas, quelle est la raison pour laquelle cette information doit être incluse dans l'IL?

Répondre

16

Vous devez le regarder du même point de vue que les modificateurs d'accès. Les modificateurs d'accès existent dans IL, mais sont-ils vraiment un contrôle d'exécution? (1) Je ne peux pas assigner directement des champs privés à la compilation, (2) Je peux les assigner en utilisant la réflexion. Jusqu'à présent, il semble pas de vérification de l'exécution, comme readonly.

Mais examinons les modificateurs d'accès. Effectuez les opérations suivantes:

  1. Créer Assemblée A.dll avec public class C
  2. Créer une Assemblée b.exe qui fait référence A.dll. B.exe utilise la classe C.
  3. Construire les deux assemblages. Exécuter B.exe fonctionne très bien.
  4. Recréez A.dll mais définissez la classe C sur interne. Remplacez A.dll dans le répertoire B.exe.

Maintenant, l'exécution de B.exe déclenche une exception d'exécution.

Les modificateurs d'accès existent également en IL, n'est-ce pas? Alors, quel est leur but? Le but est que les autres assemblys qui référencent un assembly .Net doivent savoir à quoi ils sont autorisés à accéder et à quoi ils ne sont pas autorisés à accéder, à la fois à la compilation et à l'exécution.

La lecture seule semble avoir un but similaire dans IL. Il indique aux autres assemblées s'ils peuvent écrire dans un champ sur un type particulier. Cependant, en lecture seulene semble pas pour avoir cette même vérification d'exécution que les modificateurs d'accès présentent dans mon exemple ci-dessus.Il semble que readonly est une vérification à la compilation et ne se produit pas en cours d'exécution. Jetez un oeil à un exemple de performance ici: Read-only performance vs const.

Encore une fois, cela ne signifie pas que l'IL est inutile. L'IL s'assure qu'une erreur de compilation se produit en premier lieu. Rappelez-vous, lorsque vous construisez vous ne construisez pas contre le code, mais les assemblages.

+0

Oups, vous avez trouvé la même analyse de performance que j'ai faite. Donc +1 pour vous et j'ai supprimé ma réponse (moins détaillée). – Eddie

+0

Donne un sens parfait, merci :) – JulianR

6

Si vous utilisez une variable d'instance standard, readonly fonctionnera presque de la même manière qu'une variable normale. L'IL ajoutée devient une vérification de temps de compilation, mais elle est pratiquement ignorée lors de l'exécution.

Si vous utilisez un membre statique en lecture seule, les choses sont un peu différentes ...

Comme l'élément statique en lecture seule est définie au cours du constructeur statique, le JIT « sait » qu'une valeur existe. Il n'y a pas de mémoire supplémentaire - readonly empêche simplement d'autres méthodes de le définir, mais c'est une vérification de la compilation. SInce que le JIT sait que ce membre ne peut jamais changer, il est "codé en dur" à l'exécution, donc l'effet final est comme avoir une valeur const. La différence est que cela prendra plus de temps pendant le temps JIT lui-même, puisque le compilateur JIT doit faire un travail supplémentaire pour câbler la valeur de readonly en place. (Cela va être très rapide, cependant.)

Expert C++/CLI par Marcus Hegee a une assez bonne explication à ce sujet.

3

Même si readonly n'avait d'effet qu'au moment de la compilation, il serait toujours nécessaire de stocker les données dans l'ensemble (c'est-à-dire l'IL). Le CLR est un CommunLangue Runtime - les classes écrites dans une langue peuvent être utilisées et étendues par d'autres langages. Étant donné que chaque compilateur pour le CLR ne va pas savoir lire et compiler tous les autres langages, afin de préserver la sémantique des champs readonly, ces données doivent être stockées dans l'assemblage de sorte que les compilateurs pour les autres langages le respectera. Bien sûr, le fait que le champ soit marqué readonly signifie que le JIT peut faire d'autres choses, comme l'optimisation (par exemple les utilisations en ligne de la valeur), etc. Indépendamment du fait que vous ayez utilisé la réflexion pour changer la valeur du champ, La création de IL qui modifie un champ initonly en dehors du constructeur correspondant (instance ou statique, en fonction du type de champ) aboutira à un assembly invérifiable.

4

Un point important qui n'a pas encore été mentionné par d'autres réponses est que lorsqu'un champ readonly est accédé ou qu'une propriété est accédée, la requête est satisfaite en utilisant une copie des données. Si les données en question sont un type de valeur avec plus de 4 à 8 octets de données, le coût de cette copie supplémentaire peut parfois être significatif. Notez que bien qu'il y ait un grand saut de coût lorsque les structures passent de 16 octets à 17, les structures peuvent être un peu plus grandes et être encore plus rapides que les classes dans beaucoup d'applications, si elles ne sont pas copiées trop souvent. Par exemple, si l'on est supposé avoir un type qui représente les sommets d'un triangle dans l'espace tridimensionnel. Une implémentation simple serait une structure contenant une structure avec trois float pour chaque point; probablement 36 octets au total. Si les points, et les coordonnées dans chaque point, sont des champs publics mutables, on peut accéder rapidement et facilement à someTriangle.P1.X, sans avoir à copier les données sauf la coordonnée Y du sommet 1. D'un autre côté, si P1 était une propriété ou un readonly champ, le compilateur devrait copier P1 à une structure temporaire, puis lire X à partir de cela.

+0

Cela peut être une très vieille question, mais dans le cas où un lecteur futur est intéressé, l'article de blog suivant par Jon Skeet contient beaucoup plus d'informations détaillées liées à cette réponse: http: //codeblog.jonskeet .uk/2014/07/16/micro-optimisation-the-surprenante-inefficience-of-readonly-fields/ –

+0

@KlitosKyriacou: Je ne pense pas avoir déjà vu ce post particulier. Je souhaite vraiment que .NET fournisse un moyen par lequel les méthodes de structure pourraient promettre de ne pas écrire ceci ou de dire explicitement qu'elles le font, car cela permettrait (1) d'améliorer les performances dans de nombreux cas en appelant des méthodes qui ne le font pas. écrire 'ceci' [comme indiqué dans le blog], et (2) permettre à quelqu'un d'avoir en toute sécurité des méthodes qui écrivent' this' et demandent au compilateur de rejeter les tentatives de les invoquer sur des variables non-variables. Il existe un certain nombre de cas où le type de données le plus logique serait sémantiquement une structure mutable, puisque ... – supercat

+0

... le modèle 'structVar = structVar.withSomeChange();' ne peut pas être rendu thread-safe, mais 'structVar. makeSomeChange() 'peut utiliser [Interlocked.CompareExchange']. Malheureusement, si une classe expose une propriété de type de structure [hypothétique] 'AtomicBitVector32', un compilateur cédera silencieusement du code fictif pour une opération comme' thing.flags.TestAndSetBit (23); 'si' Flags' est une propriété plutôt qu'une champ. – supercat

Questions connexes