Comment pouvez-vous le profil d'un script Python?
-
06-09-2019 - |
Question
Euler projet et d'autres concours de codage ont souvent une durée maximale d'exécution ou les gens se vanter de la rapidité de leur solution particulière fonctionne. Avec python, parfois, les approches sont quelque peu kludgey - à savoir l'ajout de code temporel à __main__
Qu'est-ce qu'un bon moyen de profil combien de temps un programme Python prend pour exécuter?
La solution
Python comprend un profileur appelé cprofile. Elle ne donne que le temps total de la course, mais aussi de chaque fonction séparément, et vous indique combien de fois chaque fonction a été appelée, le rendant facile à déterminer où vous devriez faire des optimisations.
Vous pouvez appeler à partir de votre code, ou de l'interprète, comme ceci:
import cProfile
cProfile.run('foo()')
Encore plus utile, vous pouvez invoquer le cprofile lors de l'exécution d'un script:
python -m cProfile myscript.py
Pour le rendre encore plus facile, je fait un petit fichier batch appelé « profile.bat »:
python -m cProfile %1
Donc tout ce que je dois faire est de lancer:
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: lien Mise à jour à une bonne ressource vidéo à partir PyCon 2013 intitulé
profilage Python
également via YouTube.
Autres conseils
Il y a quelque temps que je fait pycallgraph
qui génère une visualisation à partir de votre code Python. Edit:. J'ai mis à jour l'exemple de travailler avec 3.3, la dernière version de cette écriture
Après un pip install pycallgraph
et l'installation GraphViz vous pouvez l'exécuter à partir de la ligne de commande:
pycallgraph graphviz -- ./mypythonscript.py
Ou, vous pouvez profiler certaines parties de votre code:
from pycallgraph import PyCallGraph
from pycallgraph.output import GraphvizOutput
with PyCallGraph(output=GraphvizOutput()):
code_to_profile()
Soit ces éléments vont générer un fichier pycallgraph.png
similaire à l'image ci-dessous:
Il est intéressant de souligner que l'utilisation du profileur fonctionne uniquement (par défaut) sur le thread principal, et vous n'obtenir aucune information d'autres sujets si vous les utilisez. Cela peut être un peu Gotcha car il est complètement passé sous silence dans le documentation profileur .
Si vous voulez aussi le profil de threads, vous aurez envie de regarder le titre fonction threading.setprofile()
dans la documentation.
Vous pouvez aussi 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 utiliser cette classe ProfiledThread
au lieu de la norme. Il pourrait vous donner plus de flexibilité, mais je ne suis pas sûr que ça vaut le coup, surtout si vous utilisez le code tiers qui ne serait pas utiliser votre classe.
Le python de wiki est une grande page pour les ressources de profilage: http://wiki.python.org/moin/PythonSpeed/PerformanceTips#Profiling_Code
tout comme le python docs: http://docs.python.org/library/profile.html
comme le montre Chris Lawlor cprofile est un excellent outil et peut facilement être utilisé pour imprimer à l'écran:
python -m cProfile -s time mine.py <args>
ou le fichier:
python -m cProfile -o output.file mine.py <args>
PS> Si vous utilisez Ubuntu, assurez-vous d'installer profil python
sudo apt-get install python-profiler
Si vous sortie le fichier que vous pouvez obtenir de belles visualisations en utilisant les outils suivants
PyCallGraph: un outil pour créer des images appels graphique
installation:
sudo pip install pycallgraph
run:
pycallgraph mine.py args
vue:
gimp pycallgraph.png
Vous pouvez utiliser ce que vous voulez pour voir le fichier png, je gimp Malheureusement, je reçois souvent
point: graphique est trop grand pour bitmaps cairo-renderer. Mise à l'échelle par 0.257079 pour adapter
ce qui rend mes images unusably 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 de points):
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
@ commentaire de Maxy sur cette réponse m'a aidé assez que je pense qu'il mérite sa propre réponse: j'avais déjà cprofile fichiers -generated de .pstats et je ne voulais pas relancer les choses avec pycallgraph, donc j'utilisé gprof2dot , et a obtenu de jolis 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 le point (la même chose que pycallgraph utilise) si la sortie ressemble. J'ai l'impression que gprof2dot perd moins d'informations que:
Je suis tombé sur un outil pratique appelé SnakeViz lors de 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 manière habituelle, je l'utilise est de générer un fichier stat avec %prun
puis faire une analyse en SnakeViz.
La principale technique de savoir utilisé est diagramme de rayon de soleil comme indiqué ci-dessous, dans laquelle la hiérarchie des appels de fonction est agencé en tant que couches d'arcs et les informations de temps codées dans leurs largeurs angulaires.
La meilleure chose est que vous pouvez interagir avec le tableau. Par exemple, pour zoomer on peut cliquer sur un arc, et l'arc et ses descendants seront agrandis en tant que nouveau sunburst pour afficher plus de détails.
Je pense que cProfile
est grand pour le profilage, tandis que kcachegrind
est idéal pour visualiser les résultats. pyprof2calltree
entre les poignées de la conversion de fichiers.
python -m cProfile -o script.profile script.py
pyprof2calltree -i script.profile -o script.calltree
kcachegrind script.calltree
Pour installer les outils nécessaires (sur Ubuntu, au moins):
apt-get install kcachegrind
pip install pyprof2calltree
Le résultat:
Il faut aussi mentionner l'interface graphique cprofile spectateur vidage RunSnakeRun. Il vous permet de trier et de sélectionner, zoomer ainsi dans les parties pertinentes du programme. Les dimensions des rectangles de l'image est proportionnelle au temps nécessaire. Si vous passez la souris sur un rectangle, il met en évidence 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 ce qui appelle la partie.
L'information descriptive est très utile. Il vous montre le code pour ce bit qui peut être utile lorsque vous traitez avec des appels de bibliothèque intégrés. Il vous indique quel fichier et de la ligne pour trouver le code.
tiens également à souligner à ce que l'OP a dit: « profilage », mais il semble qu'il voulait dire « timing ». Gardez à l'esprit des programmes d'fonctionnera plus lentement lorsque profilé.
Simplest et plus rapide façon de trouver où tout le temps va.
1. pip install snakeviz
2. python -m cProfile -o temp.dat <PROGRAM>.py
3. snakeviz temp.dat
Dessine un diagramme circulaire dans un navigateur. Le plus gros morceau est la fonction de problème. Très simple.
Un module de profilage agréable est le line_profiler (appelé à l'aide du kernprof.py script). Il peut être téléchargé .
Ma compréhension est que cprofile ne donne que des informations sur le temps total passé dans chaque fonction. Ainsi, les lignes individuelles de code ne sont pas chronométrées. Ceci est un problème dans le calcul scientifique car, souvent, une seule ligne peut prendre beaucoup de temps. En outre, comme je me souviens, cprofile n'a pas pris le temps que je passais à dire numpy.dot.
pProfile
line_profiler
(déjà présenté ici) a également inspiré pprofile
, qui est décrit comme suit:
Ligne granularité, fil courant déterministe et statistique pure python profileur
Il fournit des lignes granularité 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 callgrind format qui peuvent être facilement analysés avec [k|q]cachegrind
.
vprof
Il y a aussi vprof , un paquet Python décrit comme:
[...] fournissant visualisations riches et interactifs pour diverses caractéristiques du programme Python comme le temps en cours d'exécution et l'utilisation de la mémoire.
J'ai récemment créé thon pour visualiser les profils d'exécution et d'importation Python; cela peut être utile ici.
Installer avec
pip3 install tuna
Créer un profil d'exécution
python -mcProfile -o program.prof yourfile.py
ou un profil d'importation (obligatoire Python 3.7+)
python -X importprofile yourfile.py 2> import.log
Ensuite, il suffit d'exécuter le thon sur le fichier
tuna program.prof
Il y a beaucoup de bonnes réponses mais soit la ligne de commande d'utilisation ou d'un programme externe pour le profilage et / ou trier les résultats.
J'ai vraiment manqué d'une certaine façon je pourrais utiliser dans mon IDE (Eclipse-PyDev) sans toucher la ligne de commande ou d'installer quoi que ce soit. Alors voilà.
profilage sans ligne de commande
def count():
from math import sqrt
for x in range(10**5):
sqrt(x)
if __name__ == '__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.
Suite à la réponse de Joe Shaw sur le code multi-thread ne pas fonctionner comme prévu, je me suis dit que la méthode runcall
dans cprofile fait simplement des appels self.enable()
et self.disable()
autour de l'appel de fonction profilée, de sorte que vous pouvez simplement faire vous-même et avoir tout ce que le code vous voulez en entre avec un minimum d'interférences avec le code existant.
cprofile est idéal pour le profilage rapide, mais la plupart du temps qu'il mettait fin pour moi les erreurs. Fonction runctx résout ce problème en initialisant correctement l'environnement et les variables, espérons qu'il peut être utile pour quelqu'un:
import cProfile
cProfile.runctx('foo()', None, locals())
Ma façon est d'utiliser Yappi ( https://code.google.com/p/yappi/ ) . Il est particulièrement utile combiné avec un serveur RPC où (même juste pour le débogage) vous méthode inscrivez 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 travail de programme, vous pouvez commencer à profiler à tout moment en appelant la méthode RPC startProfiler
et vider les informations de profilage dans un fichier journal en appelant printProfiler
(ou modifier la méthode rpc pour le retourner à 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
Il ne peut pas être très utile pour les courts scripts mais permet d'optimiser les processus de type serveur en particulier compte tenu de la méthode printProfiler
peut être appelé à plusieurs reprises au fil du temps à cerner et à comparer par exemple différents scénarios d'utilisation du programme.
Jamais eu envie de savoir ce que l'enfer que script python est en train de faire? Entrer le Inspecter Shell. Shell inspectez vous permet d'imprimer / modifier et globals terme fonctions sans interrompre le script en cours d'exécution. Maintenant avec 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 l'utiliser (et votre montre-bracelet).
Pour ajouter à https://stackoverflow.com/a/582337/1070617 ,
J'ai écrit ce module qui vous permet d'utiliser cprofile et de visualiser sa sortie facilement. Plus d'informations 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 / profilage-python-avec-cprofile.html sur la façon de comprendre les statistiques collectées.
Un nouvel outil pour gérer le profilage dans le python est PyVmMonitor: http://www.pyvmmonitor.com/
Il a des caractéristiques uniques telles que
- Joindre profileur à un programme en cours d'exécution (CPython)
- Sur le profilage de la demande avec l'intégration Yappi
- Profil sur une autre machine
- processus multiples support (multitraitement, django ...)
- vue échantillonnage direct / CPU (avec sélection de plage horaire)
- profilage déterministes par l'intégration cprofile / profil
- Analyser les résultats des pstats existants
- Ouvrez les fichiers DOT
- Accès API programatic
- échantillons de groupe par la méthode ou la ligne
- intégration PyDev
- intégration PyCharm
Note:. Il est commercial, mais sans pour l'open source
Cela dépend de ce que vous voulez voir sur le profilage. temps simple les mesures peuvent être données par (bash).
time python python_prog.py
Même '/ usr / bin / time' peut produire des mesures détaillées en utilisant l'indicateur 'verbose'.
Pour vérifier les paramètres de temps donnés par chaque fonction et de mieux comprendre combien de temps est consacré à des fonctions, vous pouvez utiliser le cprofile intégré en python.
Entrant dans des mesures plus détaillées comme la performance, le temps est pas la seule mesure. Vous pouvez vous soucier de la mémoire, etc.
fils
Options de profilage:
1. line_profiler est un autre profileur couramment utilisé pour trouver des mesures de synchronisation ligne par ligne.
2. memory_profiler est un outil pour le profil de la mémoire.
3. heapy (du projet Guppy) Profil comment les objets dans le tas sont utilisés.
Voici quelques-unes des plus courantes que je tends à utiliser. Mais si vous voulez en savoir plus, essayez de lire ce livre Il est un très bon livre sur commencer avec la performance à l'esprit. Vous pouvez déplacer sur des sujets avancés sur l'utilisation Cython et JIT (Just-in-time) python compilé.
Il y a aussi un profileur statistique appelée statprof
. Il est un profileur d'échantillonnage, il ajoute une surcharge minimale à votre code et donne basé sur la ligne (pas seulement basés sur une fonction) timings. Il est plus adapté aux applications en temps réel comme les jeux mous, mais peut-être avoir moins de précision que cprofile.
Le la version en pypi est un peu vieux, donc peut l'installer avec pip
par le dépôt 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
Quand je ne suis pas root sur le serveur, j'utilise lsprofcalltree.py et exécuter mon programme comme celui-ci:
python lsprofcalltree.py -o callgrind.1 test.py
Ensuite, je peux ouvrir le rapport avec un logiciel compatible callgrind, comme qcachegrind
gprof2dot_magic
Fonction Magic gprof2dot
à profil tout énoncé Python comme un graphique DOT dans Notebook JupyterLab ou Jupyter.
GitHub repo: https://github.com/mattijn/gprof2dot_magic
Installation
Assurez-vous que vous avez le paquet Python gprof2dot_magic
.
pip install gprof2dot_magic
Ses dépendances gprof2dot
et graphviz
seront installés et
utilisation
Pour activer la fonction magique, d'abord charger le module gprof2dot_magic
%load_ext gprof2dot_magic
et le profil de toute déclaration de ligne sous forme de graphique DOT en tant que tel:
%gprof2dot print('hello world')
La solution terminale uniquement (et le plus simple) si tous ces UI de fantaisie de ne parviennent pas à installer ou à exécuter:
ignorer complètement cProfile
et le remplacer par pyinstrument
, qui va recueillir et afficher l'arborescence des appels juste après l'exécution.
Installer:
$ pip install pyinstrument
Profil et résultats Affichage:
$ python -m pyinstrument ./prog.py
Fonctionne avec python2 et 3.