- Tail call : appel de fonction effectué juste avant le retour d’une fonction. Lorsqu’une optimisation de tail call se produit, l’instruction
jmp est utilisée pour réduire la pile d’appels.
- Avantages :
- Réduit l’utilisation de mémoire de pile de O(n) à O(1).
- Élimine le surcoût de performance des appels de fonction, ce qui permet de l’utiliser comme structure de contrôle itérative efficace.
Problèmes de la boucle d’interpréteur
- Problèmes :
- Plus une fonction grossit et plus le flux de contrôle se complexifie, plus il devient difficile de conserver les données importantes dans les registres.
- Lorsque le chemin rapide et le chemin lent sont mélangés, la qualité du code se dégrade.
Améliorer la boucle d’interpréteur avec les tail calls
- Solution : utiliser des tail calls pour séparer chaque tâche en petites fonctions, chaque fonction appelant la tâche suivante via un tail call.
- Avantages :
- Permet de contrôler l’allocation des registres.
- Maintient la qualité du code en séparant le chemin rapide du chemin lent.
- Permet d’optimiser des séquences d’instructions indépendantes.
Limites
- Problème en présence d’appels non tail call : s’il existe des appels qui ne sont pas des tail calls, des stack frames sont créées et les données sont stockées sur la pile, ce qui dégrade les performances.
- Gestion d’exceptions complexe : lorsque la gestion des exceptions est complexe, la duplication du code et sa complexité augmentent.
- Problème de portabilité : l’attribut
musttail n’étant pas standard, il n’est pas pris en charge par tous les compilateurs.
Résumé GN⁺
- L’optimisation des tail calls joue un rôle important dans l’amélioration des performances, avec des résultats particulièrement marquants pour l’analyse de Protobuf.
- Cette technique peut aussi s’appliquer aux principaux interpréteurs de langages écrits en C (Python, Ruby, PHP, Lua, etc.).
- Le problème de portabilité de l’attribut
musttail reste un point à résoudre.
- Parmi les projets offrant des fonctionnalités similaires, on peut citer LuaJIT et l’interpréteur WebAssembly wasm3.
1 commentaires
Discussion sur Hacker News
Une proposition pour la norme C inclut les appels de queue sous la forme
return goto (expression);[[musttail]], cela garantit la durée de vie des objets locaux et n’exige pas d’analyse d’échappement étenduePour les amateurs de Rust, il existait une ancienne RFC visant à ajouter le mot-clé
becomeEn C++, les interpréteurs accélèrent principalement en utilisant des computed goto
Le problème du changement de contexte via des appels de queue nécessite des fonctions qui utilisent des conventions d’appel
Espoir de voir l’attribut
[[musttail]]se diffuser à GCC, Visual C++ et d’autres compilateurs populaires[[musttail]]à GCC est en coursEn évoquant le support C++, il est souligné que C++ a très peu d’appels de queue
Quelqu’un se demande ce qui se passe si une exception est levée dans une fonction C++
[[musttail]]Il est noté que, pour des exemples simples,
__attribute__((musttail))n’est pas nécessaire pour obtenir une bonne génération de codeQuelqu’un s’interroge sur la vitesse d’une approche utilisant un trampoline, où un pointeur de fonction retourné est appelé depuis une boucle externe
Une demande vise à clarifier l’exemple d’un chemin d’exception encapsulé avec
[[musttail]][[musttail]]empêche la construction de frames de pile et le spill des registres