web-dev-qa-db-fra.com

java combien coûte un appel de méthode

Je suis débutant et j'ai toujours lu qu'il était mauvais de répéter du code. Cependant, il semble que pour ne pas le faire, vous devrez généralement avoir des appels de méthode supplémentaires. Disons que j'ai la classe suivante

public class BinarySearchTree<E extends Comparable<E>>{
    private BinaryTree<E> root;
    private final BinaryTree<E> EMPTY = new BinaryTree<E>();
    private int count;
    private Comparator<E> ordering;

    public BinarySearchTree(Comparator<E> order){
        ordering = order;
        clear();
    }

    public void clear(){
        root = EMPTY;
        count = 0;
    }
}

Serait-il plus optimal pour moi de simplement copier et coller les deux lignes de ma méthode clear () dans le constructeur au lieu d'appeler la méthode réelle? Si oui, quelle différence cela fait-il? Que se passe-t-il si mon constructeur effectue 10 appels de méthode avec chacun en définissant simplement une variable d'instance sur une valeur? Quelle est la meilleure pratique de programmation?

76
jhlu87

Serait-il plus optimal pour moi de simplement copier et coller les deux lignes de ma méthode clear () dans le constructeur au lieu d'appeler la méthode réelle?

Le compilateur peut effectuer cette optimisation. Et la JVM aussi. La terminologie utilisée par le rédacteur du compilateur et les auteurs de la JVM est "expansion en ligne".

Si oui, quelle différence cela fait-il?

Mesure le. Souvent, vous constaterez que cela ne fait aucune différence. Et si vous croyez qu'il s'agit d'un point chaud de performance, vous cherchez au mauvais endroit; c'est pourquoi vous devrez le mesurer.

Que se passe-t-il si mon constructeur effectue 10 appels de méthode avec chacun en définissant simplement une variable d'instance sur une valeur?

Encore une fois, cela dépend du bytecode généré et des optimisations d'exécution effectuées par la machine virtuelle Java. Si le compilateur/la JVM peut aligner les appels de méthode, il effectuera l'optimisation pour éviter la surcharge de création nouveaux cadres de pile au moment de l'exécution.

Quelle est la meilleure pratique de programmation?

Éviter l'optimisation prématurée. La meilleure pratique consiste à écrire du code lisible et bien conçu, puis à optimiser les points chauds de performance de votre application.

70
Vineet Reynolds

Ce que tout le monde a dit sur l'optimisation est absolument vrai.

Il n'y a aucune raison du point de vue des performances pour aligner la méthode. S'il s'agit d'un problème de performances, le JIT de votre machine virtuelle Java l'intègre. En Java, les appels de méthode sont si proches de la gratuité que cela ne vaut pas la peine d'y penser.

Cela étant dit, il y a un problème différent ici. À savoir, il est mauvaise pratique de programmation d'appeler une méthode redéfinissable (c'est-à-dire, qui n'est pas final, static ou private) à partir du constructeur. (En vigueur Java, 2e éd., P. 89 dans l'article intitulé "Concevoir et documenter l'héritage ou bien l'interdire")

Que se passe-t-il si quelqu'un ajoute une sous-classe de BinarySearchTree appelée LoggingBinarySearchTree qui remplace toutes les méthodes publiques avec du code comme:

public void clear(){
  this.callLog.addCall("clear");
  super.clear();
}

Alors le LoggingBinarySearchTree ne sera jamais constructible! Le problème est que this.callLog sera null lorsque le constructeur BinarySearchTree sera en cours d'exécution, mais le clear qui sera appelé sera le surchargé, et vous obtiendrez un NullPointerException .

Notez que Java et C++ diffèrent ici: en C++, un constructeur de superclasse qui appelle une méthode virtual finit par appeler celui défini dans la superclasse, pas le surchargé. entre les deux langues l'oublient parfois.

Compte tenu de cela, je pense qu'il est probablement plus propre dans votre cas d'inclure la méthode clearlorsqu'elle est appelée depuis le constructeur, mais en général en Java vous devriez allez-y et faites tous les appels de méthode que vous voulez.

18
Daniel Martin

Je le laisserais définitivement tel quel. Que faire si vous modifiez la logique clear()? Il serait impossible de trouver tous les endroits où vous avez copié les 2 lignes de code.

5
Marcelo

La meilleure pratique consiste à mesurer deux fois et à couper une fois.

Une fois que vous avez perdu du temps, vous ne pourrez plus jamais le récupérer! (Alors, mesurez-le d'abord et demandez-vous si cela vaut la peine d'être optimisé. Combien de temps réel économiserez-vous?)

Dans ce cas, le Java VM fait probablement déjà l'optimisation dont vous parlez).

3
Arafangion

D'une manière générale (et en tant que débutant, cela signifie toujours!), Vous ne devriez jamais faire de micro-optimisations comme celle que vous envisagez. Privilégiez toujours la lisibilité du code à des choses comme celle-ci.

Pourquoi? Parce que le compilateur/hotspot fera ces sortes d'optimisations pour vous à la volée, et bien d'autres encore. Si quoi que ce soit, lorsque vous essayez de faire des optimisations dans ce sens (mais pas dans ce cas), vous ralentirez probablement les choses. Hotspot comprend les idiomes de programmation courants, si vous essayez de faire cette optimisation vous-même, il ne comprendra probablement pas ce que vous essayez de faire, il ne pourra donc pas l'optimiser.

Le coût de maintenance est également beaucoup plus élevé. Si vous commencez à répéter du code, cela nécessitera beaucoup plus d'efforts, ce qui sera probablement beaucoup plus compliqué que vous ne le pensez!

En passant, vous pouvez arriver à certains points de votre vie de codage où vous devez effectuer des optimisations de bas niveau - mais si vous atteignez ces points, vous saurez certainement, définitivement le moment venu. Et si vous ne le faites pas, vous pouvez toujours revenir en arrière et optimiser plus tard si vous en avez besoin.

3
Michael Berry

coût d'un appel de méthode est la création (et l'élimination) d'un cadre de pile et de quelques expressions de code supplémentaires si vous avez besoin de transmettre des valeurs à la méthode.

2
Andreas_D

Le modèle que je suis, est de savoir si cette méthode en question satisferait ou non à l'une des suivantes:

  • Serait-il utile d'avoir cette méthode disponible en dehors de cette classe?
  • Serait-il utile d'avoir cette méthode disponible dans d'autres méthodes?
  • Serait-il frustrant de réécrire cela chaque fois que j'en avais besoin?
  • La polyvalence de la méthode pourrait-elle être augmentée avec l'utilisation de quelques paramètres?

Si l'une des réponses ci-dessus est vraie, elle doit être enveloppée dans sa propre méthode.

1
Peaches491

Conservez la méthode clear() lorsqu'elle facilite la lisibilité. Avoir du code non maintenable est plus cher.

1
Fabian Barney

L'optimisation des compilateurs permet généralement de supprimer la redondance de ces opérations "supplémentaires"; dans de nombreux cas, la différence entre du code "optimisé" et du code simplement écrit comme vous le souhaitez et exécuté via un compilateur d'optimisation est nulle; c'est-à-dire que le compilateur d'optimisation fait généralement un aussi bon travail que vous le feriez, et il le fait sans provoquer de dégradation du code source. En fait, plusieurs fois, le code "optimisé à la main" finit par être MOINS efficace, car le compilateur prend en compte de nombreuses choses lors de l'optimisation. Laissez votre code dans un format lisible et ne vous inquiétez pas de l'optimisation jusqu'à une date ultérieure.

"L'optimisation prématurée est la racine de tout Mal." - Donald Knuth

1
Paul Sonier

Je ne m'inquiéterais pas autant de l'appel de méthode que de la logique de la méthode. S'il s'agissait de systèmes critiques, et que le système devait être "rapide", je chercherais à optimiser les codes dont l'exécution est longue.

0
Buhake Sindi

Comme d'autres l'ont dit, le coût de l'appel de méthode est trivial à nada, car le compilateur l'optimisera pour vous.

Cela dit, il y a des dangers à faire des appels de méthode aux méthodes d'instance à partir d'un constructeur. Vous courez le risque de mettre à jour ultérieurement la méthode d'instance afin qu'elle puisse essayer d'utiliser une variable d'instance qui n'a pas encore été initiée par le constructeur. Autrement dit, vous ne voulez pas nécessairement séparer les activités de construction du constructeur.

Une autre question - votre méthode clear () définit la racine sur EMPTY, qui est initialisée lorsque l'objet est créé. Si vous ajoutez ensuite des nœuds à EMPTY, puis appelez clear (), vous ne réinitialiserez pas le nœud racine. Est-ce le comportement que vous souhaitez?

0
Matthew Flynn

Compte tenu de la mémoire des ordinateurs modernes, cela est très peu coûteux. Il est toujours préférable de diviser votre code en méthodes afin que quelqu'un puisse lire rapidement ce qui se passe. Cela aidera également à réduire les erreurs dans le code si l'erreur est limitée à une seule méthode avec un corps de quelques lignes.

0
adamjmarkham