PROJET AUTOBLOG


Sam & Max: Python, Django, Git et du cul

Site original : Sam & Max: Python, Django, Git et du cul

⇐ retour index

Les interpréteurs alternatifs de python 20

jeudi 3 mars 2016 à 09:17

Ceci est un post invité de deronnax posté sous licence creative common 3.0 unported.

Bonjour, tous.
Vu qu’un des derniers posts de Sam&Max parlait de vitesse et des implémentations alternatives de Python, je propose un article sur l’état de ces différentes implémentations justement, et leur rapport à la vitesse.

Je commence par un petit rappel, pour nos lecteurs les moins aguerris avec l’écosystème Python : Python est un langage et aussi un interpréteur de référence, dont le vrai nom est CPython, écrit en C, qui livre également la bibliothèque standard Python (les modules datetime, urllib, collections, etc). CPython a des performances typiques de langage de script comme PHP ou Ruby, c’est à dire pas terribles, et a une conception assez simple, qui utilise des solutions simples pour répondre aux problèmes de langages de script. Celles qui font le plus parler d’elles (en mal évidement) étant la gestion de la mémoire par comptage de références (references counting), considérée moins performant qu’un vrai ramasse-miette (garbage collector) et assez limitée, et un verrou global de l’interpréteur, dont vous avez déjà dû entendre parler, le très mal-aimé GIL (Global Interpreter Lock), qui empêche entre autre à CPython d’avoir de vrai threads, c’est à dire des portions de code s’exécutant réellement en même temps, concurremment, sur des cœurs processeur différents. Néanmoins, rappelons-le encore : ces solutions permettent d’avoir un interpréteur simple et clair, pour lequel écrire des modules d’extensions en C est simple, ce qui est considéré actuellement comme le plus important. De plus, CPython lui-même n’ayant pas des performances significatives, du vrai multithread n’est pas un besoin pertinent. Si vous avez besoin de faire des traitements lourds, 9 fois sur 10 une bibliothèque spécialisée en C s’en occupe et fait elle même le traitement lourd en C dans ses threads, tel qu’OpenCV ou NumPy. En général vous avez très rarement besoin de vrai thread Python, et donc le GIL est relativement un faux problème.
La communauté Python s’est adaptée à cet absence de vrai concurrence, et différentes solutions sont apparues pour contourner le problème, je pense par exemple à l’excellent module multiprocessing, qui vous permet d’instancier différents interpréteurs et de les faire communiquer entre eux de manière transparente comme si c’était des threads.
D’une manière générale, quand il sera question d’un interpréteur alternatif, quatre points intéresseront la communauté :

Je ne parlerai ici que des interpréteurs se voulant concurrents de CPython et remplissant la même niche. Les interpréteurs remplissant une tache un peu différente, et/ou nécessitant des modifications significatives de la manière d’écrire du code Python, ou n’ayant pas un niveau d’aboutissement significatif ne seront pas abordés, comme Cython ou Stackless Python. Voici une liste complète des implémentations de python.

Déjà, niveau implémentations alternatives, parlons des historiques Jython et IronPython ; ces deux projets sont des interpréteurs python tournant dans la VM existante d’un autre langage, respectivement la JVM Java d’Oracle (Sun, avant son rachat) et le CLR/.Net C# de Microsoft, apportant ainsi différents bénéfices, entre autre un vrai ramasse-miette et aussi des vrai threads qui carburent. Ils ont aussi en commun d’avoir été peu adoptés (très peu dans le cas d’IronPython) et qu’ils ne sont plus très vivants (très peu dans le cas d’IronPython, déjà lâché par Microsoft en 2010) : dernière release de Jython: mai 2015 ; IronPython: juin 2014. Ça fait mal. De plus, si à leur sortie ils donnaient par moment de meilleures performances que CPython, ce dernier s’est beaucoup amélioré et maintenant ils se valent au mieux. De plus, tous les deux sont très lourds, avec une empreinte mémoire beaucoup plus grosse et un temps de démarrage sans comparaison avec CPython. À déployer, c’est complètement la mort aussi, rien à voir avec apt-get install python/pypy. Très beaucoup la mort pour obtenir un IronPython qui tourne sous Mono sous Ubuntu. Ces projets sont bien partis pour disparaître s’ils ne changent pas. Et le support de python3 est inexistant pour tous les deux. Et vos modules codés en C, vous pouvez vous asseoir dessus.

Pour la petite histoire (anecdote à recaser en soirée pour briller), Jim Hugunin, le créateur de Jython, a commencé à écrire IronPython pour démontrer aux gens que la plateforme .Net était mauvaise et biens moins bonne que la JVM pour écrire une implémentation de langage. Jim a commencé à écrire une implémentation de Python en .Net, et à sa grande surprise a réussi à faire une implémentation très aboutie en peu de temps et a trouvé la plateforme .Net tellement géniale qu’il a quitté le projet Jython pour lancer IronPython.

Ensuite vient Pypy, l’interpréteur Python en python orienté vitesse. Pypy existe depuis une dizaine d’année et vise à être un drop-in remplacement de CPython, c’est à dire qu’ils suffit de l’installer et de changer les commandes “python truc” par “pypy truc” et hop ça marche. Ça fonctionne, à condition de ne pas utiliser de modules compilés en C. Si c’est néanmoins le cas, Pypy peut utiliser les modules en C de CPython moyennant recompilation, mais attention, les perfs sont moins bonne qu’avec CPython, dû au fait que l’API vient de CPython et impose à Pypy, un interfaçage/fonctionnement qui n’est pas le sien et ne supporte pas toute l’API. Pypy délivre actuellement des performances en moyenne neuf fois supérieures à CPython sur leur suite de tests, ce qui est assez bluffant. Après, à chacun de considérer si la suite de test de Pypy est représentative d’une utilisation réelle, ce qui est le problème existentielle de chaque implémentation Python alternative qui vous met sous le nez un graphe montrant qu’elle est X fois plus rapide que CPython sur sa suite de tests. Pypy utilise également un GIL, donc ne fourni pas de vrais threads, mais a dans un coin un projet de passer à un modèle de concurrence qui permettrait de s’affranchir du GIL, et dispose par contre d’un vrai ramasse-miette. Le support de Python 3 est minimum, en juin 2014 est sorti Pypy 2.4 supportant python version 3.2, l’équipe informant qu’il est néanmoins plus lent que Pypy visant python 2.7, autant vous dire qu’on est pas en avance.

Vous avez pu tomber sur des noms genre Unladden Swallow et Stackless Python : Unladen Swallow était un Pypy-like antérieur par des ingénieurs de Google qui voulait générer du JIT avec LLVM en promettant des performances 5 fois supérieurs à CPython. Le projet a échoué à tenir ses promesses et a fini par mourir doucement lors que Google a retiré son financement. Ce qui a pu être sauvé a été intégré dans Pypy (des améliorations au module Pickle). Stackless  est un interpréteur modifié qui intègre les coroutines de base et d’autres trucs parallèles/asynchrone, il n’est pas un drop-in remplacement et demande d’écrire du python qui ne tournera que sur et pour lui.

Ensuite est venu il y a un an le “Pypy” de Dropbox, Pyston. Tout le monde a tapé sur Dropbox pour avoir réinventé sa roue carrée avec Pyston au lieu de contribuer à Pypy, Dropbox a répondu qu’ils utilisent une approche différente de celle de Pypy, ils utilisent un method-at-a-time JIT au lieu d’un tracing JIT, méthode qui a donné de si bons résultats avec le moteur javascript de Google, V8, en s’appuyant sur la célèbre LLVM, la “machine virtuelle pour le bas-niveau”. LLVM étant par ailleurs soutenue par des grands noms comme Apple et Intel, si Dropbox ne merde pas, ça pourrait donner un truc intéressant. De plus, contrairement à Pypy qui a choisi de peu supportter l’API C de CPython pour les modules, Pyston vise une compatibilité absolue avec l’API C, c’est une de ses priorité fondamentale . Nous pouvons en déduire que Dropbox a beaucoup de modules d’extension en C auxquels ils tiennent beaucoup. Pypy utilise un ramasse miette et un GIL. Un point fait néanmoins tousser avec Pyston, c’est que les créateurs déclarent ne vouloir que viser Python 2.7 et ne pas envisager de supporter python3. On a du mal à croire que Guido van Rossum travaille pour cette boite. Toutes ces informations viennent de la FAQ de Pyston. Le blog de Pyston, intéressant, explique leurs choix technique et rend compte de leur avancée : http://blog.pyston.org/.

Pyston a fait plusieurs chose intelligemment, notamment au lieu de réécrire un interpréteur, ils ont forké CPython et branché leurs systèmes de JIT dedans, ce qui leur a permis de sortir en moins d’un an une release de Pyston plutôt viable qui fait tourner beaucoup de choses et passe beaucoup de tests. Leur interpréteur est déjà 25% plus rapide que CPython sur leur suite de test, face à un Pypy 50% plus performant. C’est un petit tour de force.

Un énorme écueil d’écrire un interpréteur alternatif à CPython étaient les modules en C de la bibliothèque standard ; une partie significative des modules de la bibliothèque standard étaient écrits en C, donc si vous faisiez un interpréteur alternatif, il fallait aussi vous recoder dans le langage de votre interpréteur tous les modules de la bibliothèque standard que vous ne pouviez utiliser, ce qui implique de faire 10.000 tests pour s’assurer qu’ils ont exactement le même comportement que ceux de la bibliothèque standard, ce qui est extrêmement fastidieux et pénible. Depuis Python 3.3 en septembre 2012 , CPython fourni un recode en Python de tous ses module en C qui passent exactement la même suite de test et qui sont donc “garantis” (modulo erreur humaine, c’est une vaste et dure tâche) de se comporter exactement comme leurs congénères en C. C’est une énorme charge de travail en moins pour les développeurs d’interpréteurs alternatifs. Également, depuis Python 3.1 , l’import de modules, qui auparavant était une machinerie interne un peu obscure de l’interpréteur CPython, et donc qu’il fallait recoder en croisant les doigts pour que ça reproduise exactement le comportement de CPython sous peine de tout voir exploser, a été recodé en Python et est livré avec CPython, donc fini de redevoir recoder un système d’import, vous pouvez juste le reprendre et l’utiliser, ouf.

De tous, Pypy est le seul qui s’approche actuellement vaguement d’un “concurrent” sérieux à CPython, talonné par Pyston pour un futur proche. Et encore, ce n’est pas encore le drop-in remplacement parfait. Et tous ont un support de Python3 inexistant, sauf Pypy qui en a un insuffisant :). Et tous, en tant que VM complète (donc lourde) ou interpréteur-optimiseur visant à optimiser le code (et donc analyser), ont des temps de démarrage et d’atteinte de l’efficacité sans commune mesure avec CPython. Ils ne sont intéressants qu’en cas de long-running (genre, vos sites Django). Et tous utilisent un ramasse-miette, ce qui donne un comportement différents sur les objets “finalisés”. Alors que CPython a un comportement déterministe et détruit immédiatement les objets qui ne sont plus utilisés, les ramasse-miettes reportent leur destruction à un moment où il sera plus opportun de les détruire, potentiellement très longtemps ou jamais au pire des cas. C’est pour ça que par hygiène, il faut toujours libérer explicitement les ressources des objets représentants des ressources tel que les files-like objets représentants fichiers ou socket en utilisant leur méthode “close()” ou le manager “with” même si ça fonctionne très bien sans sous CPython. Pour les objets représentant des ressources limitées comme les fichiers, les sockets ou n’importe quel lien vers une ressource limitée procurée par l’OS, ne pas le faire signifie se prendre rapidement un stop de l’OS. D’une manière générale, CPython applique des solutions simples et déterministes qui offrent pas mal de garanties sympathiques (gestion des ressources, atomicité, alignement mémoire, adresse des objets en mémoire fixe, etc) qui sont maintenant considérées comme acquise et immuables par beaucoup de programmes python, malheureusement gênantes pour faire un interpréteur optimisant vraiment agressivement (là j’ai pas de sources mais c’est une complainte qui revient régulièrement dans les discussions des autres interpréteurs, surtout Pypy).

Parmi les news relatives à la vitesse de Python que l’article de S&M ne mentionne pas, la nouvelle release de la JVM vient avec un meilleur découplage des éléments la composant, et du coup il devient facile de l’utiliser pour un autre langage. IBM a fait un proof-of-concept en faisant un interpréteur python, qu’ils vont open-sourcer (et faire pareil avec Ruby). Pas d’altruisme là-dedans, c’est pour montrer au monde que leur VM est bien fichue. Ça pourrait être le nouveau Jython : http://www.infoworld.com/article/3014128/open-source-tools/ibms-open-source-jvm-project-could-also-speed-ruby-python.html.

Aussi, Microsoft s’étant visiblement, dernièrement, un peu amouraché de Python, on peut s’attendre que leur coopération à Python aille un peu plus loin. Ou pas.

Après, la triste vérité est que les langages dynamiques sont très durs à optimiser, parce qu’ils sont justement dynamiques, surtout Python où tout est modifiable, et donc interpréteurs/VMs ne peuvent être sûrs de rien et ne peuvent pas supprimer pleins de tests/résolutions/recherches, et que Python, malgré toutes ses implémentations pleines de bonne volonté, ne sera jamais aussi rapide que le C, ni même que Java. La dernière fois que j’ai regardé (~3 ans), le benchmark game donnait python 20x plus lent que le C, contre 2x pour le Java. Ça fait réfléchir. Tous les efforts poussés à fond, on tapera peut être le 5x plus lent que le C :)

Actuellement, un core-contributeur de CPython (français, cocorico !) travaille sur différents sujets visant à améliorer significativement les performances de CPython. C’est la première fois qu’un chantier aussi vaste est lancé visant à sérieusement améliorer les performances de CPython. Le site sur le sujet : http://faster-cpython.readthedocs.org/

Ces différents projets acceptent les dons. Je vous encourage évidemment vivement à donner. La vitesse de Python est l’un dernier écueil qui pourraient faire choisir à un décideur technique pressé un autre langage plutôt que Python. Des meilleurs interpréteurs Python, c’est plus de tâches accomplies en Python, donc plus de postes pour les développeurs Python, donc plus de demande, et donc de meilleurs salaires. Quand vous donnez à des projets Python, vous vous versez de l’argent à vous-même dans le futur. De plus, ça aide à faire reculer les parts de marché de cette horreur qu’est PHP et de la concurrence de Ruby et Go, et ça, c’est toujours ça de pris :)
Même une petite somme est significative. Avec l’euro fort, vous avez un grand pouvoir. Et un grand pouvoir implique de grandes responsabilités.
Donner à Cpython : https://www.python.org/psf/donations/
Donner à Pypy : http://pypy.org/

Voila, vous savez tout sur l’histoire des différents interpréteurs Python. Avec ça, vous pourrez vous la pétez en soirée et niquer des tonnes de meufs. Ou pas.