Java utilise getter dans for loop ou crée une variable locale? [dupliquer]


Cette question a déjà une réponse ici:

J'ai une boucle qui s'exécute 4096 fois et il devrait être aussi rapide que possible. La performance est vraiment importante ici. Actuellement, j'utilise des méthodes getter dans la boucle qui renvoient simplement des valeurs ou des objets à partir de champs qui ne changent pas pendant le la boucle est en cours.

Exemple:

for (;;) {
    doSomething(example.getValue());
}

Y a-t-il des frais généraux utilisant des getters? Est-ce plus rapide en utilisant la manière suivante?

Exemple:

Object object = example.getValue();
for (;;) {
    doSomething(object);
}

Si oui, est-ce également vrai pour accéder à des champs publics comme example.value?

Edit: Je n'utilise pas System.out.println() dans la boucle.

Edit: Certains champs ne sont pas final. Aucun champ n'est volatile et aucune méthode (getter) n'est synchronized.

Author: stonar96, 2016-04-11

5 answers

Comme Rogério a répondu à , obtenir la référence d'objet en dehors de la boucle (Object object = example.getValue();) sera probablement plus rapide (ou ne sera jamais plus lent) que d'appeler le getter à l'intérieur de la boucle car

  • dans le "pire" cas, example.getValue()pourrait en fait faire des choses très coûteuses en calcul en arrière-plan malgré que les méthodes getter sont censées être "triviales". En attribuant une référence une fois et en la réutilisant, vous ne faites que ce calcul coûteux lorsque.
  • dans le "meilleur" cas, example.getValue() fait quelque chose de trivial tel que return value;et donc l'affecter à l'intérieur de la boucle ne serait pas plus cher qu'en dehors de la boucle après que le compilateur JIT inline le code.

Cependant, plus important est la différence de sémantique entre les deux et ses effets possibles dans un environnement multithread: Si l'état de l'objet example change d'une manière qui amène example.getValue() à renvoyer des références à des objets différents, il est possible que, à chaque itération, la méthode doSomething(Object object) fonctionnera réellement sur une instance différente de Object en appelant directement doSomething(example.getValue());. D'autre part, en appelant une lecture en dehors de la boucle et de la définition d'une référence à l'instance retournée (Object object = example.getValue();), doSomething(object); fonctionne sur object n fois pour n itérations.

Cette différence de sémantique peut entraîner un comportement radicalement différent dans un environnement multithread de celui dans un environnement mono-thread. En outre, cette il n'est pas nécessaire qu'il s'agisse d'un problème multi-threading "en mémoire": Si example.getValue() dépend par exemple des ressources de base de données/disque dur/réseau, il est possible que ces données changent pendant l'exécution de la boucle, ce qui permet de renvoyer un objet différent même si l'application Java elle-même est mono-thread. Pour cette raison, il est préférable de considérer ce que vous voulez accomplir avec votre boucle, puis de choisir l'option qui reflète le mieux le comportement prévu.

 7
Author: errantlinguist, 2017-05-23 12:09:35

Cela dépend du getter.

S'il s'agit d'un simple getter, le JIT l'alignera de toute façon sur un accès direct au champ, donc il n'y aura pas de différence mesurable. Du point de vue du style, utilisez le getter - c'est moins de code.

Si le getter accède à un champ volatile, il y a un accès mémoire supplémentaire car la valeur ne peut pas être conservée dans le registre, mais l'accès est très petit.

Si le getter est synchronized, l'utilisation d'une variable locale sera mesurablement plus rapide car les verrous ne le font pas besoin d'être obtenu et libéré à chaque appel, mais le code de boucle utilisera la valeur potentiellement périmée du champ au moment où le getter a été appelé.

 5
Author: Bohemian, 2016-04-11 18:57:43

Vous devriez préférer une variable locale en dehors de la boucle, pour les raisons suivantes:

  1. Il a tendance à rendre le code plus facile à lire/comprendre, en évitant les appels de méthode imbriqués comme doSomething(example.getValue()) dans une seule ligne de code, et en permettant au code de donner un meilleur nom, plus spécifique, à la valeur renvoyée par la méthode getter.
  2. Toutes les méthodes getter ne sont pas triviales (c'est-à-dire qu'elles font parfois un travail potentiellement coûteux), mais les développeurs ne le remarquent souvent pas, en supposant une méthode donnée est trivial et peu coûteux quand ce n'est pas vraiment le cas. Dans de tels cas, le code peut prendre un coup de performance significatif sans que le développeur s'en rende compte. L'extraction dans une variable locale tend à éviter ce problème.
 4
Author: Rogério, 2016-04-11 19:02:07

Il est très facile de s'inquiéter des performances beaucoup plus que nécessaire. Je connais ce sentiment. Quelques points à considérer:

  1. 4096 n'est pas beaucoup, donc à moins que cela ne doive se terminer dans un temps extrêmement court, ne vous inquiétez pas tant des performances.
  2. S'il y a autre chose qui coûte cher dans cette boucle, le getter n'aura pas d'importance.
  3. L'optimisation prématurée est la racine de tout mal. Concentrez-vous d'abord sur la correction et la clarté de votre code. Puis de mesurer et de profil et réduisez la chose la plus chère, et prenez soin de cela. Améliorer l'algorithme réel si possible.

En ce qui concerne votre question, je ne sais pas exactement ce que fait le JIT, mais à moins qu'il ne puisse prouver avec certitude que example.getValue() ou example.value ne change pas dans la boucle (ce qui est difficile à faire sauf si le champ est final et que le getter est trivial) alors il n'y a logiquement aucun moyen programme. Les appels répétés sont certainement une quantité non nulle de travail supplémentaire.

Cela dit, créez la variable locale en dehors de la boucle, qu'elle soit plus rapide ou non, car elle est plus claire. Peut-être que cela vous surprend, mais un bon code n'est pas toujours le plus court. Exprimer son intention et d'autres informations est extrêmement important. Dans ce cas, la variable locale en dehors de la boucle rend évident pour quiconque lit le code que l'argument de doSomething ne change pas (surtout si vous le faites finale) ce qui est utile à savoir. Sinon, ils pourraient avoir à faire des recherches supplémentaires pour s'assurer qu'ils savent comment le programme se comporte.

 1
Author: Alex Hall, 2016-04-11 19:05:17

Si vous devez l'exécuter aussi vite que possible, vous ne devez pas utiliser System.out.println dans les sections critiques.

Concernant getter: Il y a une légère surcharge pour l'utilisation de getter, mais vous ne devriez pas vous en soucier. Java a une optimisation getter et setter dans le compilateur JIT. Donc finalement, ils seront remplacés par du code natif.

 0
Author: Denis Kokorin, 2016-04-11 18:44:48