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?
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 .
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:
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.
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
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:
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.
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:
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.
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.
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.
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
.
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.
J'ai récemment créé thon pour visualiser Python runtime et importer des profils; 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 (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
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.
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.
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.
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.
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())
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.
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
Remarque: c'est commercial, mais gratuit pour l'open source.
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).
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).
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.
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
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
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.
gprof2dot_magic
Fonction magique permettant à gprof2dot
de profiler une instruction Python sous forme de graphique DOT dans JupyterLab ou Jupyter Notebook.
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')