Dans cette nouvelle version, les choses qui font plaisir :
- Par rapport à la version Q2, gain en utilisation mémoire de 930% ;
- Gain de performance par rapport à la version 2009Q2 entre 15 et 70% suivant les benchmarks ;
- Intégration avec GDB 7.0 permettant de débugger le code passé par le compilateur JIT ;
- Intégration avec OProfile 0.9.4 afin de fournir une interface de profilage sans difficulté qui couvre le C et le Python ;
- Beaucoup de bugs et de restrictions du compilateur JIT de LLVM ont été corrigés au passage ;
- Unladen Swallow 2009Q3 passe toutes les suites de test de projets Python majeurs tel que Twisted, Django, Numpy ou Swing (cf la liste de tests pour la liste complète des suites de test).
Les choses qui font moins plaisir :
- Le compilateur JIT de LLVM et d'autres outils ont demandé plus de travail qu'imaginé au départ. En conséquence, le projet n'a pas progressé autant en performance que les auteurs l'auraient voulu ;
- L'utilisation mémoire reste entre deux et trois fois supérieure à celle de Python 2.6.1. Cependant, il y a des pistes intéressantes pour réduire cela d'ici la version 2009Q4 .
L'augmentation très importante de la consommation mémoire qui avait fait tiquer lors de la release Q2 a été retravaillée. L'intégration de LLVM étant terminée, le projet commence à tirer partie de l'infrastructure de JIT et il reste encore pas mal de marge de progression sur ce plan.
J'ai personnellement hâte de voir la suite, notamment vis à vis des autres objectifs d'améliorations, comme la suppression du GIL (Great Interpreter Lock, Grand verrou de l'interpréteur - qui empêche Python d'exploiter le modèle de threads à son maximum).
Ce que j'apprécie dans ce projet est la rigueur de l'approche. Les auteurs ont commencé par aligner une suite considérable de benchmarks pour mesurer les performances de l'interpréteur. Jusqu'à présent, les gains de performance en Python étaient mesurés en général à l'aide de deux benchmarks, PyStone et PyBench dont les résultats étaient largement discutables (voire plus que discutable pour PyBench).
Puisque le but d'Unladen Swallow est d'accélérer des applications réelles, ils ont pris tous les projets majeurs et utilisent leur suite de non régression comme indicatif de benchmark. En plus de fournir une mesure en utilisation plus réelle, cela garantit aussi la compatibilité à 100% avec le Python existant.
# Unladen Swallow ?
Posté par zebra3 . Évalué à 5.
Blague à part, le gain en mémoire de 930% peut sembler spectaculaire, mais il est si élevé que àa laisse penser que les développeurs avaient laissé passer plein de fuites mémoire sur la Q2. M'enfin, beau boulot quand même.
Article Quarante-Deux : Toute personne dépassant un kilomètre de haut doit quitter le Tribunal. -- Le Roi de Cœur
[^] # Re: Unladen Swallow ?
Posté par Boa Treize (site web personnel) . Évalué à 2.
[^] # Re: Unladen Swallow ?
Posté par JGO . Évalué à 9.
[^] # Re: Unladen Swallow ?
Posté par Moonz . Évalué à 7.
[^] # Re: Unladen Swallow ?
Posté par Sylvain Sauvage . Évalué à 9.
Quand on passe de 100 à 80, on dit que la diminution est de 20%, pas de 25%.
Quand on compare, on compare par rapport à l’existant, aux précédents. Donc la formule est différence / valeur précédente.
On ne calcule jamais avec la valeur finale en rapport.
La valeur précédente est la valeur de référence parce qu’elle était connue avant. C’est ce que « précédent » veut dire.
C’est gentil de vouloir grossir les chiffres, certains y arrivent sans que ça se voie mais 930%, c’est tellement ridicule que ça saute aux yeux de tout le monde. Résultat, perte de crédibilité.
[^] # Re: Unladen Swallow ?
Posté par Philippe F (site web personnel) . Évalué à 7.
Normalement, l'interpréteur Python convertit un programme en bytecode python, conserve ce bytecode en mémoire et l'éxecute dans sa VM. La consommation de mémoire de Python correspond grosso-modo à la mémoire utilisée par la VM, plus tous les modules sous forme de bytecode.
Ce qui se passe avec la couche LLVM que Unladen Swallow a rajouté, c'est que certains morceaux de bytecode qui sont exécutés très souvent deviennent Hot et sont convertis en un autre bytecode, le bytecode de LLVM (qui s'appelle IR pour Intermediate Representation). Unladen Swallow conserve donc en mémoire le bytecode IR des modules optimisés. Ensuite, lorsque LLVM exécute le bytecode IR, il applique d'une part des optimisations, d'autre part il observe la fréquence d'utilisation des morceaux d'IR et peut décider si certains morceaux reviennent très souvent de balancer son compilateur JIT, qui lui va transformer l'IR en assembleur optimisé à donf.
On peut donc se retrouver facilement avec en mémoire pour chaque module le bytecode Python, le bytecode IR et l'assembleur. Ca va même plus loin puisque pour optimiser l'IR, les variables Python utilisées sont présentées comme constantes à la couche LLVM. Cela permet de faire un travail d'optimisation plus poussé, mais ça contraint à surveiller lesdites variables. Si certaines valeurs changent, l'IR n'est plus valide, il faut donc relancer une phase de compilation de l'IR et se débarasser de l'IR précédent, devenu obsolète. Et bien sur, faire cette surveillance sur les variables python implique de leur rajouter quelques champs afin de surveiller leur variation, donc augmenter la taille de certains objets Python. Il me semble qu'il y a aussi un cache au niveau de l'IR.
Pour la version Q2, l'objectif était de mettre en place toute cette infrastructure de compilation. C'est assez complexe comme vous pouvez le constater et la problématique de la mémoire a été laissé de côté pour cette phase. Il faut bien commencer par quelque part.
Au point que dans certaines versions de Unladen Swallow, l'IR obsolète n'était pas déchargé tout de suite de la mémoire.
Donc comme vous le voyez, c'est facile de consommer un max de mémoire avec cette architecture. Fondamentalement, Unladen Swallow consommera toujours plus de mémoire que le même code Python, ne serait-ce que par l'instrumentation du bytecode Python nécessaire pour déclencher correctement la partie LLVM.
Par contre, avec la release Q3, ils ont commencé à regarder d'un peu plus près les grosses sources de consommation, d'où ce gain phénoménal. Pas plus tard qu'avant hier, il y a eu des modifications pour faire encore des gains de consommation mémoire, donc on peut imaginer que la version Q4 rentrera dans le domaine du raisonnable.
[^] # Re: Unladen Swallow ?
Posté par Philippe F (site web personnel) . Évalué à 2.
On peut imaginer que dans la partie hot d'un programme, on s'amuse moins à changer le type des variables ou les faire référer vers d'autres objets.
Exemple qui marcherait dans Unladen Swallow :
def main_loop() :
....data_to_process[:] = fetch_data()
....while len(data_to_process):
........idx_to_remove = extract_some_data( data_to_process )
........for idx in idx_to_remove :
............del data_to_process[ idx ]
Dans cet exemple l'objet data_to_process varie mais pointe toujours vers la même liste. LLVM pourra donc optimiser l'indexation des objets dans la liste et l'accès à la longueur.
[^] # Re: Unladen Swallow ?
Posté par Antoine . Évalué à 2.
[^] # Re: Unladen Swallow ?
Posté par Philippe F (site web personnel) . Évalué à 1.
Je préfèrerai stocker des références aux objets à supprimer. Mais il me semble que l'implémentation des listes en Python est basé sur un tableau, donc c'est pas possible.
En passant par un ensemble, je pourrais activer un algo en O(n), non ?
[^] # Re: Unladen Swallow ?
Posté par Antoine . Évalué à 3.
En effet, ce qui est coûteux ce n'est pas l'indexation (O(1)) mais la suppression (O(n)). Note que la suppression à la fin (pop()) est peu coûteuse.
En passant par un ensemble, je pourrais activer un algo en O(n), non ?
Ca dépend si tu as besoin de l'indexation, mais, oui, tu pourrais.
[^] # Re: Unladen Swallow ?
Posté par Antoine . Évalué à 3.
[^] # Re: Unladen Swallow ?
Posté par nerbrume . Évalué à 1.
Du coup, j'ai une question :
Est-il possible de completement "traduire" en assembleur "optimisé a donf" un programme python ? Parcequ'ensuite, a la limite, que le compilo prenne 10 fois plus de temps, on s'en fiche, si après on a les perf d'un langage compilé comme le C.
Ou alors, y'a encore un truc que je saisis pas...
[^] # Re: Unladen Swallow ?
Posté par Philippe F (site web personnel) . Évalué à 1.
Non, pas vraiment. Même avec Unladen Swallow, c'est uniquement la partie de Python qui analyse un opcode Python et décide quelle fonction en C de Python appeler qui est passée par LLVM.
Ca veut dire que si l'implémentation en Python d'une opération est lente, elle restera lente. Par contre son appel l'appel de cette fonction pourra être optimisé pour devenir plus rapide via LLVM qu'elle ne l'était en C via Python.
On peut forcer la compilation en LLVM de toutes les modules Python (et avoir une consommation mémoire en x10), mais je sais pas si on peut forcer l'utilisation du JIT. Je suis pas sur que ça aie un sens, puisque le JIT est efficace justement parce qu'il ne travaille que sur une petite partie du code et peut donc faire des optimisations non globales.
[^] # Re: Unladen Swallow ?
Posté par Antoine . Évalué à 2.
http://www.cython.org/
# Python et vm de sun
Posté par collinm (site web personnel) . Évalué à 3.
www.solutions-norenda.com
[^] # Re: Python et vm de sun
Posté par ribwund . Évalué à 2.
[^] # Re: Python et vm de sun
Posté par Philippe F (site web personnel) . Évalué à 1.
IronPython, l'implémentation de Python pour le CLR de .NET est en revanche significativement plus rapide que CPython :
http://ironpython.codeplex.com/wikipage?title=IP26RC1VsCPy26(...)
La preuve, si les gens en doutaient encore, qu'un compilateur à qui on passe les bonnes informations peut faire un travail d'optimisation plus poussé qu'un être humain.
# C'est ambitieux... mais si ça marche...
Posté par lolop (site web personnel) . Évalué à 6.
Goals
3. Maintain source-level compatibility with CPython applications.
4. Maintain source-level compatibility with CPython extension modules.
Sachant cela, sachant aussi que les parties critiques des traitements lourds sont déjà écrits en C donc exécuté avec du code natif.
Comme ils restent avec la compatibilité CPython, ils vont juste remplacer l'interprétation de byte-code par du code déjà compilé en natif.
Mais, ils se heurteront au lock global et à sa prise/libération, s'ils réussissent à le contourner et que ça fonctionne encore bien, chapeau (et GVR sera sûrement très intéressé) [*].
Ils se heurteront aussi à tous les moments où le programme passe par de la résolution de noms, inévitable si l'on veut conserver la souplesse qu'apporte ce mode de fonctionnement de Python.
Je vois bien un intérêt fort pour ce qui est boucles, pour la compilation native de certains modules standards actuellement en code Pyhton. Aller plus loin nécessiterais de donner au compilateur des indications plus complètes (genre variable typée entier ou chaîne, où l'on pourrait alors court-circuiter la résolution de noms et passer directement à du code de calcul natif...) qui peuvent faire gagner beaucoup, mais éloignent du langage dynamique que l'on connaît.
Je suis franchement curieux de voir comment tout ça va évoluer.
Et une petite mise en garde pour les détracteurs "python est trop lent": même avec un tel projet en fonctionnement, un programme Python avec le côté dynamique du langage ne pourra jamais aller plus vite qu'un programme équivalent en langage style C/C++/Fortran... bien codé (j'insiste là dessus) [**].
Après, combien de temps pour bien coder en Python, et combien de temps pour bien coder en C/C++/Fortran... et quel est le ratio de code sur lequel le programme passe 90% de son temps...
[*] A lire dans les liens donnés: http://code.google.com/p/unladen-swallow/wiki/ProjectPlan, par exemple ils considèrent l'utilisation d'un autre type de ramasse-miettes pour éviter le problème du GIL (gaffe aux effets de bords par rapport aux codeurs qui auront joué avec le fait que pour le moment CPython utilise un comptage de références) - et il me semble que le GIL a aussi un effet de protection de l'ensemble des donnés, pas uniquement des compteurs de références.
[**] Je ne parle pas d'un programme Python qui se limiterais à un appel à une fonction de Numpy... mais d'un truc complet avec tout plein de scripts derrière.
Votez les 30 juin et 7 juillet, en connaissance de cause. http://www.pointal.net/VotesDeputesRN
[^] # Re: C'est ambitieux... mais si ça marche...
Posté par Albert_ . Évalué à -7.
[^] # *respire*
Posté par Signez (site web personnel) . Évalué à 9.
[^] # Re: C'est ambitieux... mais si ça marche...
Posté par Philippe F (site web personnel) . Évalué à 4.
Comme je l'explique plus haut, si j'ai bien compris ce qui se dit sur la mailing liste, ils font le postulat que les références des variables changent peu et mettent donc en cache la destination de la référence. J'imagine que pour les objets immutables, ils vont jusqu'à mettre la valeur en cache.
Donc ils court-circuitent autant que faire possible la résolution dynamique des objets, au prix d'un cache plus important. Mais dans la boucle chaude d'un programme, c'est pas là où tu t'amuses le plus à changer les types des objets, à résoudre dynamiquement les modules, etc. Au contraire, déjà en Python, il vaut mieux tout mettre en variable locale pour gagner en perf.
Mais, ils se heurteront au lock global et à sa prise/libération, s'ils réussissent à le contourner et que ça fonctionne encore bien, chapeau (et GVR sera sûrement très intéressé)
N'oublions pas que tout le monde travaille chez Google, donc ils peuvent discuter en direct. Pour citer un message de la mailing list :
FYI, at breakfast this morning, Guido seemed receptive to the idea of dropping the more exotic threading models and just keeping the pthreads and Windows support.
Prendre son petit-dej avec Guido, ça aide pour discuter du futur du langage.
[^] # Re: C'est ambitieux... mais si ça marche...
Posté par Antoine . Évalué à 2.
FYI, at breakfast this morning, Guido seemed receptive to the idea of dropping the more exotic threading models and just keeping the pthreads and Windows support.
Damned, tu as un lien ? Ça m'intéresse.
Suivre le flux des commentaires
Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.