web-dev-qa-db-fra.com

Accélérer Python

Ce sont vraiment deux questions, mais elles sont si similaires, et pour rester simple, je pensais les combiner:

  • Premièrement : Étant donné un projet Python établi, quels sont les moyens décents de l’accélérer au-delà de l’optimisation dans le code?

  • Deuxièmement : Lors de l'écriture d'un programme en python à partir de rien, quels sont les bons moyens d'améliorer considérablement les performances?

Pour la première question, imaginez qu’on vous remette un projet rédigé avec décence et que vous deviez améliorer les performances, mais que vous ne semblez pas vraiment gagner beaucoup en refactoring/optimisation. Que feriez-vous pour l'accélérer dans ce cas à moins de le réécrire dans quelque chose comme C?

42
akdom

À propos de "Deuxièmement: lors de l'écriture d'un programme en python à partir de rien, quels sont les bons moyens d'améliorer considérablement les performances?"

Rappelez-vous les règles d'optimisation de Jackson:

  • Règle 1: Ne le fais pas.
  • Règle 2 (pour les experts uniquement): Ne le faites pas encore.

Et la règle Knuth:

  • "L'optimisation prématurée est la racine de tout Mal."

Les règles les plus utiles se trouvent dans Règles générales d'optimisation .

  1. Ne pas optimiser comme vous allez. D'abord bien faire les choses Alors, vite. Optimiser un mauvais programme est toujours faux.

  2. Rappelez-vous la règle des 80/20.

  3. Exécutez toujours les repères "avant" et "après". Sinon, vous ne saurez pas si vous avez trouvé le 80%.

  4. Utilisez les bons algorithmes et structures de données. Cette règle devrait être la première. Rien ne compte autant que l'algorithme et la structure de données.

Résultat final

Vous ne pouvez pas empêcher ou éviter l'effort "d'optimiser ce programme". Cela fait partie du travail. Vous devez planifier cela et le faire avec soin, tout comme la conception, le code et les activités de test.

41
S.Lott

Plutôt que de simplement cogner jusqu'à C, je suggérerais:

Faites que votre code compte. Faites plus avec moins d'exécutions de lignes:

  • Changer l'algorithme à un plus rapide. Il n’est pas nécessaire de vouloir être plus rapide dans de nombreux cas.
  • Utilisez des primitives python écrites en C. Certaines choses vont forcer la dépêche d'un interprète, contrairement à d'autres. Ce dernier est préférable
  • Méfiez-vous du code qui construit d'abord une structure de données volumineuse suivie de sa consommation. Pensez à la différence entre range et xrange. En général, il est souvent utile de penser à l’utilisation de la mémoire du programme. L'utilisation de générateurs peut parfois amener O(n) utilisation de la mémoire jusqu'à O (1).
  • Python est généralement non optimisant. Soulevez le code invariant en boucles, éliminez les sous-expressions courantes dans les boucles serrées.
  • Si quelque chose coûte cher, effectuez un pré-calcul ou une mémo. Les expressions régulières peuvent être compilées par exemple.
  • Besoin de réduire les chiffres? Vous voudrez peut-être vérifier numpy.
  • De nombreux programmes python sont lents car ils sont liés par des entrées/sorties sur disque ou des bases de données. Assurez-vous que vous avez quelque chose d'intéressant à faire pendant que vous attendez que les données arrivent plutôt que de simplement bloquer. Une arme pourrait être quelque chose comme le cadre Twisted.
  • Notez que de nombreuses bibliothèques de traitement de données cruciales ont des versions C, que ce soit XML, JSON ou autre. Ils sont souvent considérablement plus rapides que l’interprète Python.

Si tout ce qui précède échoue pour le code profilé et mesuré, commencez à réfléchir au chemin de réécriture C.

26

Les suspects habituels - profilez-le, trouvez la ligne la plus chère, déterminez ce qu'il fait, réparez-le. Si vous n'avez pas fait beaucoup de profilage auparavant, il pourrait y avoir de grosses boucles quadratiques ou une duplication de chaîne cachée derrière des expressions autrement inoffensives.

En Python, la concaténation de chaînes et les générateurs sont deux des causes les plus courantes de ralentissement non évident que j'ai trouvées. Puisque les chaînes de Python sont immuables, procédez comme ceci:

result = u""
for item in my_list:
    result += unicode (item)

va copier le entier chaîne deux fois par itération. Ceci a été bien couvert et la solution consiste à utiliser "".join:

result = "".join (unicode (item) for item in my_list)

Les générateurs sont un autre coupable. Ils sont très faciles à utiliser et peuvent considérablement simplifier certaines tâches, mais un générateur mal appliqué sera beaucoup plus lent que le simple ajout d'éléments à une liste et le renvoi de la liste.

Enfin, n’ayez pas peur de réécrire des bits en C! Python, en tant que langage dynamique de haut niveau, n'est tout simplement pas capable de faire correspondre la vitesse de C. S'il existe une fonction que vous ne pouvez plus optimiser en Python, envisagez de l'extraire dans un module d'extension.

Ma technique préférée pour cela consiste à maintenir à la fois les versions Python et C d'un module. La version Python est écrite pour être aussi claire et évidente que possible - tout bogue doit être facile à diagnostiquer et à corriger. Ecrivez vos tests contre ce module. Ensuite, écrivez la version C et testez-la. Son comportement devrait dans tous les cas être égal à celui de l’implémentation Python - s’ils diffèrent, il devrait être très facile de déterminer ce qui ne va pas et de corriger le problème.

23
John Millikin

La première chose qui me vient à l’esprit: psyco . Il ne fonctionne que sur x86, pour le moment.

Ensuite, liaison constante . En d'autres termes, toutes les références globales (et global.attr , global.attr.attr …) soient des noms locaux à l'intérieur de fonctions et de méthodes. Ce n'est pas toujours réussi, mais en général cela fonctionne. Cela peut être fait à la main, mais c'est évidemment fastidieux.

Vous avez dit en dehors de l'optimisation dans le code, donc je ne vais pas approfondir cette question, mais gardez votre esprit ouvert pour les erreurs typiques (for i in range(10000000) vient à l'esprit) que les gens font.

17
tzot

Cython et pyrex peuvent être utilisés pour générer du code c en utilisant une syntaxe semblable à celle de python. Psyco est également fantastique pour les projets appropriés (parfois, vous ne remarquerez pas beaucoup de vitesse, parfois 50 fois plus vite). Je pense toujours que le meilleur moyen est de profiler votre code (cProfile, etc.) et ensuite de coder les goulots d'étranglement en tant que fonctions c pour python.

9
hacama

Je suis surpris que personne ne mentionne ShedSkin: http://code.google.com/p/shedskin/ , il convertit automatiquement votre programme python en C++ et, dans certains cas, il offre de meilleures améliorations que psyco en termes de rapidité.

De plus, des anecdotes sur la simplicité: http://pyinsci.blogspot.com/2006/12/trying-out-latest-release-of-shedskin.html

Il y a cependant des limitations, veuillez consulter: http://tinyurl.com/shedskin-limitations

7
torial

J'espère que vous avez lu: http://wiki.python.org/moin/PythonSpeed/PerformanceTips

En reprenant ce qui existe déjà, il existe habituellement 3 principes:

  • écrivez du code qui est transformé en meilleur bytecode, par exemple, utilisez des locales, évitez les recherches/appels inutiles, utilisez des constructions idiomatiques (s'il existe une syntaxe naturelle pour ce que vous voulez, utilisez-la - généralement plus rapidement. par exemple: ne faites pas: "pour entrer some_dict.keys () ", do" pour la clé dans some_dict ")
  • tout ce qui est écrit en C est considérablement plus rapide, abusez des fonctions/modules C disponibles
  • en cas de doute, importez timeit, profile
5
ionelmc

Cela n'accélérera pas nécessairement votre code, mais constitue une connaissance essentielle de la programmation en Python si vous souhaitez éviter de ralentir votre code. Le "Global Interpreter Lock" (GIL) a le potentiel de réduire considérablement la vitesse de votre programme multi-thread si son comportement n'est pas compris (oui, ce bit m'a été malin ... j'avais une machine à 4 processeurs qui ne utiliser plus de 1,2 processeurs à la fois). Il y a un article d'introduction avec quelques liens pour vous aider à démarrer à SmoothSpan .

4
Steve Moyer

Exécutez votre application via le profileur Python. Trouvez un goulot d'étranglement sérieux. Réécrivez ce goulot d'étranglement en C. Répétez.

4
Vicent Marti

Les gens ont donné de bons conseils, mais vous devez savoir que, lorsque des performances élevées sont requises, le modèle python est le suivant: punt to c. Des efforts tels que psyco pourraient à l'avenir aider un peu, mais python n'est pas un langage rapide, et il n'est pas conçu pour l'être. Très peu de langues ont la capacité de vraiment bien faire le travail dynamique tout en générant un code très rapide. au moins pour l'avenir prévisible (et une partie de la conception va à l'encontre d'une compilation rapide), ce sera le cas.

Donc, si vous vous trouvez vraiment dans cette impasse, votre meilleur pari sera d'isoler les parties de votre système qui sont inacceptablement lentes en (bon) python, et de concevoir autour de l'idée que vous allez réécrire ces bits en C. Désolé. Un bon design peut aider à rendre cela moins douloureux. Prototypez-le en python d'abord, ensuite, vous aurez également facilement un contrôle de santé mentale sur votre c.

Cela fonctionne assez bien pour des choses comme Numpy, après tout. Je ne saurais trop insister sur le fait qu'un bon design peut vous aider. Si vous piquez simplement vos bits python de façon itérative et remplacez les plus lents par C, vous risquez de vous retrouver avec un gros désordre. Pensez exactement où les bits C sont nécessaires et comment ils peuvent être minimisés et encapsulés de manière judicieuse.

4
simon

Il est souvent possible d'atteindre des vitesses proches du C (suffisamment proches pour tout projet utilisant Python en premier lieu!) En remplaçant les algorithmes explicites écrits à la longue en Python par un algorithme implicite utilisant un appel Python intégré. Cela fonctionne car la plupart des fonctions intégrées à Python sont écrites en C de toute façon. Bien, en CPython bien sûr ;-) https://www.python.org/doc/essays/list2str/

3
sep332

C'est la procédure que j'essaie de suivre:

  • importer psyco; psyco.full ()
  • Si ce n'est pas assez rapide, exécutez le code dans un profileur pour voir où se trouvent les goulots d'étranglement. (DÉSACTIVEZ psyco pour cette étape!)
  • Essayez de faire ce que d'autres personnes ont mentionné pour obtenir le code le plus rapidement possible au niveau de ces goulots d'étranglement.
    • Des éléments comme [str (x) pour x dans l] ou [x.strip () pour x en l]] sont beaucoup, beaucoup plus lents que map (str, x) ou map (str.strip, x).
  • Après cela, si j’ai encore besoin de plus de vitesse, il est très facile de faire fonctionner PyRex. Je copie d'abord une section de code python, la mets directement dans le code pyrex et vois ce qui se passe. Ensuite, je le tourne jusqu'à ce que cela devienne de plus en plus rapide.
3
Claudiu

Juste une remarque sur l’utilisation de psyco: Dans certains cas, cela peut produire des temps d’exécution plus lents. Surtout lorsque j'essaie d'utiliser psyco avec du code écrit en C. Je ne me souviens pas de l'article que j'ai lu, mais les fonctions map() et reduce() ont été spécifiquement mentionnées. Heureusement, vous pouvez dire à psyco de ne pas gérer des fonctions et/ou des modules spécifiques.

3
Walter

La référence canonique pour améliorer le code Python est la suivante: PerformanceTips . Je recommande de ne pas optimiser en C sauf si vous en avez vraiment besoin. Pour la plupart des applications, vous pouvez obtenir les performances dont vous avez besoin en suivant les règles affichées dans ce lien.

2
Jason Baker

Si vous utilisez psyco, je recommanderais psyco.profile() au lieu de psyco.full(). Pour un projet plus important, les fonctions optimisées utilisant moins de mémoire seront plus intelligentes.

Je recommanderais également de regarder les itérateurs et les générateurs. Si votre application utilise de grands ensembles de données, cela vous évitera de nombreuses copies de conteneurs.

1
Peter Shinners

Outre le (grand) psyco et le (Nice) shedskin , je vous conseillerais d'essayer cython une excellente fourchette de pyrex .

Ou, si vous n'êtes pas pressé, je vous recommande d'attendre. De nouvelles machines virtuelles python arrivent, et unladen-swallow se frayera un chemin dans le courant dominant.

1
Davide

Pour un projet établi, je pense que le principal avantage en termes de performances sera d'utiliser autant que possible la librairie interne Python.

Quelques conseils sont ici: http://blog.hackerearth.com/faster-python-code

0

Deux façons d'accélérer le code Python ont été introduites après avoir posé cette question:

  • Pypy a un compilateur JIT, ce qui le rend beaucoup plus rapide pour le code lié au CPU.
  • Pypy est écrit dans Rpython , un sous-ensemble de Python qui compile en code natif, en exploitant la chaîne d'outils LLVM.
0
Janus Troelsen