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?
À 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:
Et la règle Knuth:
Les règles les plus utiles se trouvent dans Règles générales d'optimisation .
Ne pas optimiser comme vous allez. D'abord bien faire les choses Alors, vite. Optimiser un mauvais programme est toujours faux.
Rappelez-vous la règle des 80/20.
Exécutez toujours les repères "avant" et "après". Sinon, vous ne saurez pas si vous avez trouvé le 80%.
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.
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:
numpy
.Twisted
.Si tout ce qui précède échoue pour le code profilé et mesuré, commencez à réfléchir au chemin de réécriture C.
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.
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.
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.
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
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:
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 .
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.
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.
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/
C'est la procédure que j'essaie de suivre:
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.
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.
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.
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.
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
Deux façons d'accélérer le code Python ont été introduites après avoir posé cette question: