Champ vs Propriété. Optimisation de la performance

S’il vous plaît noter cette question liée à la performance seulement. Supprimez les directives de conception, la philosophie, la compatibilité, la portabilité et tout ce qui ne concerne pas les performances pures. Je vous remercie.

Maintenant à la question. J’ai toujours supposé que parce que les getters / setters C # sont vraiment des méthodes déguisées, alors la lecture du champ public doit être plus rapide que d’appeler un getter.

Donc, pour être sûr d’avoir fait un test (le code ci-dessous). Cependant, ce test ne produit que les résultats attendus (les champs sont plus rapides que les getters à 34% ) si vous l’exécutez depuis Visual Studio.

Une fois que vous le lancez depuis la ligne de commande, il affiche à peu près le même timing …

La seule explication pourrait être que le CLR effectue une optimisation supplémentaire (corrigez-moi si je me trompe ici).

Je ne crois pas que dans les applications réelles où ces propriétés soient utilisées de manière beaucoup plus sophistiquée, elles seront optimisées de la même manière.

Aidez-moi à prouver ou à réfuter l’idée selon laquelle, dans la réalité, les propriétés sont plus lentes que les champs.

La question est: comment dois-je modifier les classes de test pour que le CLR modifie le comportement afin que le champ public devienne plus important que les getters. OU montre-moi que toute propriété sans logique interne aura le même effet qu’un champ (au moins sur le getter)

EDIT: Je ne parle que de la version x64.

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Diagnostics; using System.Runtime.InteropServices; namespace PropertyVsField { class Program { static int LEN = 20000000; static void Main(ssortingng[] args) { List a = new List(LEN); List b = new List(LEN); Random r = new Random(DateTime.Now.Millisecond); for (int i = 0; i < LEN; i++) { double p = r.NextDouble(); a.Add(new A() { P = p }); b.Add(new B() { P = p }); } Stopwatch sw = new Stopwatch(); double d = 0.0; sw.Restart(); for (int i = 0; i < LEN; i++) { d += a[i].P; } sw.Stop(); Console.WriteLine("auto getter. {0}. {1}.", sw.ElapsedTicks, d); sw.Restart(); for (int i = 0; i < LEN; i++) { d += b[i].P; } sw.Stop(); Console.WriteLine(" field. {0}. {1}.", sw.ElapsedTicks, d); Console.ReadLine(); } } class A { public double P { get; set; } } class B { public double P; } } 

Comme d’autres l’ont déjà mentionné, les getters sont en ligne.

Si vous voulez éviter l’inlining, vous devez

  • remplacer les propriétés automatiques par des propriétés manuelles:

     class A { private double p; public double P { get { return p; } set { p = value; } } } 
  • et dites au compilateur de ne pas incorporer le getter (ou les deux, si vous en avez envie):

      [MethodImpl(MethodImplOptions.NoInlining)] get { return p; } 

Notez que la première modification ne fait pas de différence en termes de performances, alors que la seconde modification montre une surcharge de méthode claire:

Propriétés manuelles:

 auto getter. 519005. 10000971,0237547. field. 514235. 20001942,0475098. 

Pas d’inclinaison du getter:

 auto getter. 785997. 10000476,0385552. field. 531552. 20000952,077111. 

Jetez un coup d’œil aux propriétés et aux champs – Pourquoi est-ce important? (Jonathan Aneja) article de blog de l’un des membres de l’équipe VB sur MSDN. Il décrit l’argument propriété versus champs et explique également les propriétés sortingviales comme suit:

Un argument que j’ai entendu pour utiliser les champs sur les propriétés est que «les champs sont plus rapides», mais pour les propriétés sortingviales, ce n’est pas vrai, car le compilateur Just-In-Time (JIT) du CLR va efficace comme accéder directement à un champ.

Le JIT va aligner toute méthode (pas seulement un getter) que ses mésortingques internes détermineront plus rapidement. Étant donné qu’une propriété standard est return _Property; il sera en ligne dans tous les cas.

La raison pour laquelle vous constatez un comportement différent est que, en mode Debug avec un débogueur connecté, le JIT est considérablement handicapé, pour garantir que tous les emplacements de stack correspondent à ce que vous attendez du code.

Vous oubliez aussi la règle de performance numéro un, les tests sont plus efficaces. Par exemple, même si le sorting rapide est asymptotiquement plus rapide que le sorting par insertion, le sorting par insertion est en fait plus rapide pour les entrées extrêmement petites.

La seule explication pourrait être que le CLR effectue une optimisation supplémentaire (corrigez-moi si je me trompe ici).

Oui, cela s’appelle l’inlining. Cela se fait dans le compilateur (niveau de code machine – c’est-à-dire JIT). Comme le getter / setter est sortingvial (code très simple), les appels de méthode sont détruits et le getter / setter est écrit dans le code environnant.

Cela ne se produit pas en mode débogage pour prendre en charge le débogage (c.-à-d. La possibilité de définir un point d’arrêt dans un getter ou un setter).

Dans Visual Studio, il n’y a aucun moyen de faire cela dans le débogueur. Comstackr la version, exécutez sans débogueur attaché et vous obtiendrez l’optimisation complète.

Je ne crois pas que dans les applications réelles où ces propriétés soient utilisées de manière beaucoup plus sophistiquée, elles seront optimisées de la même manière.

Le monde est plein d’illusions qui sont fausses. Ils seront optimisés car ils sont encore sortingviaux (c.-à-d. Du code simple, donc ils sont en ligne).

Il convient de noter qu’il est possible de voir les “vraies” performances dans Visual Studio.

  1. Comstackr en mode Release avec les optimisations activées.
  2. Allez dans Débogage -> Options et parameters, et décochez l’option “Supprimer l’optimisation JIT sur la charge du module (Géré uniquement)”.
  3. Si vous le souhaitez, désélectionnez “Activer Just My Code”, sinon vous risquez de ne pas pouvoir entrer dans le code.

Maintenant, l’assemblage assemblé sera le même, même si le débogueur est connecté, ce qui vous permettra d’intervenir dans le déassembly optimisé si vous le souhaitez. Ceci est essentiel pour comprendre comment le CLR optimise le code.