web-dev-qa-db-fra.com

Comment pouvez-vous profiler un script Python?

Le projet Euler et les autres concours de codage ont souvent un délai d'exécution maximal ou les utilisateurs se vantent de la rapidité d'exécution de leur solution. Avec python, les approches sont parfois un peu kludgey - c’est-à-dire l’ajout de code de minutage à __main__.

Quel est un bon moyen de définir le temps qu’il faut à un programme python pour s'exécuter?

1166
Chris Lawlor

Python inclut un profileur appelé cProfile . Il indique non seulement le temps total d'exécution, mais également les temps de chaque fonction séparément et vous indique le nombre de fois où chaque fonction a été appelée, ce qui permet de déterminer facilement où des optimisations doivent être effectuées.

Vous pouvez l'appeler depuis votre code, ou depuis l'interprète, comme ceci:

import cProfile
cProfile.run('foo()')

Encore plus utilement, vous pouvez invoquer cProfile lors de l’exécution d’un script:

python -m cProfile myscript.py

Pour rendre cela encore plus facile, j'ai créé un petit fichier batch appelé 'profile.bat':

python -m cProfile %1

Donc tout ce que j'ai à faire est de courir:

profile euler048.py

Et je reçois ceci:

1007 function calls in 0.061 CPU seconds

Ordered by: standard name
ncalls  tottime  percall  cumtime  percall filename:lineno(function)
    1    0.000    0.000    0.061    0.061 <string>:1(<module>)
 1000    0.051    0.000    0.051    0.000 euler048.py:2(<lambda>)
    1    0.005    0.005    0.061    0.061 euler048.py:2(<module>)
    1    0.000    0.000    0.061    0.061 {execfile}
    1    0.002    0.002    0.053    0.053 {map}
    1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler objects}
    1    0.000    0.000    0.000    0.000 {range}
    1    0.003    0.003    0.003    0.003 {sum}

EDIT: Mise à jour du lien vers une bonne ressource vidéo de PyCon 2013 intitulée Profil Python (---)
Également via YouTube .

1243
Chris Lawlor

Il y a quelque temps, j'ai créé pycallgraph qui génère une visualisation à partir de votre code Python. Edit: J'ai mis à jour l'exemple pour qu'il fonctionne avec la version 3.3, la dernière version en date de cette écriture.

Après un pip install pycallgraph et une installation de GraphViz , vous pouvez l’exécuter à partir de la ligne de commande:

pycallgraph graphviz -- ./mypythonscript.py

Ou, vous pouvez profiler des parties particulières de votre code:

from pycallgraph import PyCallGraph
from pycallgraph.output import GraphvizOutput

with PyCallGraph(output=GraphvizOutput()):
    code_to_profile()

L'un ou l'autre générera un fichier pycallgraph.png similaire à l'image ci-dessous:

enter image description here

391
gak

Il est intéressant de noter que l'utilisation du profileur ne fonctionne (par défaut) que sur le thread principal et que vous ne recevrez aucune information des autres threads si vous les utilisez. Cela peut être un peu un piège car il est complètement non mentionné dans la documentation du profileur .

Si vous souhaitez également profiler les threads, vous voudrez probablement vous reporter à la fonction threading.setprofile() dans la documentation.

Vous pouvez également créer votre propre sous-classe threading.Thread pour le faire:

class ProfiledThread(threading.Thread):
    # Overrides threading.Thread.run()
    def run(self):
        profiler = cProfile.Profile()
        try:
            return profiler.runcall(threading.Thread.run, self)
        finally:
            profiler.dump_stats('myprofile-%d.profile' % (self.ident,))

et utilisez cette classe ProfiledThread à la place de la classe standard. Cela pourrait vous donner plus de souplesse, mais je ne suis pas sûr que cela en vaille la peine, surtout si vous utilisez du code tiers qui n'utiliserait pas votre classe.

190
Joe Shaw

Le wiki python est une excellente page pour le profilage des ressources: http://wiki.python.org/moin/PythonSpeed/PerformanceTips#Profiling_Code

comme le python _ docs: http://docs.python.org/library/profile.html

comme l'a montré Chris Lawlor cProfile est un excellent outil qui peut facilement être utilisé pour imprimer à l'écran:

python -m cProfile -s time mine.py <args>

ou déposer:

python -m cProfile -o output.file mine.py <args>

PS> Si vous utilisez Ubuntu, assurez-vous d’installer python-profile

Sudo apt-get install python-profiler 

Si vous exportez dans un fichier, vous pouvez obtenir des visualisations Nice à l'aide des outils suivants

PyCallGraph: un outil pour créer des images de graphes d'appels
installer:

 Sudo pip install pycallgraph

courir:

 pycallgraph mine.py args

vue:

 gimp pycallgraph.png

Vous pouvez utiliser ce que vous voulez pour voir le fichier png, j'ai utilisé gimp
Malheureusement, j'ai souvent

point: le graphique est trop volumineux pour les bitmaps cairo-renderer. Mise à l'échelle par 0,257079 pour s'adapter

ce qui rend mes images inutilement petites. Donc, je crée généralement des fichiers svg:

pycallgraph -f svg -o pycallgraph.svg mine.py <args>

PS> assurez-vous d’installer graphviz (qui fournit le programme dot):

Sudo pip install graphviz

Graphique alternatif utilisant gprof2dot via @maxy/@quodlibetor:

Sudo pip install gprof2dot
python -m cProfile -o profile.pstats mine.py
gprof2dot -f pstats profile.pstats | dot -Tsvg -o mine.svg
137
brent.payne

Le commentaire de @ Maxy sur cette réponse m'a aidé suffisamment pour que je pense qu'il mérite sa propre réponse: j'avais déjà des fichiers .pstats générés par cProfile et je ne voulais pas ré-exécuter les choses avec pycallgraph, alors J'ai utilisé gprof2dot , et j'ai obtenu de jolies svgs:

$ Sudo apt-get install graphviz
$ git clone https://github.com/jrfonseca/gprof2dot
$ ln -s "$PWD"/gprof2dot/gprof2dot.py ~/bin
$ cd $PROJECT_DIR
$ gprof2dot.py -f pstats profile.pstats | dot -Tsvg -o callgraph.svg

et BLAM!

Il utilise point (la même chose que pycallgraph utilise) pour que le résultat soit similaire. J'ai l'impression que gprof2dot perd moins d'informations cependant:

gprof2dot example output

129
quodlibetor

Je suis tombé sur un outil pratique appelé SnakeViz lors de mes recherches sur ce sujet. SnakeViz est un outil de visualisation de profilage basé sur le Web. Il est très facile à installer et à utiliser. La façon habituelle dont je l’utilise est de générer un fichier de statistiques avec %prun puis de faire une analyse dans SnakeViz.

La principale technique de visualisation utilisée est le diagramme de Sunburst , comme indiqué ci-dessous, dans lequel la hiérarchie des appels de fonction est agencée sous forme de couches d'arcs et d'informations temporelles codées dans leur angular largeurs.

La meilleure chose à faire est que vous puissiez interagir avec le graphique. Par exemple, pour zoomer, on peut cliquer sur un arc, et l'arc et ses descendants seront agrandis comme un nouveau rayon de soleil pour afficher plus de détails.

enter image description here

61
zaxliu

Je pense que cProfile est excellent pour le profilage, tandis que kcachegrind est idéal pour visualiser les résultats. Le pyprof2calltree entre gère la conversion de fichier.

_python -m cProfile -o script.profile script.py
pyprof2calltree -i script.profile -o script.calltree
kcachegrind script.calltree
_

Pour installer les outils requis (sur Ubuntu au moins):

_apt-get install kcachegrind
pip install pyprof2calltree
_

Le résultat:

Screenshot of the result

46
Federico

Il convient également de mentionner le visualiseur de dump cProfile de l'interface graphique RunSnakeRun . Il vous permet de trier et de sélectionner, en zoomant ainsi sur les parties pertinentes du programme. La taille des rectangles dans l'image est proportionnelle au temps pris. Si vous passez la souris sur un rectangle, il met en surbrillance cet appel dans le tableau et partout sur la carte. Lorsque vous double-cliquez sur un rectangle, il effectue un zoom avant sur cette partie. Il vous montrera qui appelle cette partie et comment cette partie appelle.

Les informations descriptives sont très utiles. Il vous montre le code de ce bit, ce qui peut être utile lorsque vous traitez avec des appels de bibliothèque intégrés. Il vous dit quel fichier et quelle ligne trouver le code.

Je veux également indiquer que le PO a dit "profiler", mais il semble vouloir dire "timing". Gardez à l'esprit que les programmes fonctionneront plus lentement une fois profilés.

enter image description here

40
Pete

Le plus simple et le plus rapide moyen de trouver où tout le temps se passe.

1. pip install snakeviz

2. python -m cProfile -o temp.dat <PROGRAM>.py

3. snakeviz temp.dat

Dessine un graphique en secteurs dans un navigateur. Le plus gros morceau est la fonction du problème. Très simple.

36
CodeCabbie

Line_profiler (appelé à l’aide du script kernprof.py) est un module de profilage de Nice. Il peut être téléchargé ici .

Si j'ai bien compris, cProfile ne donne que des informations sur le temps total passé dans chaque fonction. Donc, les lignes de code individuelles ne sont pas chronométrées. C'est un problème en informatique scientifique, car souvent une seule ligne peut prendre beaucoup de temps. De plus, si je me souviens bien, cProfile n’a pas saisi le temps que je passais à dire numpy.dot.

31
Ian Langmore

profil

line_profiler (déjà présenté ici) a aussi inspiré pprofile , décrit comme suit:

Granularité linéaire, déterministe et modérateur statistique pur en python

Il fournit une granularité de ligne comme line_profiler, est pur Python, peut être utilisé comme une commande autonome ou un module, et peut même générer des fichiers au format callgrind qui peuvent être facilement analysés avec [k|q]cachegrind.

vprof

Il existe également vprof , un package Python décrit comme suit:

[...] fournissant des visualisations riches et interactives pour diverses Python caractéristiques du programme, telles que le temps d'exécution et l'utilisation de la mémoire.

heatmap

29
BenC

J'ai récemment créé thon pour visualiser Python runtime et importer des profils; cela peut être utile ici.

enter image description here

Installer avec

pip3 install tuna

Créer un profil d'exécution

python -mcProfile -o program.prof yourfile.py

ou un profil d'importation (Python 3.7+ requis)

python -X importprofile yourfile.py 2> import.log

Ensuite, il suffit de lancer le thon sur le fichier

tuna program.prof
19
Nico Schlömer

Il y a beaucoup de bonnes réponses, mais ils utilisent la ligne de commande ou un programme externe pour profiler et/ou trier les résultats.

J'ai vraiment manqué un moyen que je pourrais utiliser dans mon IDE (Eclipse-PyDev) sans toucher à la ligne de commande ou installer quoi que ce soit. Alors voila.

Profiling sans ligne de commande

def count():
    from math import sqrt
    for x in range(10**5):
        sqrt(x)

if __== '__main__':
    import cProfile, pstats
    cProfile.run("count()", "{}.profile".format(__file__))
    s = pstats.Stats("{}.profile".format(__file__))
    s.strip_dirs()
    s.sort_stats("time").print_stats(10)

Voir docs ou d'autres réponses pour plus d'informations.

13
David Mašek

Suite à la réponse de Joe Shaw sur le code multi-thread ne fonctionnant pas comme prévu, je me suis dit que la méthode runcall de cProfile se contente de faire des appels self.enable() et self.disable() autour de l'appel de fonction profilée. peut simplement le faire vous-même et avoir le code que vous voulez entre les deux en interférant le moins possible avec le code existant.

12
PypeBros

Dans Virtaal's source , il existe une classe et un décorateur très utiles qui peuvent faciliter le profilage (même pour des méthodes/fonctions spécifiques). La sortie peut alors être visualisée très confortablement dans KCacheGrind.

10
Walter

le profil cProfile est excellent pour le profilage rapide, mais la plupart du temps, il se terminait par des erreurs. La fonction runctx résout ce problème en initialisant correctement l'environnement et les variables, en espérant qu'il puisse être utile à quelqu'un:

import cProfile
cProfile.runctx('foo()', None, locals())
9
Datageek

Ma façon est d'utiliser yappi ( https://code.google.com/p/yappi/ ). C'est particulièrement utile en combinaison avec un serveur RPC où (même juste pour le débogage) vous enregistrez une méthode pour démarrer, arrêter et imprimer des informations de profilage, par exemple. de cette façon:

@staticmethod
def startProfiler():
    yappi.start()

@staticmethod
def stopProfiler():
    yappi.stop()

@staticmethod
def printProfiler():
    stats = yappi.get_stats(yappi.SORTTYPE_TTOT, yappi.SORTORDER_DESC, 20)
    statPrint = '\n'
    namesArr = [len(str(stat[0])) for stat in stats.func_stats]
    log.debug("namesArr %s", str(namesArr))
    maxNameLen = max(namesArr)
    log.debug("maxNameLen: %s", maxNameLen)

    for stat in stats.func_stats:
        nameAppendSpaces = [' ' for i in range(maxNameLen - len(stat[0]))]
        log.debug('nameAppendSpaces: %s', nameAppendSpaces)
        blankSpace = ''
        for space in nameAppendSpaces:
            blankSpace += space

        log.debug("adding spaces: %s", len(nameAppendSpaces))
        statPrint = statPrint + str(stat[0]) + blankSpace + " " + str(stat[1]).ljust(8) + "\t" + str(
            round(stat[2], 2)).ljust(8 - len(str(stat[2]))) + "\t" + str(round(stat[3], 2)) + "\n"

    log.log(1000, "\nname" + ''.ljust(maxNameLen - 4) + " ncall \tttot \ttsub")
    log.log(1000, statPrint)

Ensuite, lorsque votre programme fonctionne, vous pouvez démarrer le profileur à tout moment en appelant la méthode RPC startProfiler et vider les informations de profilage dans un fichier journal en appelant printProfiler (ou en modifiant la méthode rpc pour le renvoyer à l'appelant). et obtenir une telle sortie:

2014-02-19 16:32:24,128-|SVR-MAIN  |-(Thread-3   )-Level 1000: 
name                                                                                                                                      ncall     ttot    tsub
2014-02-19 16:32:24,128-|SVR-MAIN  |-(Thread-3   )-Level 1000: 
C:\Python27\lib\sched.py.run:80                                                                                                           22        0.11    0.05
M:\02_documents\_repos\09_aheadRepos\apps\ahdModbusSrv\pyAheadRpcSrv\xmlRpc.py.iterFnc:293                                                22        0.11    0.0
M:\02_documents\_repos\09_aheadRepos\apps\ahdModbusSrv\serverMain.py.makeIteration:515                                                    22        0.11    0.0
M:\02_documents\_repos\09_aheadRepos\apps\ahdModbusSrv\pyAheadRpcSrv\PicklingXMLRPC.py._dispatch:66                                       1         0.0     0.0
C:\Python27\lib\BaseHTTPServer.py.date_time_string:464                                                                                    1         0.0     0.0
c:\users\zasiec~1\appdata\local\temp\easy_install-hwcsr1\psutil-1.1.2-py2.7-win32.Egg.tmp\psutil\_psmswindows.py._get_raw_meminfo:243     4         0.0     0.0
C:\Python27\lib\SimpleXMLRPCServer.py.decode_request_content:537                                                                          1         0.0     0.0
c:\users\zasiec~1\appdata\local\temp\easy_install-hwcsr1\psutil-1.1.2-py2.7-win32.Egg.tmp\psutil\_psmswindows.py.get_system_cpu_times:148 4         0.0     0.0
<string>.__new__:8                                                                                                                        220       0.0     0.0
C:\Python27\lib\socket.py.close:276                                                                                                       4         0.0     0.0
C:\Python27\lib\threading.py.__init__:558                                                                                                 1         0.0     0.0
<string>.__new__:8                                                                                                                        4         0.0     0.0
C:\Python27\lib\threading.py.notify:372                                                                                                   1         0.0     0.0
C:\Python27\lib\rfc822.py.getheader:285                                                                                                   4         0.0     0.0
C:\Python27\lib\BaseHTTPServer.py.handle_one_request:301                                                                                  1         0.0     0.0
C:\Python27\lib\xmlrpclib.py.end:816                                                                                                      3         0.0     0.0
C:\Python27\lib\SimpleXMLRPCServer.py.do_POST:467                                                                                         1         0.0     0.0
C:\Python27\lib\SimpleXMLRPCServer.py.is_rpc_path_valid:460                                                                               1         0.0     0.0
C:\Python27\lib\SocketServer.py.close_request:475                                                                                         1         0.0     0.0
c:\users\zasiec~1\appdata\local\temp\easy_install-hwcsr1\psutil-1.1.2-py2.7-win32.Egg.tmp\psutil\__init__.py.cpu_times:1066               4         0.0     0.0 

Cela peut ne pas être très utile pour les scripts courts, mais cela aide à optimiser les processus de type serveur, en particulier étant donné que la méthode printProfiler peut être appelée plusieurs fois dans le temps pour établir un profil et comparer, par exemple. différents scénarios d'utilisation du programme.

6
Mr. Girgitt

PyVmMonitor est un nouvel outil permettant de gérer le profilage dans Python: http://www.pyvmmonitor.com/

Il a des caractéristiques uniques telles que

  • Attacher le profileur à un programme en cours d'exécution (CPython)
  • Profilage à la demande avec intégration Yappi
  • Profil sur une autre machine
  • Prise en charge de plusieurs processus (multitraitement, Django ...)
  • Vue en direct de l'échantillonnage/du processeur (avec sélection d'une plage de temps)
  • Profilage déterministe par intégration de cProfile/profile
  • Analyser les résultats PStats existants
  • Ouvrir les fichiers DOT
  • Accès à l'API programmé
  • Grouper les échantillons par méthode ou par ligne
  • Intégration PyDev
  • Intégration PyCharm

Remarque: c'est commercial, mais gratuit pour l'open source.

3
Fabio Zadrozny

Cela dépend de ce que vous voulez voir dans le profilage. Des métriques de temps simples peuvent être données par (bash).

time python python_prog.py

Même '/ usr/bin/time' peut produire des métriques détaillées en utilisant l'indicateur '--verbose'.

Pour vérifier les métriques de temps données par chaque fonction et pour mieux comprendre le temps consacré aux fonctions, vous pouvez utiliser le fichier cProfile intégré en python.

Si l’on se base sur des indicateurs plus détaillés comme les performances, le temps n’est pas le seul indicateur. Vous pouvez vous soucier de la mémoire, des threads, etc.
Options de profilage:
1. line_profiler est un autre profileur utilisé couramment pour rechercher des métriques de minutage ligne par ligne.
2. memory_profiler est un outil permettant de profiler l'utilisation de la mémoire.
3. tas (à partir du projet Guppy) Profilez comment les objets du tas sont utilisés.

Ce sont quelques-uns des plus communs que j'ai tendance à utiliser. Mais si vous voulez en savoir plus, essayez de lire ceci livre C'est un très bon livre sur les débuts avec la performance en tête. Vous pouvez passer à des rubriques avancées sur l’utilisation de Cython et du python compilé JIT (Just-in-Time).

3
VishalMishra

Vous voulez toujours savoir ce que diable fait le script python? Entrez dans le shell d'inspection. Inspect Shell vous permet d'imprimer/de modifier des éléments globaux et d'exécuter des fonctions sans interrompre le script en cours d'exécution. Maintenant avec l'auto-complétion et l'historique des commandes (uniquement sur linux).

Inspect Shell n'est pas un débogueur de style pdb.

https://github.com/amoffat/Inspect-Shell

Vous pouvez utiliser cela (et votre montre).

3
Colonel Panic

Pour ajouter à https://stackoverflow.com/a/582337/1070617 ,

J'ai écrit ce module qui vous permet d'utiliser cProfile et d'afficher facilement sa sortie. Plus ici: https://github.com/ymichael/cprofilev

$ python -m cprofilev /your/python/program
# Go to http://localhost:4000 to view collected statistics.

Voir aussi: http://ymichael.com/2014/03/08/profiling-python-with-cprofile.html sur la manière de donner un sens aux statistiques collectées.

3
michael

Il existe également un profileur statistique appelé statprof . C'est un profileur d'échantillonnage, ce qui ajoute une surcharge minimale à votre code et donne des timings basés sur les lignes (pas seulement sur les fonctions). Il est plus adapté aux applications temps réel comme les jeux, mais peut-être moins précis que cProfile.

Le version in pypi est un peu ancien, vous pouvez donc l'installer avec pip en spécifiant le référentiel git :

pip install git+git://github.com/bos/statprof.py@1a33eba91899afe17a8b752c6dfdec6f05dd0c01

Vous pouvez l'exécuter comme ceci:

import statprof

with statprof.profile():
    my_questionable_function()

Voir aussi https://stackoverflow.com/a/10333592/320036

1
z0r

Quand je ne suis pas root sur le serveur, j'utilise lsprofcalltree.py et exécute mon programme comme suit:

python lsprofcalltree.py -o callgrind.1 test.py

Ensuite, je peux ouvrir le rapport avec n’importe quel logiciel compatible callgrind, tel que qcachegrind

0
Vincent Fenet

La solution de terminal uniquement (et la plus simple) si toutes ces interfaces utilisateur sophistiquées ne parviennent pas à s'installer ou à s'exécuter:
ignore complètement cProfile et le remplace par pyinstrument, qui collectera et affichera l’arbre des appels juste après son exécution.

Installer:

$ pip install pyinstrument

Profil et résultat d'affichage:

$ python -m pyinstrument ./prog.py

Fonctionne avec python2 et 3.

0
Francois

gprof2dot_magic

Fonction magique permettant à gprof2dot de profiler une instruction Python sous forme de graphique DOT dans JupyterLab ou Jupyter Notebook.

enter image description here

Repo GitHub: https://github.com/mattijn/gprof2dot_magic

installation

Assurez-vous que le package Python _ gprof2dot_magic.

pip install gprof2dot_magic

Ses dépendances gprof2dot et graphviz seront également installées

usage

Pour activer la fonction magique, chargez d'abord le module gprof2dot_magic

%load_ext gprof2dot_magic

puis profilez toute instruction de ligne sous forme de graphique DOT en tant que telle:

%gprof2dot print('hello world')

enter image description here

0
Mattijn