Performances des méthodes statiques par rapport aux méthodes d'instance

108

Ma question concerne les caractéristiques de performance des méthodes statiques par rapport aux méthodes d'instance et leur évolutivité. Supposons pour ce scénario que toutes les définitions de classe se trouvent dans un seul assembly et que plusieurs types de pointeurs discrets sont requis.

Considérer:

public sealed class InstanceClass
{
      public int DoOperation1(string input)
      {
          // Some operation.
      }

      public int DoOperation2(string input)
      {
          // Some operation.
      }

      // … more instance methods.
}

public static class StaticClass
{
      public static int DoOperation1(string input)
      {
          // Some operation.
      }

      public static int DoOperation2(string input)
      {
          // Some operation.
      }

      // … more static methods.
}

Les classes ci-dessus représentent un modèle de style d'assistance.

Dans une classe d'instance, la résolution de la méthode d'instance prend un moment pour s'opposer à StaticClass.

Mes questions sont:

  1. Lorsque conserver l'état n'est pas un problème (aucun champ ou propriété n'est requis), est-il toujours préférable d'utiliser une classe statique?

  2. Là où il y a un nombre considérable de ces définitions de classes statiques (disons 100 par exemple, avec un certain nombre de méthodes statiques chacune) cela affectera-t-il négativement les performances d'exécution ou la consommation de mémoire par rapport au même nombre de définitions de classe d'instance?

  3. Lorsqu'une autre méthode de la même classe d'instance est appelée, la résolution d'instance se produit-elle toujours? Par exemple, en utilisant le mot-clé [this] comme this.DoOperation2("abc")dans DoOperation1la même instance.

Bernie White
la source
qu'entendez-vous par «résolution d'instance»? Au niveau IL, le pointeur "this" est disponible comme toute autre variable locale. En fait, sur certaines anciennes versions de CLR / JIT, vous pouvez appeler une méthode d'instance sur un NULL à condition qu'elle ne touche pas le 'this' - le code vient de survoler et de planter sur rien ... maintenant CLR / JIT contient un null explicite- vérifier chaque invocation de membre ..
quetzalcoatl
> vijaymukhi.com/documents/books/ilbook/chap8.htm et «instance d'appel» contre simplement «appel». Le premier attend «ce» paramètre et le second - non.
quetzalcoatl
@Quetzalcoatl désolé pour la confusion, la question était plutôt de méthode à méthode à partir de la même instance et si cela nécessite que l'instance soit résolue en elle-même.
Bernie White
1
@quetzalcoatl J'ai supposé qu'il voulait dire, "est-ce que le compilateur se débarrasse de la vérification qui thispointe vers quelque chose quand une classe appelle une méthode d'instance sur elle-même?"
Jon Hanna

Réponses:

152

En théorie, une méthode statique devrait fonctionner légèrement mieux qu'une méthode d'instance, toutes choses étant égales par ailleurs, en raison du thisparamètre caché supplémentaire .

En pratique, cela fait si peu de différence qu'il sera caché dans le bruit des diverses décisions du compilateur. (Par conséquent, deux personnes pourraient "prouver" l'une meilleure que l'autre avec des résultats en désaccord). Notamment, car le thisest normalement passé dans un registre et est souvent dans ce registre pour commencer.

Ce dernier point signifie qu'en théorie, nous devrions nous attendre à ce qu'une méthode statique qui prend un objet comme paramètre et fasse quelque chose avec lui soit légèrement moins bonne que l'équivalent en tant qu'instance sur ce même objet. Encore une fois, la différence est si légère que si vous essayez de la mesurer, vous finirez probablement par mesurer une autre décision du compilateur. (D'autant que la probabilité que cette référence soit dans un registre tout le temps est également assez élevée).

Les vraies différences de performances se résumeront à savoir si vous avez artificiellement des objets en mémoire pour faire quelque chose qui devrait naturellement être statique, ou si vous emmêlez des chaînes de passage d'objets de manière compliquée pour faire ce qui devrait naturellement être une instance.

D'où le numéro 1. Lorsque le maintien de l'état n'est pas un problème, il est toujours préférable d'être statique, car c'est à cela que sert la statique . Ce n'est pas un problème de performance, bien qu'il y ait une règle générale pour bien jouer avec les optimisations du compilateur - il est plus probable que quelqu'un se soit efforcé d'optimiser les cas d'utilisation normale que ceux qui présentent une utilisation étrange.

Numéro 2. Ne fait aucune différence. Il y a un certain montant de coût par classe pour chaque membre, en fonction de la quantité de métadonnées, de la quantité de code dans le fichier DLL ou EXE réel et de la quantité de code jit. C'est la même chose que ce soit une instance ou statique.

Avec l'élément 3, thisest commethis ça. Notez cependant:

  1. Le thisparamètre est passé dans un registre particulier. Lors de l'appel d'une méthode d'instance dans la même classe, elle sera probablement déjà dans ce registre (à moins qu'elle n'ait été cachée et que le registre soit utilisé pour une raison quelconque) et par conséquent, aucune action n'est requise pour définir lethis sur ce qu'il doit être défini. . Cela s'applique dans une certaine mesure, par exemple aux deux premiers paramètres de la méthode étant les deux premiers paramètres d'un appel qu'elle effectue.

  2. Comme il sera clair que ce thisn'est pas nul, cela peut être utilisé pour optimiser les appels dans certains cas.

  3. Comme il sera clair que ce thisn'est pas nul, cela peut rendre les appels de méthode intégrés plus efficaces à nouveau, car le code produit pour simuler l'appel de méthode peut omettre certaines vérifications nulles dont il pourrait avoir besoin de toute façon.

  4. Cela dit, les chèques nuls sont bon marché!

Il convient de noter que les méthodes statiques génériques agissant sur un objet, plutôt que les méthodes d'instance, peuvent réduire certains des coûts discutés à http://joeduffyblog.com/2011/10/23/on-generics-and-some-of- the-associated-overheads / dans le cas où cette statique donnée n'est pas appelée pour un type donné. Comme il le dit: «En passant, il s'avère que les méthodes d'extension sont un excellent moyen de rendre les abstractions génériques plus payantes».

Cependant, notez que cela ne concerne que l'instanciation d'autres types utilisés par la méthode, qui n'existent pas autrement. En tant que tel, cela ne s'applique vraiment pas à de nombreux cas (une autre méthode d'instance a utilisé ce type, un autre code ailleurs utilisé ce type).

Résumé:

  1. La plupart du temps, les coûts de performance de l'instance par rapport au statique sont inférieurs à négligeables.
  2. Les coûts viendront généralement lorsque vous abusez de l'électricité statique, par exemple ou vice-versa. Si vous ne faites pas partie de votre décision entre statique et instance, vous aurez plus de chances d'obtenir le résultat correct.
  3. Il existe de rares cas où les méthodes génériques statiques dans un autre type entraînent la création de moins de types que les méthodes génériques d'instance, ce qui peut parfois avoir un petit avantage à devenir rarement utilisé (et "rarement" fait référence aux types avec lesquels il est utilisé dans le durée de vie de l'application, pas à quelle fréquence elle est appelée). Une fois que vous avez compris ce dont il parle dans cet article, vous verrez que de toute façon, cela n'est pas pertinent à 100% pour la plupart des décisions statiques contre instance. Edit: Et cela n'a surtout ce coût qu'avec ngen, pas avec du code jitted.

Edit: Une note sur à quel point les vérifications nulles sont bon marché (ce que j'ai réclamé ci-dessus). La plupart des vérifications nulles dans .NET ne vérifient pas du tout null, elles continuent plutôt ce qu'elles allaient faire avec l'hypothèse que cela fonctionnera, et si une exception d'accès se produit, elle est transformée en un fichier NullReferenceException. En tant que tel, la plupart du temps lorsque conceptuellement le code C # implique une vérification nulle parce qu'il accède à un membre d'instance, le coût s'il réussit est en fait nul. Une exception serait certains appels intégrés, (car ils veulent se comporter comme s'ils appelaient un membre d'instance) et ils frappent simplement un champ pour déclencher le même comportement, donc ils sont également très bon marché, et ils peuvent encore souvent être laissés de côté de toute façon (par exemple, si la première étape de la méthode impliquait d'accéder à un champ tel quel).

Jon Hanna
la source
Pouvez-vous nous dire si la question statique vs instance a une incidence sur la cohérence du cache? Le recours à l'un ou à l'autre est-il plus susceptible de provoquer des erreurs de cache? Y a-t-il un bon aperçu expliquant pourquoi?
scriptocalypse
@scriptocalypse Pas vraiment. Le cache d'instructions ne verra aucune différence, et à ce niveau, il n'y a pas beaucoup de différence entre l'accès aux données via thisou via un paramètre explicite. Un impact plus important ici serait la proximité des données avec les données associées (les champs de type valeur ou les valeurs de tableau sont plus proches que les données des champs de type référence) et les modèles d'accès.
Jon Hanna
"en théorie, nous devrions nous attendre à ce qu'une méthode statique qui prend un objet comme paramètre et fasse quelque chose avec lui soit légèrement moins bonne que l'équivalent en tant qu'instance sur ce même objet." - Voulez-vous dire que si la méthode ci-dessus prend paramètre en tant qu'objet au lieu de chaîne, non statique, c'est mieux? par exemple: ma méthode statique prend l'objet comme paramètre et le sérialise en chaîne et renvoie une chaîne. suggérez-vous d'utiliser non statique dans ce cas?
batmaci
1
@batmaci Je veux dire qu'il y a de bonnes chances que obj.DoSomehting(2)ce soit légèrement moins cher que, DoSomething(obj, 2)mais comme je l'ai aussi dit, la différence est si légère et si dépendante de petites choses qui pourraient finir par être différentes dans la compilation finale que cela ne vaut pas la peine de s'inquiéter du tout. Si vous faites quelque chose d'aussi cher (par rapport au genre de différences de jeu ici) que la sérialisation de quelque chose sur une chaîne, c'est particulièrement inutile.
Jon Hanna
Il manque une chose, peut-être évidente, mais importante, dans cette réponse par ailleurs excellente: une méthode d'instance nécessite une instance, et la création d'une instance n'est pas bon marché. Même une valeur par défaut ctornécessite toujours l'initialisation de tous les champs. Une fois que vous avez déjà une instance, cette réponse s'applique ("toutes choses égales par ailleurs"). Bien sûr, une cctorméthode coûteuse peut également ralentir les méthodes statiques, mais ce n'est que lors du premier appel et s'applique également aux méthodes d'instance. Voir aussi docs.microsoft.com/en-us/previous-versions/dotnet/articles/…
Abel
8

Lorsque conserver l'état n'est pas un problème (aucun champ ou propriété n'est requis), est-il toujours préférable d'utiliser une classe statique?

Je dirais, oui. En déclarant quelque chose, staticvous déclarez une intention d'exécution sans état (ce n'est pas obligatoire, mais une intention de quelque chose auquel on pourrait s'attendre)

Là où il y a un nombre considérable de ces classes statiques (disons 100 par exemple, avec un certain nombre de méthodes statiques chacune) cela affectera-t-il négativement les performances d'exécution ou la consommation de mémoire par rapport au même nombre de classes d'instance?

Ne le pensez pas, sauf si vous êtes sûr que les classes statiques sont vraiment stetless, car sinon, il est facile de gâcher les allocations de mémoire et d'obtenir des fuites de mémoire.

Lorsque le mot clé [this] est utilisé pour appeler une autre méthode dans la même classe d'instance, la résolution d'instance se produit-elle toujours?

Pas sûr, à propos de ce point (il s'agit uniquement d'un détail de mise en œuvre de CLR), mais pensez que oui.

Tigran
la source
Les méthodes statiques ne peuvent pas être moquées.Si vous faites du TDD ou même simplement des tests unitaires, cela nuira beaucoup à vos tests.
trampster
@trampster Pourquoi? C'est juste un morceau de logique. Vous pouvez facilement vous moquer de ce que vous lui donnez? Pour obtenir un comportement correct. Et beaucoup de méthodes statiques seront de toute façon des éléments de logique privés dans une fonction.
M. Mimpen
@ M.Mimpen tant que vous le laissez à de petites pièces privées, votre amende, si c'est une méthode publique et que vous l'utilisez à partir d'autres se ferme et que vous devez changer ce qu'il fait dans votre test, alors votre blocage, des choses comme l'accès aux fichiers ou à la base de données ou les appels réseau, etc., s'ils sont mis en méthode statique, ils deviendront non moquables, à moins que vous ne disiez que vous injectez une dépendance moquable en tant que paramètre de la méthode statique
trampster
-2

les méthodes statiques sont plus rapides mais moins POO, si vous allez utiliser des modèles de conception méthode statique probablement un mauvais code, pour mieux écrire la logique métier sans statique, des fonctions communes comme la lecture de fichiers, WebRequest, etc. mieux se rendre compte comme statique ... vos questions n'ont pas d'universel répondre

burn_LEGION
la source
16
Vous n'avez donné aucun argument à vos affirmations.
ymajoros
2
@ fjch1997 2 votants semblent penser le contraire (pour ce que ça vaut). Commenter les votes
négatifs