Python plus rapide avec un minimum d'effort – Python réel

By | novembre 11, 2020

python pour débutant

Python est l'un des langages de programmation les plus populaires parmi les développeurs, mais il présente certaines limites. Par exemple, selon l'application, il peut être jusqu'à 100 fois plus lent que certaines langues de niveau inférieur. C'est pourquoi de nombreuses entreprises réécrivent leurs applications dans un autre langage une fois que la vitesse de Python devient un goulot d'étranglement pour les utilisateurs. Mais que se passerait-il s'il y avait un moyen de conserver les fonctionnalités impressionnantes de Python et d'améliorer sa vitesse? Entrez PyPy.

PyPy est un interpréteur Python très conforme qui est une alternative intéressante à CPython 2.7, 3.6 et bientôt 3.7. En installant et en exécutant votre application avec elle, vous pouvez obtenir des améliorations de vitesse notables. L'ampleur de l'amélioration que vous verrez dépend de l'application que vous exécutez.

Dans ce didacticiel, vous apprendrez:

  • Comment installer et exécuter votre code avec PyPy
  • Comment PyPy se compare à CPython en termes de vitesse
  • Qu'est-ce que PyPy fonctionnalités sont et comment ils font exécuter votre code Python plus rapide
  • Qu'est-ce que PyPy limites sont

Les exemples de ce didacticiel utilisent Python 3.6 puisqu'il s'agit de la dernière version de Python avec laquelle PyPy est compatible.

Python et PyPy

La spécification du langage Python est utilisée dans un certain nombre d'implémentations telles que CPython (écrit en C), Jython (écrit en Java), IronPython (écrit pour .NET) et PyPy (écrit en Python).

CPython est l'implémentation originale de Python et est de loin la plus populaire et la plus maintenue. Lorsque les gens se réfèrent à Python, ils parlent le plus souvent de CPython. Vous utilisez probablement CPython en ce moment!

Cependant, comme il s'agit d'un langage interprété de haut niveau, CPython a certaines limites et ne remportera aucune médaille pour la vitesse. C’est là que PyPy peut être utile. Puisqu'il adhère aux spécifications du langage Python, PyPy ne nécessite aucune modification de votre base de code et peut offrir des améliorations de vitesse significatives grâce aux fonctionnalités que vous verrez ci-dessous.

Maintenant, vous vous demandez peut-être pourquoi CPython n'implémente pas les fonctionnalités impressionnantes de PyPy si elles utilisent la même syntaxe. La raison en est que la mise en œuvre de ces fonctionnalités nécessiterait d'énormes modifications du code source et constituerait une entreprise majeure.

Sans trop plonger dans la théorie, voyons PyPy en action.

Installation

Votre système d'exploitation peut déjà fournir un package PyPy. Sur macOS, par exemple, vous pouvez l'installer à l'aide de Homebrew:

Sinon, vous pouvez télécharger un binaire prédéfini pour votre système d'exploitation et votre architecture. Une fois le téléchargement terminé, il vous suffit de décompresser l'archive tar ou le fichier ZIP. Ensuite, vous pouvez exécuter PyPy sans avoir besoin de l'installer n'importe où:

$ tar xf pypy3.6-v7.3.1-osx64.tar.bz2
$ ./pypy3.6-v7.3.1-osx64/bin/pypy3
Python 3.6.9 (?, 19 juil.2020, 21:37:06)
[PyPy 7.3.1 with GCC 4.2.1]
Tapez "aide", "copyright", "crédits" ou "licence" pour plus d'informations.

Avant d'exécuter le code ci-dessus, vous devez vous trouver dans le dossier où vous avez téléchargé le binaire. Reportez-vous à la documentation d'installation pour les instructions complètes.

PyPy en action

Vous avez maintenant installé PyPy et vous êtes prêt à le voir en action! Pour ce faire, créez un fichier Python appelé script.py et mettez-y le code suivant:

    1total = 0
    2pour je dans intervalle(1, 10 000):
    3    pour j dans intervalle(1, 10 000):
    4        total + = je + j
    5
    6impression(F"Le résultat est total")

C'est un script qui, en deux imbriqués pour boucles, ajoute les nombres de 1 à 9 999et imprime le résultat.

Pour voir combien de temps il faut pour exécuter ce script, modifiez-le pour ajouter les lignes en surbrillance:

    1importer temps
    2
    3Heure de début = temps.temps()
    4
    5total = 0
    6pour je dans intervalle(1, 10 000):
    sept    pour j dans intervalle(1, 10 000):
    8        total + = je + j
    9
diximpression(F"Le résultat est total")
11
12heure de fin = temps.temps()
13impression(F"Ça a pris heure de fin-Heure de début:.2f    secondes pour calculer ")

Le code effectue maintenant les actions suivantes:

  • Ligne 3 enregistre l'heure actuelle dans la variable Heure de début.
  • Lignes 5 à 8 exécutez les boucles.
  • Ligne 10 imprime le résultat.
  • Ligne 12 enregistre l'heure actuelle pour heure de fin.
  • Ligne 13 imprime la différence entre Heure de début et heure de fin pour montrer combien de temps il a fallu pour exécuter le script.

Essayez de l'exécuter avec Python. Voici ce que j'obtiens sur mon MacBook Pro 2015:

$ python3.6 script.py
Le résultat est 999800010000
Il a fallu 20,66 secondes pour calculer

Maintenant, lancez-le avec PyPy:

$ pypy3 script.py
Le résultat est 999800010000
Il a fallu 0,22 seconde pour calculer

Dans ce petit benchmark synthétique, PyPy est environ 94 fois plus rapide que Python!

Pour des benchmarks plus sérieux, vous pouvez jeter un œil au PyPy Speed ​​Center, où les développeurs exécutent des benchmarks nocturnes avec différents exécutables.

Gardez à l'esprit que la façon dont PyPy affecte les performances de votre code dépend de ce que fait votre code. Il existe certaines situations dans lesquelles PyPy est en fait plus lent, comme vous le verrez plus tard. Cependant, en moyenne géométrique, il est 4,3 fois plus rapide que Python.

PyPy et ses caractéristiques

Historiquement, PyPy a fait référence à deux choses:

  1. UNE cadre de langage dynamique pour générer des interprètes pour les langues dynamiques
  2. UNE Implémentation Python en utilisant ce cadre

Vous avez déjà vu la deuxième signification en action en installant PyPy et en exécutant un petit script avec. L'implémentation Python que vous avez utilisée a été écrite à l'aide d'un framework de langage dynamique appelé RPython, tout comme CPython a été écrit en C et Jython a été écrit en Java.

Mais n’avez-vous pas dit plus tôt que PyPy était écrit en Python? Eh bien, c'est un peu une simplification. La raison pour laquelle PyPy est devenu connu comme un interpréteur Python écrit en Python (et non en RPython) est que RPython utilise la même syntaxe que Python.

Pour tout clarifier, voici comment PyPy est produit:

  1. Le code source est écrit en RPython.

  2. La chaîne d'outils de traduction RPython est appliquée au code, ce qui le rend essentiellement plus efficace. Il compile également le code en code machine, c'est pourquoi les utilisateurs de Mac, Windows et Linux doivent télécharger différentes versions.

  3. Un exécutable binaire est produit. Il s'agit de l'interpréteur Python que vous avez utilisé pour exécuter votre petit script.

N'oubliez pas que vous n'avez pas besoin de suivre toutes ces étapes pour utiliser PyPy. L'exécutable est déjà disponible pour que vous puissiez l'installer et l'utiliser.

De plus, comme il est très déroutant d’utiliser le même mot à la fois pour le framework et pour l’implémentation, l’équipe derrière PyPy a décidé de s’éloigner de cette double utilisation. Désormais, PyPy se réfère uniquement à l'implémentation Python. Le cadre est appelé le Chaîne d'outils de traduction RPython.

Ensuite, vous découvrirez les fonctionnalités qui rendent PyPy meilleur et plus rapide que Python dans certains cas.

Compilateur juste à temps (JIT)

Avant de vous lancer dans la compilation JIT, prenons du recul et passons en revue les propriétés des langages compilés tels que C et des langages interprétés tels que JavaScript.

Compilé les langages de programmation sont plus performants mais sont plus difficiles à porter sur différentes architectures CPU et systèmes d'exploitation. Interprété les langages de programmation sont plus portables, mais leurs performances sont bien pires que celles des langages compilés. Ce sont les deux extrêmes du spectre.

Ensuite, il y a des langages de programmation tels que Python qui font un mélange de compilation et d'interprétation. Plus précisément, Python est d'abord compilé dans un bytecode intermédiaire, qui est ensuite interprété par CPython. Cela rend le code plus performant que le code écrit dans un langage de programmation purement interprété et maintient l'avantage de la portabilité.

Cependant, les performances sont encore loin de celles de la version compilée. La raison en est que le code compilé peut faire de nombreuses optimisations qui ne sont tout simplement pas possibles avec le bytecode.

C’est là que le compilateur juste à temps (JIT) entre en jeu. Il essaie d'obtenir les meilleures parties des deux mondes en faisant une véritable compilation en code machine et une certaine interprétation. En un mot, voici les étapes de la compilation JIT pour fournir des performances plus rapides:

  1. Identifiez les composants du code les plus fréquemment utilisés, comme une fonction dans une boucle.
  2. Convertissez ces pièces en code machine pendant l'exécution.
  3. Optimiser le code machine généré.
  4. Remplacez l'implémentation précédente par la version de code machine optimisée.

Vous vous souvenez des deux boucles imbriquées au début du didacticiel? PyPy a détecté que la même opération était exécutée encore et encore, l'a compilée en code machine, optimisé le code machine, puis a échangé les implémentations. C’est pourquoi vous avez constaté une si grande amélioration de la vitesse.

Collecte des ordures

Chaque fois que vous créez des variables, des fonctions ou tout autre objet, votre ordinateur leur alloue de la mémoire. Finalement, certains de ces objets ne seront plus nécessaires. Si vous ne les nettoyez pas, votre ordinateur risque de manquer de mémoire et de planter votre programme.

Dans les langages de programmation tels que C et C ++, vous devez généralement gérer ce problème manuellement. D'autres langages de programmation tels que Python et Java le font automatiquement pour vous. C'est appelé ramassage automatique des ordures, et il existe plusieurs techniques pour y parvenir.

CPython utilise une technique appelée comptage de références. Essentiellement, le nombre de références d'un objet Python est incrémenté chaque fois que l'objet est référencé, et il est décrémenté lorsque l'objet est déréférencé. Lorsque le nombre de références est égal à zéro, CPython appelle automatiquement la fonction de désallocation de mémoire pour cet objet. C’est une technique simple et efficace, mais il y a un hic.

Lorsque le nombre de références d'un grand arbre d'objets devient nul, tout les objets associés sont libérés. En conséquence, vous avez une pause potentiellement longue pendant laquelle votre programme ne progresse pas du tout.

Il existe également un cas d’utilisation dans lequel le comptage de références ne fonctionne tout simplement pas. Considérez le code suivant:

    1classe UNE(objet):
    2    passer
    3
    4une = UNE()
    5une.une_propriété = une
    6del une

Dans le code ci-dessus, vous définissez une nouvelle classe. Ensuite, vous créez une instance de la classe et l'affectez comme une propriété sur elle-même. Enfin, vous supprimez l'instance.

À ce stade, l'instance n'est plus accessible. Cependant, le comptage de références ne supprime pas l'instance de la mémoire car elle a une référence à elle-même, de sorte que le nombre de références n'est pas zéro. Ce problème s'appelle un cycle de référence, et il ne peut pas être résolu en utilisant le comptage de références.

C'est là que CPython utilise un autre outil appelé le ramasse-miettes cyclique. Il parcourt tous les objets en mémoire à partir de racines connues comme le type objet. Il identifie ensuite tous les objets accessibles et libère les objets inaccessibles car ils ne sont plus vivants. Cela résout le problème du cycle de référence. Cependant, cela peut créer des pauses encore plus visibles lorsqu'il y a un grand nombre d'objets en mémoire.

PyPy, en revanche, n'utilise pas de comptage de références. Au lieu de cela, il n'utilise que la deuxième technique, le chercheur de cycle. Autrement dit, il marche périodiquement sur des objets vivants à partir des racines. Cela donne à PyPy un certain avantage par rapport à CPython car il ne se soucie pas du comptage de références, ce qui rend le temps total passé à la gestion de la mémoire inférieur à celui de CPython.

En outre, au lieu de tout faire dans une seule entreprise majeure comme CPython, PyPy divise le travail en un nombre variable de pièces et exécute chaque pièce jusqu'à ce qu'il n'en reste plus. Cette approche n'ajoute que quelques millisecondes après chaque collection mineure plutôt que d'ajouter des centaines de millisecondes en une seule fois comme CPython.

Le garbage collection est complexe et contient de nombreux détails supplémentaires qui dépassent la portée de ce didacticiel. Vous pouvez trouver plus d'informations sur le garbage collection de PyPy dans la documentation.

Limitations de PyPy

PyPy n'est pas une solution miracle et n'est peut-être pas toujours l'outil le plus adapté à votre tâche. Cela peut même rendre votre application beaucoup plus lente que CPython. C’est pourquoi il est important de garder à l’esprit les limitations suivantes.

Cela ne fonctionne pas bien avec les extensions C

PyPy fonctionne mieux avec les applications Python pures. Chaque fois que vous utilisez un module d'extension C, il s'exécute beaucoup plus lentement qu'en CPython. La raison en est que PyPy ne peut pas optimiser les modules d'extension C car ils ne sont pas entièrement pris en charge. De plus, PyPy doit émuler le comptage de références pour cette partie du code, ce qui le rend encore plus lent.

Dans de tels cas, l'équipe PyPy recommande de supprimer l'extension CPython et de la remplacer par une version pure Python afin que JIT puisse la voir et faire ses optimisations. Si ce n’est pas une option, vous devrez utiliser CPython.

Cela étant dit, l'équipe principale travaille sur les extensions C. Certains packages ont déjà été portés sur PyPy et fonctionnent tout aussi vite.

Cela ne fonctionne bien qu'avec les programmes de longue durée

Imaginez que vous vouliez vous rendre dans un magasin très proche de chez vous. Vous pouvez aller à pied ou en voiture.

Votre voiture est clairement beaucoup plus rapide que vos pieds. Cependant, pensez à ce qu'il vous faudrait faire:

  1. Allez dans votre garage.
  2. Démarrez votre voiture.
  3. Réchauffez un peu la voiture.
  4. Conduisez jusqu'à la boutique.
  5. Trouvez une place de parking.
  6. Répétez le processus sur le chemin du retour.

Il y a beaucoup de frais généraux impliqués dans la conduite d'une voiture, et cela ne vaut pas toujours la peine si l'endroit où vous voulez aller est à proximité!

Pensez maintenant à ce qui se passerait si vous vouliez vous rendre dans une ville voisine à cinquante kilomètres de là. Cela vaudrait certainement la peine d'y conduire au lieu d'aller à pied.

Bien que la différence de vitesse ne soit pas aussi perceptible que dans l'analogie ci-dessus, il en va de même avec PyPy et CPython.

Lorsque vous exécutez un script avec PyPy, il fait beaucoup de choses pour rendre votre code plus rapide. Si le script est trop petit, la surcharge entraînera l'exécution de votre script plus lente que dans CPython. D'un autre côté, si vous avez un script de longue durée, ces frais généraux peuvent rapporter des dividendes de performance importants.

Pour voir par vous-même, exécutez le petit script suivant à la fois dans CPython et PyPy:

    1importer temps
    2
    3Heure de début = temps.temps()
    4
    5pour je dans intervalle(100):
    6    impression(je)
    sept
    8heure de fin = temps.temps()
    9impression(F"Ça a pris heure de fin-Heure de début:.10f    secondes pour calculer ")

Il y a un petit délai au début lorsque vous l'exécutez avec PyPy, tandis que CPython l'exécute instantanément. En chiffres exacts, il faut 0,0004873276 secondes pour l'exécuter sur un MacBook Pro 2015 avec CPython et 0,0019447803 secondes pour l'exécuter avec PyPy.

Il ne fait pas de compilation anticipée

Comme vous l'avez vu au début de ce didacticiel, PyPy n'est pas une implémentation Python entièrement compilée. Il compile Du code Python, mais ce n’est pas un compilateur pour le code Python. En raison du dynamisme inhérent à Python, il est impossible de compiler Python dans un binaire autonome et de le réutiliser.

PyPy est un interpréteur d'exécution c'est plus rapide qu'un langage entièrement interprété, mais c'est plus lent qu'un langage entièrement compilé tel que C.

Conclusion

PyPy est une alternative rapide et performante à CPython. En exécutant votre script avec lui, vous pouvez obtenir une amélioration majeure de la vitesse sans apporter une seule modification à votre code. Mais ce n’est pas une solution miracle. Il présente certaines limites et vous devrez tester votre programme pour voir si PyPy peut vous aider.

Dans ce didacticiel, vous avez appris:

  • Quoi PyPy est
  • Comment installer PyPy et exécutez votre script avec ça
  • Comment PyPy se compare à CPython en termes de vitesse
  • Quoi fonctionnalités PyPy a et comment améliore la vitesse de vos programmes
  • Quoi limites PyPy a qui peut le rendre inapproprié dans certains cas

Si votre script Python a besoin d'un peu de vitesse, essayez PyPy. Selon votre programme, vous pouvez obtenir des améliorations de vitesse notables!

Si vous avez des questions, n'hésitez pas à nous contacter dans la section commentaires ci-dessous.