PyPy - Comment peut-il battre CPython?

264

Depuis le blog Google Open Source :

PyPy est une réimplémentation de Python en Python, utilisant des techniques avancées pour essayer d'obtenir de meilleures performances que CPython. De nombreuses années de dur labeur ont finalement porté leurs fruits. Nos résultats de vitesse battent souvent CPython, allant d'un peu plus lent, à des accélérations jusqu'à 2x sur le code d'application réel, à des accélérations jusqu'à 10x sur de petits benchmarks.

Comment est-ce possible? Quelle implémentation Python a été utilisée pour implémenter PyPy? CPython ? Et quelles sont les chances qu'un PyPyPy ou PyPyPyPy bat leur score?

(Sur une note connexe ... pourquoi quelqu'un essaierait-il quelque chose comme ça?)

Agnel Kurian
la source
43
Nitpick: PyPy est PyPyPy. Considérez le préfixe Py- * comme un opérateur de projection.
u0b34a0f6ae
D'accord. donc PyPy devrait être préféré à CPython? a-t-il des inconvénients?
balki
10
PyPy est excellent pour l'optimisation de l'exécution, mais ses différents entrailles le rendent incompatible avec plusieurs extensions C populaires.
Cees Timmerman
4
Presque tout le monde manque la question de savoir comment un gain de vitesse est théoriquement possible. Mais pensez-y: Python peut tout faire, tout comme une machine de Turing. Il peut appeler gcc, après tout. Vous pouvez donc également écrire du code python qui s'exécute sur CPython, qui interprète un autre code python, le traduit en C, et exécute gcc, puis exécute le programme compilé. Et cela pourrait être plus rapide si le code est appelé assez souvent.
osa

Réponses:

155

Q1. Comment est-ce possible?

La gestion manuelle de la mémoire (ce que fait CPython avec son comptage) peut être plus lente que la gestion automatique dans certains cas.

Des limitations dans l'implémentation de l'interpréteur CPython empêchent certaines optimisations que PyPy peut faire (par exemple, des verrous à grain fin).

Comme Marcelo l'a mentionné, le JIT. Le fait de pouvoir confirmer à la volée le type d'un objet peut vous éviter d'avoir à faire plusieurs déréférences de pointeur pour finalement arriver à la méthode que vous souhaitez appeler.

Q2. Quelle implémentation Python a été utilisée pour implémenter PyPy?

L'interpréteur PyPy est implémenté dans RPython qui est un sous-ensemble statiquement typé de Python (le langage et non l'interpréteur CPython). - Consultez https://pypy.readthedocs.org/en/latest/architecture.html pour plus de détails.

Q3. Et quelles sont les chances qu'un PyPyPy ou PyPyPyPy bat leur score?

Cela dépendrait de la mise en place de ces hypothétiques interprètes. Si l'un d'entre eux, par exemple, a pris la source, a fait une sorte d'analyse sur celui-ci et l'a converti directement en code d'assemblage spécifique cible serré après un certain temps d'exécution, j'imagine que ce serait bien plus rapide que CPython.

Mise à jour: Récemment, sur un exemple soigneusement conçu , PyPy a surpassé un programme C similaire compilé avec gcc -O3. C'est un cas artificiel, mais présente quelques idées.

Q4. Pourquoi voudrait-on essayer quelque chose comme ça?

Depuis le site officiel. https://pypy.readthedocs.org/en/latest/architecture.html#mission-statement

Nous visons à fournir:

  • un cadre de traduction et de support commun pour la production d'
    implémentations de langages dynamiques, mettant l'accent sur une
    séparation nette entre les spécifications de langue et les
    aspects d' implémentation . Nous appelons cela le RPython toolchain_.

  • une implémentation conforme, flexible et rapide du langage Python_ qui utilise la chaîne d'outils ci-dessus pour activer de nouvelles fonctionnalités avancées de haut niveau sans avoir à coder les détails de bas niveau.

En séparant les problèmes de cette manière, notre implémentation de Python - et d'autres langages dynamiques - est capable de générer automatiquement un compilateur Just-in-Time pour n'importe quel langage dynamique. Il permet également une approche mix-and-match des décisions d'implémentation, y compris de nombreuses qui étaient historiquement hors du contrôle d'un utilisateur, telles que la plate-forme cible, les modèles de mémoire et de thread, les stratégies de récupération de place et les optimisations appliquées, y compris si oui ou non un JIT en premier lieu.

Le compilateur C gcc est implémenté en C, le compilateur Haskell GHC est écrit en Haskell. Avez-vous une raison pour que l'interpréteur / compilateur Python ne soit pas écrit en Python?

Noufal Ibrahim
la source
82
Cette réponse manque complètement l'explication principale de la rapidité de PyPy; Bien qu'il mentionne que PyPy n'est pas vraiment implémenté en Python, mais en RPython, il ne souligne pas que le code RPython est compilé statiquement et optimisé pour produire l'interpréteur PyPy (il se trouve qu'il s'agit également d'un code Python valide qui peut s'exécuter sur le dessus). de CPython beaucoup plus lentement). Ce qu'ils ont implémenté en "Python normal", c'est le "compilateur" RPython (le framework de traduction mentionné dans la citation de bloc).
Ben
12
C'est enterrer la lede. La plupart des performances proviennent de la traduction en C (ce qui rend l'interpréteur pas beaucoup plus lent que CPython) et JIT, ce qui rend les raccourcis beaucoup plus rapides.
Tobu
4
"Mise à jour: récemment, sur un exemple soigneusement conçu, PyPy a surpassé un programme C similaire compilé avec gcc -O3." Et si vous lisez le premier commentaire sous ce post, vous verrez que l'auteur de ce post ne connaît pas l'optimisation du temps de liaison. Avec l'optimisation du temps de liaison activée, le code C s'exécute plus rapidement.
Ali
2
Eh bien, le blog était en 2011 et cette réponse en 2014. En outre, le commentaire mentionne les bibliothèques partagées. Je ne sais pas dans quelle mesure (réponse et article de blog) est valable. Toutes les technologies impliquées ont beaucoup changé au cours des dernières années.
Noufal Ibrahim
1
Sur les deux exemples soigneusement conçus de Pypy étant plus rapide que l'équivalent C, chacun est plus rapide en référence pour un ensemble de raisons très spécifiques. Le premier parce que Pypy est suffisamment intelligent pour réaliser que le comptage en boucle serrée n'a jamais ce nombre utilisé, il peut donc être entièrement supprimé (passe JIT) le second pour une combinaison de: parce que le Pypy JIT peut "s'aligner à travers les limites de la bibliothèque", étant donné l'exemple de la fonction "printf" étant spécialisée pour ne pouvoir littéralement émettre qu'un entier, et élimine les malloc répétés (surcharge d'allocation de mémoire).
amcgregor
291

"PyPy est une réimplémentation de Python en Python" est une façon assez trompeuse de décrire PyPy, à mon humble avis, bien que ce soit techniquement vrai.

Il y a deux parties principales de PyPy.

  1. Le cadre de traduction
  2. L'interprète

Le cadre de traduction est un compilateur. Il compile le code RPython en C (ou d'autres cibles), ajoutant automatiquement des aspects tels que le garbage collection et un compilateur JIT. Il ne peut pas gérer du code Python arbitraire, seulement RPython.

RPython est un sous-ensemble de Python normal; tout le code RPython est du code Python, mais pas l'inverse. Il n'y a pas de définition formelle de RPython, car RPython est fondamentalement juste "le sous-ensemble de Python qui peut être traduit par le cadre de traduction de PyPy". Mais pour être traduit, le code RPython doit être typé statiquement (les types sont inférés, vous ne les déclarez pas, mais c'est toujours strictement un type par variable), et vous ne pouvez pas faire des choses comme déclarer / modifier des fonctions / cours à l'exécution non plus.

L'interpréteur est alors un interpréteur Python normal écrit en RPython.

Le code RPython étant du code Python normal, vous pouvez l'exécuter sur n'importe quel interpréteur Python. Mais aucune des prétentions de vitesse de PyPy ne vient de le faire de cette façon; c'est juste pour un cycle de test rapide, car la traduction de l'interpréteur prend beaucoup de temps.

Cela dit, il devrait être immédiatement évident que les spéculations sur PyPyPy ou PyPyPyPy n'ont en fait aucun sens. Vous avez un interprète écrit en RPython. Vous le traduisez en code C qui exécute Python rapidement. Là, le processus s'arrête; il n'y a plus de RPython à accélérer en le traitant à nouveau.

"Comment est-il possible que PyPy soit plus rapide que CPython" devient également assez évident. PyPy a une meilleure implémentation, y compris un compilateur JIT (ce n'est généralement pas aussi rapide sans le compilateur JIT, je crois, ce qui signifie que PyPy n'est que plus rapide pour les programmes sensibles à la compilation JIT). CPython n'a jamais été conçu pour être une implémentation hautement optimisante du langage Python (bien qu'ils essaient d'en faire une implémentation hautement optimisée , si vous suivez la différence).


La partie vraiment innovante du projet PyPy est qu'ils n'écrivent pas à la main des schémas GC sophistiqués ou des compilateurs JIT. Ils écrivent l'interpréteur relativement directement dans RPython, et pour tous RPython est de niveau inférieur à Python, c'est toujours un langage de récupération de place orienté objet, beaucoup plus haut niveau que C. Ensuite, le cadre de traduction ajoute automatiquement des choses comme GC et JIT. Le cadre de traduction est donc un énormemais cela s'applique également à l'interpréteur python PyPy, mais ils modifient leur implémentation, ce qui permet une plus grande liberté d'expérimentation pour améliorer les performances (sans se soucier de l'introduction de bogues GC ou de la mise à jour du compilateur JIT pour faire face aux changements). Cela signifie également que lorsqu'ils parviendront à implémenter un interpréteur Python3, cela bénéficiera automatiquement des mêmes avantages. Et tout autre interprète écrit avec le framework PyPy (dont il existe un certain nombre à différents stades de polissage). Et tous les interprètes utilisant le framework PyPy prennent automatiquement en charge toutes les plateformes prises en charge par le framework.

Ainsi, le véritable avantage du projet PyPy est de séparer (autant que possible) toutes les parties de la mise en œuvre d'un interprète indépendant de la plate-forme efficace pour un langage dynamique. Et puis en proposer une bonne mise en œuvre en un seul endroit, qui peut être réutilisée par de nombreux interprètes. Ce n'est pas une victoire immédiate comme «mon programme Python s'exécute plus rapidement maintenant», mais c'est une excellente perspective pour l'avenir.

Et il peut exécuter votre programme Python plus rapidement (peut-être).

Ben
la source
4
Je ne pouvais pas suivre la différence :(
polvoazul
37
@polvoazul La différence entre une implémentation de langage optimisée et une optimisation ? Eh bien, quand je dis que le CPython est une implémentation bien optimisée, je veux dire que les développeurs essaient de faire fonctionner efficacement les algorithmes internes de l'interpréteur lui-même et les structures de données intégrées. Une implémentation optimisante , OTOH, analyserait le code des utilisateurs finaux et essayerait de trouver des moyens de le transformer pour l'exécuter plus efficacement.
Ben
23

PyPy est implémenté en Python, mais il implémente un compilateur JIT pour générer du code natif à la volée.

La raison pour implémenter PyPy sur Python est probablement qu'il s'agit simplement d'un langage très productif, d'autant plus que le compilateur JIT rend les performances du langage hôte quelque peu hors de propos.

Marcelo Cantos
la source
Le JIT génère-t-il du code Python fonctionnant au même niveau que PyPy, ou génère-t-il un vrai code natif fonctionnant au niveau de l'implémentation Python sur laquelle PyPy s'exécute?
Edmund
3
Véritable code natif (voir ici ); Le code x86 32 bits pour être précis.
Marcelo Cantos du
11

PyPy est écrit en Python restreint. Il ne fonctionne pas au-dessus de l'interpréteur CPython, pour autant que je sache. Python restreint est un sous-ensemble du langage Python. AFAIK, l'interpréteur PyPy est compilé en code machine, donc lorsqu'il est installé, il n'utilise pas d'interpréteur python au moment de l'exécution.

Votre question semble s'attendre à ce que l'interpréteur PyPy s'exécute sur CPython lors de l'exécution du code. Edit: Oui, pour utiliser PyPy, vous devez d'abord traduire le code python PyPy, soit en C et compilé avec gcc, en code d'octet jvm, soit en code CLI .Net. Voir Mise en route

bobpaul
la source
8
PyPy fonctionnera au-dessus de CPython mais dans ce mode, il ne fournit pas les gains de vitesse que l'on pourrait souhaiter. :-) codespeak.net/pypy/dist/pypy/doc/…
Frank V