Publication de Unladen Swallow 2009Q3

Posté par (page perso) . Modéré par baud123.
Tags :
18
26
oct.
2009
Python
C'est avec discrétion que le projet Unladen Swallow - dont le but est d'accélérer Python en s'appuyant sur LLVM et son compilateur JIT - a sorti la version 2009Q3, environ 3 mois après la version 2009Q2

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 .
On constate que le projet Unladen Swallow progresse à un rythme régulier améliorant mois après mois la performance de Python. L'objectif de multiplier la vitesse de python par cinq est encore loin mais toutes les promesses du projet ont été tenues jusqu'à présent.

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 . Évalué à 5.

    What do you mean ? African or European swallow ?

    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 (page perso) . Évalué à 2.

      Pour autant que je sache, c'était pas tellement des fuites mémoire qu'une absence totale d'optimisation sur ce point.
    • [^] # Re: Unladen Swallow ?

      Posté par . Évalué à 9.

      C'est quoi exactement un gain de 930 % en mémoire ? Le logiciel utilisait 100 Mo de mémoire et maintenant utilise 100 - (930% de 100 Mo) = -830 Mo ? Il ajoute une barrette de 830 Mo dans mon slot disponible ?
      • [^] # Re: Unladen Swallow ?

        Posté par . Évalué à 7.

        C’est un programme qui passe de 1030 Mo à 100 Mo (% = delta / valeur finale)
        • [^] # Re: Unladen Swallow ?

          Posté par . Évalué à 9.

          Quand on passe de 100 à 120, on dit que l’augmentation est de 20%, pas 17%.
          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 (page perso) . Évalué à 7.

      Alors, quelques explications sur le gain de consommation mémoire extraordinaire. Tout d'abord, notez que les auteurs soulignent que Unladen Swallow consomme encore beaucoup trop de mémoire par rapport à l'interpréteur de référence.

      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 (page perso) . Évalué à 2.

        En y reréfléchissant, j'ai dit une connerie. Unladen Swallow ne présente pas des variables Python en tant que constante à LLVM (ce serait quand même débile), il présente en revanche les variables Python comme pointant toujours vers la même référence.

        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 . Évalué à 2.

          N'empêche qu'en faisant des suppressions un peu n'importe où dans une liste ton algo reste au minimum en O(n*n) :-)
          • [^] # Re: Unladen Swallow ?

            Posté par (page perso) . Évalué à 1.

            C'est un problème que j'ai régulièrement en Python, comment supprimer des éléments d'une liste sans passer par des indexes ?

            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 . Évalué à 3.

              Mais il me semble que l'implémentation des listes en Python est basé sur un tableau, donc c'est pas possible.

              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 . Évalué à 3.

                Je me corrige : les insertions ou suppressions dans un ensemble sont en O(log(n)) amorti. Le lookup est bien en O(1), mais si tu changes la taille de l'ensemble il y a forcément de temps en temps des redimensionnements et réallocations physiques.
        • [^] # Re: Unladen Swallow ?

          Posté par . Évalué à 1.

          Merci pour cette clarification, absolument limpide, qui m'a enfin fait comprendre ce qu'était au juste LLVM.
          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 (page perso) . Évalué à 1.

            Est-il possible de completement "traduire" en assembleur "optimisé a donf" un programme python ?

            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 . Évalué à 2.

            Il y a aussi Cython, qui fait de la compilation statique d'un Python avec annotations optionnelles.
            http://www.cython.org/
  • # Python et vm de sun

    Posté par (page perso) . Évalué à 3.

    comparativement à faire rouler python sur la vm de sun, ça donne quoi en terme de performance?

    www.solutions-norenda.com

  • # C'est ambitieux... mais si ça marche...

    Posté par (page perso) . Évalué à 6.

    D'après ce que j'ai lu des objectifs, ils ciblent la compatibilité source avec les extensions C de Python :
    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.
    • [^] # Re: C'est ambitieux... mais si ça marche...

      Posté par . Évalué à -7.

      Il y a un truc aussi qui est oublie ce n'est pas que la rapidite du soft qui est importante mais qu'il soit utilise et actuellement je suis confronte a un probleme un peu couillon mais si je veux que un des logiciels que je developpe soit utilise il faut qu'il tourne sur mac et ben la c'est gentil les trucs enC/C++/fortran mais c'est la croix et la baniere pour avoir la bonne version des bibliotheques et le pire c'est que 90% des utilisateurs seront incapable de l'installer a partir des sources. Ne connaissant rien a Mac et que je n'ai pas l'envie d'apprendre a comment faire un binaire pour (ni moi ni les autres devs) on passe tout doucement a un mode on a tout qui fonctionne en python pure avec possibilite d'avoir des calculs rapides par l'utilisation d'un bibliotheque en C++. Mais on sait deja que la derniere solution ne sera utilise a peu pres que sous linux... Donc des ameliorations de rapidite moi ca me tente bien :)
      • [^] # *respire*

        Posté par (page perso) . Évalué à 9.

        J'ai eu du mal à respirer durant la lecture de ce post, suis-je le seul ?
    • [^] # Re: C'est ambitieux... mais si ça marche...

      Posté par (page perso) . Évalué à 4.

      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.

      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 . Évalué à 2.

        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.


        Damned, tu as un lien ? Ça m'intéresse.

Suivre le flux des commentaires

Note : les commentaires appartiennent à ceux qui les ont postés. Nous n'en sommes pas responsables.