web-dev-qa-db-fra.com

Pourquoi Java n'a-t-il pas du tout d'optimisation pour la récursion de queue?

D'après ce que j'ai lu: La raison en est qu'il n'est pas facile de déterminer quelle méthode sera effectivement appelée car nous avons l'héritage.

Cependant, pourquoi Java au moins a-t-il au moins une optimisation de récursivité de queue pour les méthodes statiques et n'applique-t-il pas la bonne façon d'appeler des méthodes statiques avec le compilateur?

Pourquoi Java n'a-t-il aucun support pour la récursivité de queue?

Je ne sais pas s'il y a du tout des difficultés ici.


Concernant le doublon suggéré , comme expliqué par Jörg W Mittag1:

  • L'autre question porte sur le TCO, celle-ci sur le TRE. TRE est beaucoup plus simple que TCO.
  • En outre, l'autre question demande quelles limitations la JVM impose aux implémentations de langage qui souhaitent compiler vers la JVM, cette question concerne Java, qui est le seul langage qui n'est pas restreint par la JVM, puisque la spécification JVM peut être modifiée par les mêmes personnes qui conçoivent Java.
  • Et enfin, il n'y a même pas de restriction dans la JVM à propos de TRE, car la JVM a GOTO intra-méthode, ce qui est tout ce qui est nécessaire pour TRE

1Formatage ajouté pour annoncer les remarques faites.

97
InformedA

Comme expliqué par Brian Goetz (Java Language Architect chez Oracle) dans cette vidéo :

dans les classes jdk, [...] il existe un certain nombre de méthodes sensibles à la sécurité qui reposent sur le comptage des trames de pile entre le code de la bibliothèque jdk et le code appelant pour déterminer qui les appelle.

Tout ce qui a changé le nombre d'images sur la pile romprait cela et provoquerait une erreur. Il admet que c'était une raison stupide, et donc les développeurs JDK ont depuis remplacé ce mécanisme.

Il mentionne ensuite que ce n'est pas une priorité, mais que la récursivité de la queue

finira par se faire.

N.B. Cela s'applique à HotSpot et à OpenJDK, les autres machines virtuelles peuvent varier.

138
ggovan

Java n'a pas d'optimisation des appels de queue pour la même raison que la plupart des langages impératifs ne l'ont pas. Les boucles impératives sont le style préféré du langage, et le programmeur peut remplacer la récursivité de queue par des boucles impératives. La complexité n'en vaut pas la peine pour une fonctionnalité dont l'utilisation est déconseillée par style.

Cette chose où les programmeurs veulent parfois écrire dans un style FP dans des langages autrement impératifs n'est entrée en vogue qu'au cours des 10 dernières années, après que les ordinateurs ont commencé à évoluer en cœurs au lieu de GHz. Même maintenant, ce n'est pas si populaire. Si je proposais de remplacer une boucle impérative par une récursion de queue au travail, la moitié des réviseurs de code riraient et l'autre moitié donnerait un air confus. Même dans la programmation fonctionnelle, vous évitez généralement la récursivité de queue à moins que d'autres constructions comme les fonctions d'ordre supérieur ne correspondent pas correctement.

24
Karl Bielefeldt

Java n'a pas d'optimisation des appels longs car la JVM n'a pas de bytecode pour les appels de queue (pour certains statiquement inconn pointeur de fonction, par exemple une méthode dans certains vtables).

Cela ressemble à des raisons sociales (et peut-être techniques), l'ajout d'une nouvelle opération de bytecode dans la JVM (qui la rendrait incompatible avec les versions antérieures de cette JVM) est terriblement difficile pour le propriétaire de la spécification JVM.

Les raisons techniques de ne pas ajouter de nouveau bytecode dans la spécification JVM incluent le fait que les implémentations JVM réelles sont des éléments logiciels extrêmement complexes (par exemple en raison des nombreuses optimisations JIT qu'il fait).

Les appels de queue à une fonction inconnue nécessitent le remplacement de la trame de pile actuelle par une nouvelle, et cette opération doit se trouver dans la JVM (il ne s'agit pas seulement de changer le compilateur de génération de bytecode).

5

À moins qu'un langage n'ait une syntaxe spéciale pour effectuer un appel de queue (récursif ou autre) et qu'un compilateur émette un cri quand un appel de queue est demandé mais ne peut pas être généré, l'optimisation "facultative" de l'appel de queue ou de la récursion de queue générera des situations où une pièce de code peut nécessiter moins de 100 octets de pile sur une machine, mais plus de 100 000 000 octets de pile sur une autre. Une telle différence devrait être considérée comme qualitative plutôt que simplement quantitative.

Les machines peuvent avoir des tailles de pile différentes, et il est donc toujours possible pour le code de fonctionner sur une machine mais de souffler la pile sur une autre. En général, cependant, le code qui fonctionnera sur une machine même lorsque la pile est artificiellement restreinte fonctionnera probablement sur toutes les machines avec des tailles de pile "normales". Si, cependant, une méthode qui se répète à 1 000 000 de profondeur est optimisée pour les appels de queue sur une machine mais pas sur une autre, l'exécution sur l'ancienne machine fonctionnera probablement même si sa pile est inhabituellement petite et échouera sur cette dernière même si sa pile est inhabituellement grande .

4
supercat

Je pense que la récursivité des appels de queue n'est pas utilisée dans Java principalement parce que cela modifierait les traces de la pile et rendrait donc le débogage d'un programme beaucoup plus difficile. Je pense que l'un des principaux objectifs de Java est de permettre aux programmeurs pour déboguer facilement leur code, et le traçage de pile est essentiel pour ce faire, en particulier dans un environnement de programmation hautement orienté objet. Comme l'itération pourrait être utilisée à la place, le comité du langage doit avoir pensé que cela ne valait pas la peine d'ajouter une récursivité de queue.

2
annoying_squid