Comment pouvez-vous profiler un script Python?

1283

Project Euler et d'autres concours de codage ont souvent un temps maximum pour s'exécuter ou les gens se vantent de la rapidité d'exécution de leur solution particulière. Avec Python, les approches sont parfois quelque peu compliquées - c'est-à-dire, ajouter du code temporel à __main__.

Quelle est la bonne façon de déterminer le temps d'exécution d'un programme Python?

Chris Lawlor
la source
113
Les programmes du projet euler ne devraient pas avoir besoin de profilage. Soit vous avez un algorithme qui fonctionne en moins d'une minute, soit vous avez tout à fait le mauvais algorithme. "Tuning" est rarement approprié. Vous devez généralement adopter une nouvelle approche.
S.Lott
105
S.Lott: Le profilage est souvent un moyen utile de déterminer quels sous-programmes sont lents. Les sous-programmes qui prennent beaucoup de temps sont d'excellents candidats pour l'amélioration algorithmique.
stalepretzel

Réponses:

1370

Python inclut un profileur appelé cProfile . Il donne non seulement le temps d'exécution total, mais également le temps de chaque fonction séparément, et vous indique combien de fois chaque fonction a été appelée, ce qui permet de déterminer facilement où vous devez effectuer des optimisations.

Vous pouvez l'appeler depuis votre code ou depuis l'interpréteur, 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, 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: lien mis à jour vers une bonne ressource vidéo de PyCon 2013 intitulée Python Profiling
Also via YouTube .

Chris Lawlor
la source
251
Il est également utile de trier les résultats, ce qui peut être fait par le commutateur -s, par exemple: '-s time'. Vous pouvez utiliser les options de tri cumulatif / nom / heure / fichier.
Jiri
19
Il convient également de noter que vous pouvez utiliser le module cProfile d'ipython à l'aide de la fonction magique% prun (profil run). Importez d'abord votre module, puis appelez la fonction principale avec% prun: import euler048; % prun euler048.main ()
RussellStewart
53
Pour visualiser les vidages cProfile (créés par python -m cProfile -o <out.profile> <script>), RunSnakeRun , appelé comme runsnake <out.profile>est inestimable.
Lily Chung
13
@NeilG même pour python 3, cprofileest toujours recommandé plus profile.
trichoplax
17
Pour visualiser les vidages cProfile, RunSnakeRun n'a pas été mis à jour depuis 2011 et ne prend pas en charge python3. Vous devriez utiliser snakeviz à la place
Giacomo Tecya Pigani
424

Il y a quelque temps, j'ai fait pycallgraphqui génère une visualisation à partir de votre code Python. Edit: j'ai mis à jour l'exemple pour travailler avec 3.3, la dernière version de cette écriture.

Après avoir pip install pycallgraphinstallé et installé 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 pycallgraph.pngfichier similaire à l'image ci-dessous:

entrez la description de l'image ici

gak
la source
43
Êtes-vous à colorier en fonction du nombre d'appels? Si c'est le cas, vous devez colorer en fonction du temps car la fonction avec le plus d'appels n'est pas toujours celle qui prend le plus de temps.
rouge
21
@red Vous pouvez personnaliser les couleurs comme vous le souhaitez, et même indépendamment pour chaque mesure. Par exemple, rouge pour les appels, bleu pour l'heure, vert pour l'utilisation de la mémoire.
gak
2
obtenir cette erreurTraceback (most recent call last): /pycallgraph.py", line 90, in generate output.done() File "/net_downloaded/pycallgraph-develop/pycallgraph/output/graphviz.py", line 94, in done source = self.generate() File "/net_downloaded/pycallgraph-develop/pycallgraph/output/graphviz.py", line 143, in generate indent_join.join(self.generate_attributes()), File "/net_downloaded/pycallgraph-develop/pycallgraph/output/graphviz.py", line 169, in generate_attributes section, self.attrs_from_dict(attrs), ValueError: zero length field name in format
Ciasto piekarz
3
J'ai mis à jour cela pour mentionner que vous devez installer GraphViz pour que les choses fonctionnent comme décrit. Sur Ubuntu, c'est juste sudo apt-get install graphviz.
mlissner
2
Cela nécessite un peu de travail pour installer voici 3 étapes pour vous aider. 1. Installez via pip, 2. Installez GraphViz via exe 3. Configurez les variables de chemin vers le répertoire GraphViz 4. Découvrez comment corriger toutes les autres erreurs. 5. Déterminez où il enregistre le fichier png?
marsh
199

Il convient de souligner que l'utilisation du profileur ne fonctionne (par défaut) que sur le thread principal, et vous n'obtiendrez aucune information des autres threads si vous les utilisez. Cela peut être un peu un problème, car cela n'est pas mentionné dans la documentation du profileur .

Si vous souhaitez également profiler les discussions, vous voudrez regarder la threading.setprofile()fonction dans les documents.

Vous pouvez également créer votre propre threading.Threadsous-classe 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 ProfiledThreadclasse au lieu de la classe standard. Cela pourrait vous donner plus de flexibilité, 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.

Joe Shaw
la source
1
Je ne vois aucune référence à runcall dans la documentation non plus. En jetant un œil à cProfile.py, je ne sais pas pourquoi vous utilisez la fonction threading.Thread.run ni self comme argument. Je m'attendais à voir une référence à la méthode d'exécution d' un autre thread ici.
PypeBros
Ce n'est pas dans la documentation, mais c'est dans le module. Voir hg.python.org/cpython/file/6bf07db23445/Lib/cProfile.py#l140 . Cela vous permet de profiler un appel de fonction spécifique, et dans notre cas, nous voulons profiler la targetfonction du thread , qui est ce que l' threading.Thread.run()appel exécute. Mais comme je l'ai dit dans la réponse, cela ne vaut probablement pas la peine de sous-classer Thread, car aucun code tiers ne l'utilisera, et de l'utiliser à la place threading.setprofile().
Joe Shaw
9
l'encapsulation du code avec profiler.enable () et profiler.disable () semble également très bien fonctionner. C'est essentiellement ce que fait runcall et il n'applique aucun nombre d'arguments ou de choses similaires.
PypeBros
1
J'ai combiné mon propre stackoverflow.com/questions/10748118/… avec ddaa.net/blog/python/lsprof-calltree et ça fonctionne un peu
;!
1
Joe, savez-vous comment le profileur joue avec asyncio dans Python 3.4?
Nick Chammas
149

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

tout comme les documents python: 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 à déposer:

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

PS> Si vous utilisez Ubuntu, assurez-vous d'installer le profil python

apt-get install python-profiler 

Si vous sortez dans un fichier, vous pouvez obtenir de belles visualisations en utilisant les outils suivants

PyCallGraph: un outil pour créer des images d'appels graphiques d'
installation:

 pip install pycallgraph

courir:

 pycallgraph mine.py args

vue:

 gimp pycallgraph.png

Vous pouvez utiliser ce que vous voulez pour afficher le fichier png, j'ai utilisé gimp
Malheureusement, je reçois souvent

dot: le graphique est trop grand pour les bitmaps du rendu du Caire. Mise à l'échelle par 0,257079 pour s'adapter

ce qui rend mes images particulièrement petites. Je crée donc 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):

pip install graphviz

Graphisme alternatif utilisant gprof2dot via @maxy / @quodlibetor:

pip install gprof2dot
python -m cProfile -o profile.pstats mine.py
gprof2dot -f pstats profile.pstats | dot -Tsvg -o mine.svg
brent.payne
la source
12
gprof2dot peut aussi faire ces graphiques. Je pense que la sortie est un peu plus agréable ( exemple ).
maxy
2
graphviz est également requis si vous utilisez OSX
Vaibhav Mishra
134

Le commentaire de @ Maxy sur cette réponse m'a suffisamment aidé 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 je suis devenu joli 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 dot (la même chose que pycallgraph utilise) donc la sortie est similaire. J'ai l'impression que gprof2dot perd cependant moins d'informations:

Exemple de sortie de gprof2dot

quodlibetor
la source
1
Bonne approche, fonctionne très bien car vous pouvez afficher SVG dans Chrome, etc. et le faire évoluer. La troisième ligne a une faute de frappe, devrait être: ln -s pwd/gprof2dot/gprof2dot.py $ HOME / bin (ou utilisez ln -s $ PWD / gprof2dot / gprof2dot.py ~ / bin dans la plupart des shells - l'accent grave est pris comme formatage en premier version).
RichVel
2
Ah, bon point. Je me lntrompe presque à chaque fois.
quodlibetor
7
l'astuce est de se rappeler que ln et cp ont le même ordre d'arguments - pensez-y comme «copier file1 vers file2 ou dir2, mais faire un lien»
RichVel
Cela a du sens, je pense que l'utilisation de "TARGET" dans la page de manuel me lance.
quodlibetor
S'il vous plaît, comment avez-vous obtenu les coins arrondis? Je pense que cela améliore la lisibilité. Je reçois juste des coins tranchants laids, ce qui n'est pas cool en présence de nombreux bords autour des boîtes.
Hibou57
78

J'ai rencontré un outil pratique appelé SnakeViz lors de la recherche 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 de l'utiliser est de générer un fichier de statistiques avec %prunpuis de faire une analyse dans SnakeViz.

La principale technique utilisée est le graphique Sunburst, comme illustré ci-dessous, dans lequel la hiérarchie des appels de fonction est organisée sous forme de couches d'arcs et d'informations temporelles codées dans leurs largeurs angulaires.

La meilleure chose est que vous pouvez 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.

entrez la description de l'image ici

zaxliu
la source
1
La réponse de CodeCabbie comprend les (courtes) instructions d'installation et montre comment (facilement) utiliser SnakeViz.
Oren Milman
Ici, j'ai lu un bon guide à mon humble avis sur la façon d'utiliser le profilage pour Python sur le carnet de notes jupyter
Alexei Martianov
73

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

1. pip install snakeviz

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

3. snakeviz temp.dat

Dessine un graphique à secteurs dans un navigateur. La plus grande pièce est la fonction du problème. Très simple.

CodeCabbie
la source
1
Ce fut très utile. Je vous remercie.
jippyjoe4
55

Je pense que cProfilec'est génial pour le profilage, alors que kcachegrindc'est génial pour visualiser les résultats. L' pyprof2calltreeentre-deux gère la conversion du 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:

Capture d'écran du résultat

Federico
la source
9
Les utilisateurs Mac installent brew install qcachegrindet remplacent chacun kcachegrind avec qcachegrind dans la description pour un profilage réussi.
Kevin Katzke
Je devais le faire pour le faire fonctionner:export QT_X11_NO_MITSHM=1
Yonatan Simson
41

Il convient également de mentionner le visualiseur de vidage GUI cProfile RunSnakeRun . Il vous permet de trier et de sélectionner, 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 é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 que cette partie appelle.

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

Je voudrais également souligner que le PO a dit «profilage», mais il semble qu'il voulait dire «timing». Gardez à l'esprit que les programmes s'exécutent plus lentement lorsqu'ils sont profilés.

entrez la description de l'image ici

Pete
la source
34

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

entrez la description de l'image ici

Installer avec

pip install tuna

Créer un profil d'exécution

python3 -m cProfile -o program.prof yourfile.py

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

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

Ensuite, exécutez simplement le thon sur le fichier

tuna program.prof
Nico Schlömer
la source
33

Un module de profilage sympa est le line_profiler (appelé en utilisant le script kernprof.py). Il peut être téléchargé ici .

Ma compréhension est que cProfile ne donne que des informations sur le temps total passé dans chaque fonction. Les lignes de code individuelles ne sont donc pas chronométrées. C'est un problème dans le calcul 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.

Ian Langmore
la source
33

pprofile

line_profiler(déjà présenté ici) également inspiré pprofile, qui est décrit comme:

Profileur pur-python à granularité de ligne, déterministe et statistique

Il fournit une granularité de ligne car 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 caractéristiques du programme Python telles que le temps d'exécution et l'utilisation de la mémoire.

carte de chaleur

BenC
la source
14

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 pouvais utiliser dans mon IDE (eclipse-PyDev) sans toucher à la ligne de commande ni 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 les documents ou autres réponses pour plus d'informations.

David Mašek
la source
par exemple, le profil imprime {map} ou {xxx}. comment savoir si la méthode {xxx} est appelée à partir de quel fichier? mon profil imprime {la méthode 'compresser' des objets 'zlib.Compress'} prend la plupart du temps, mais je n'utilise pas de zlib, donc je suppose qu'une fonction call numpy peut l'utiliser. Comment savoir quel est exactement le fichier et la ligne prend beaucoup de temps?
machen
12

Suite à la réponse de Joe Shaw à propos du code multi-thread pour ne pas fonctionner comme prévu, je me suis dit que la runcallméthode dans cProfile fait simplement self.enable()et self.disable()appelle autour de l'appel de fonction profilée, donc vous pouvez simplement le faire vous-même et avoir le code que vous voulez entre interférence minimale avec le code existant.

PypeBros
la source
3
Excellent conseil! Un rapide coup d'œil au cprofile.pycode source de révèle que c'est exactement ce qui runcall()fait. Plus précis, après avoir créé une instance de profil avec prof = cprofile.Profile(), appelez immédiatement prof.disable(), puis ajoutez prof.enable()et prof.disable()appelez autour de la section de code que vous souhaitez profiler.
martineau
Ceci est très utile, mais il semble que le code qui se trouve entre activer et désactiver ne soit pas profilé - uniquement les fonctions qu'il appelle. Ai-je ce droit? Je devrais encapsuler ce code dans un appel de fonction pour qu'il compte dans l'un des nombres de print_stats ().
Bob Stein
10

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

Walter
la source
1
Merci pour ce petit bijou. FYI: Cela peut être utilisé comme un module autonome avec n'importe quel code, la base de code Virtaal n'est pas requise. Enregistrez simplement le fichier dans profiling.py et importez profile_func (). Utilisez @profile_func () comme décorateur pour toute fonction dont vous avez besoin pour profiler et alto. :)
Amjith
9

cProfile est idéal pour le profilage rapide, mais la plupart du temps, il se terminait pour moi avec des erreurs. La fonction runctx résout ce problème en initialisant correctement l'environnement et les variables, j'espère que cela peut être utile pour quelqu'un:

import cProfile
cProfile.runctx('foo()', None, locals())
Datageek
la source
7

Si vous voulez créer un profileur cumulatif, c'est-à-dire exécuter la fonction plusieurs fois de suite et regarder la somme des résultats.

vous pouvez utiliser ce cumulative_profilerdécorateur:

c'est spécifique à python> = 3.6, mais vous pouvez le supprimer nonlocalpour qu'il fonctionne sur les anciennes versions.

import cProfile, pstats

class _ProfileFunc:
    def __init__(self, func, sort_stats_by):
        self.func =  func
        self.profile_runs = []
        self.sort_stats_by = sort_stats_by

    def __call__(self, *args, **kwargs):
        pr = cProfile.Profile()
        pr.enable()  # this is the profiling section
        retval = self.func(*args, **kwargs)
        pr.disable()

        self.profile_runs.append(pr)
        ps = pstats.Stats(*self.profile_runs).sort_stats(self.sort_stats_by)
        return retval, ps

def cumulative_profiler(amount_of_times, sort_stats_by='time'):
    def real_decorator(function):
        def wrapper(*args, **kwargs):
            nonlocal function, amount_of_times, sort_stats_by  # for python 2.x remove this row

            profiled_func = _ProfileFunc(function, sort_stats_by)
            for i in range(amount_of_times):
                retval, ps = profiled_func(*args, **kwargs)
            ps.print_stats()
            return retval  # returns the results of the function
        return wrapper

    if callable(amount_of_times):  # incase you don't want to specify the amount of times
        func = amount_of_times  # amount_of_times is the function in here
        amount_of_times = 5  # the default amount
        return real_decorator(func)
    return real_decorator

Exemple

profiler la fonction baz

import time

@cumulative_profiler
def baz():
    time.sleep(1)
    time.sleep(2)
    return 1

baz()

baz a couru 5 fois et imprimé ceci:

         20 function calls in 15.003 seconds

   Ordered by: internal time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
       10   15.003    1.500   15.003    1.500 {built-in method time.sleep}
        5    0.000    0.000   15.003    3.001 <ipython-input-9-c89afe010372>:3(baz)
        5    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}

spécifiant le nombre de fois

@cumulative_profiler(3)
def baz():
    ...
moshevi
la source
7

La solution de terminal uniquement (et la plus simple), au cas où toutes ces interfaces utilisateur fantaisistes ne parviennent pas à installer ou à exécuter:
ignorer cProfilecomplètement et le remplacer par pyinstrument, qui collectera et affichera l'arborescence des appels juste après l'exécution.

Installer:

$ pip install pyinstrument

Profil et résultat d'affichage:

$ python -m pyinstrument ./prog.py

Fonctionne avec python2 et 3.

[EDIT] La documentation de l'API, pour le profilage d'une partie seulement du code, peut être trouvée ici .

François
la source
6

Ma façon est d'utiliser yappi ( https://github.com/sumerc/yappi ). Il est particulièrement utile combiné 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 manière:

@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 startProfilerméthode RPC et vider les informations de profilage dans un fichier journal en appelant printProfiler(ou modifier la méthode rpc pour la 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 

Il peut ne pas être très utile pour les scripts courts, mais aide à optimiser les processus de type serveur, d'autant plus que la printProfilerméthode peut être appelée plusieurs fois dans le temps pour profiler et comparer, par exemple, différents scénarios d'utilisation de programme.

Dans les versions plus récentes de yappi, le code suivant fonctionnera:

@staticmethod
def printProfile():
    yappi.get_func_stats().print_all()
M. Girgitt
la source
Ne devrait-il pas être nommé le Stupendous Yappi?
Therealstubot
Malheureusement, le code ci-dessus ne fonctionne qu'avec la version 0.62 qui n'est pas disponible sur pypy. Le module doit être compilé à partir de 0,62 sources disponibles ici: github.com/nirs/yappi/releases ou utiliser la construction que j'ai faite pour les fenêtres dans le repo bifurqué à cet effet github.com/Girgitt/yappi/releases
M. Girgitt
la compatibilité avec la version 1.0 peut être facilement fournie - au moins pour la sortie d'impression - en modifiant la fonction printProfiler: def printProfiler(): if not yappi_available: return stats = yappi.get_func_stats() stats.print_all(columns={0:("name",90), 1:("ncall", 5), 2:("tsub", 8), 3:("ttot", 8), 4:("tavg",8)}) (OK après avoir essayé plusieurs fois d'insérer un bloc de code dans le commentaire que j'ai abandonné. c'est incroyablement difficile pour un site de questions / réponses orienté programmation. )
M. Girgitt
4

PyVmMonitor est un nouvel outil pour gérer le profilage en Python: http://www.pyvmmonitor.com/

Il possède des fonctionnalités uniques telles que

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

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

Fabio Zadrozny
la source
4

gprof2dot_magic

Fonction magique pour gprof2dotprofiler toute instruction Python sous forme de graphique DOT dans JupyterLab ou Jupyter Notebook.

entrez la description de l'image ici

Dépôt GitHub: https://github.com/mattijn/gprof2dot_magic

installation

Assurez-vous que vous disposez du package Python gprof2dot_magic.

pip install gprof2dot_magic

Ses dépendances gprof2dotet graphvizseront également installées

usage

Pour activer la fonction magique, chargez d'abord le gprof2dot_magicmodule

%load_ext gprof2dot_magic

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

%gprof2dot print('hello world')

entrez la description de l'image ici

Mattijn
la source
3

Vous avez toujours voulu savoir ce que fait le script python? Entrez dans la coque d'inspection. Inspect Shell vous permet d'imprimer / modifier des 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-bracelet).

Colonel Panic
la source
3

Cela dépendra de ce que vous voulez voir du profilage. Des métriques de temps simples peuvent être fournies 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 combien de temps est consacré aux fonctions, vous pouvez utiliser le cProfile intégré en python.

En entrant dans des mesures plus détaillées telles que les performances, le temps n'est pas la seule mesure. Vous pouvez vous soucier de la mémoire, des threads, etc.
Options de profilage:
1. line_profiler est un autre profileur utilisé couramment pour trouver les métriques de synchronisation ligne par ligne.
2. memory_profiler est un outil pour profiler l'utilisation de la mémoire.
3. tas (du projet Guppy) Profil de la façon dont les objets du tas sont utilisés.

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

VishalMishra
la source
2

Avec un profileur statistique comme Austin , aucune instrumentation n'est requise, ce qui signifie que vous pouvez obtenir des données de profilage d'une application Python simplement avec

austin python3 my_script.py

La sortie brute n'est pas très utile, mais vous pouvez la diriger vers flamegraph.pl pour obtenir une représentation graphique de la flamme de ces données qui vous donne une ventilation de l'endroit où le temps (mesuré en microsecondes en temps réel) est passé.

austin python3 my_script.py | flamegraph.pl > my_script_profile.svg
Phoenix87
la source
2

Pour obtenir des statistiques de profil rapides sur un ordinateur portable IPython. On peut incorporer line_profiler et memory_profiler directement dans leurs cahiers.

Un autre package utile est Pympler . Il s'agit d'un puissant package de profilage capable de suivre les classes, les objets, les fonctions, les fuites de mémoire, etc. Exemples ci-dessous, documents joints.

Tu piges!

!pip install line_profiler
!pip install memory_profiler
!pip install pympler

Charge le!

%load_ext line_profiler
%load_ext memory_profiler

Utilise le!


%temps

%time print('Outputs CPU time,Wall Clock time') 
#CPU times: user 2 µs, sys: 0 ns, total: 2 µs Wall time: 5.96 µs

Donne:

  • Temps CPU: temps d'exécution au niveau CPU
  • sys times: temps d'exécution au niveau du système
  • total: temps CPU + temps système
  • Heure du mur: Horloge murale

% timeit

%timeit -r 7 -n 1000 print('Outputs execution time of the snippet') 
#1000 loops, best of 7: 7.46 ns per loop
  • Donne le meilleur temps sur un nombre donné de courses (r) en boucle (n) fois.
  • Affiche les détails de la mise en cache du système:
    • Lorsque des extraits de code sont exécutés plusieurs fois, le système met en cache quelques opérations et ne les réexécute pas, ce qui peut nuire à la précision des rapports de profil.

% élagage

%prun -s cumulative 'Code to profile' 

Donne:

  • nombre d'appels de fonction (ncalls)
  • a des entrées par appel de fonction (distinct)
  • temps pris par appel (percall)
  • temps écoulé jusqu'à cet appel de fonction (cumtime)
  • nom du func / module appelé etc ...

Profil cumulatif


% memit

%memit 'Code to profile'
#peak memory: 199.45 MiB, increment: 0.00 MiB

Donne:

  • Utilisation de la mémoire

% lprun

#Example function
def fun():
  for i in range(10):
    print(i)

#Usage: %lprun <name_of_the_function> function
%lprun -f fun fun()

Donne:

  • Statistiques en ligne

LineProfile


sys.getsizeof

sys.getsizeof('code to profile')
# 64 bytes

Renvoie la taille d'un objet en octets.


asizeof () de pympler

from pympler import asizeof
obj = [1,2,("hey","ha"),3]
print(asizeof.asizeof(obj,stats=4))

pympler.asizeof peut être utilisé pour étudier la quantité de mémoire consommée par certains objets Python. Contrairement à sys.getsizeof, asizeof taille les objets récursivement

pympler.asizeof


tracker de pympler

from pympler import tracker
tr = tracker.SummaryTracker()
def fun():
  li = [1,2,3]
  di = {"ha":"haha","duh":"Umm"}
fun()
tr.print_diff()

Suit la durée de vie d'une fonction.

sortie tracker

Le package Pympler se compose d'un grand nombre de fonctions hautement utiles pour profiler le code. Tout cela ne peut pas être couvert ici. Voir la documentation jointe pour les implémentations de profil détaillé.

Pympler doc

Aditya Patnaik
la source
1

Il existe également un profileur statistique appelé statprof. Il s'agit d'un profileur d'échantillonnage, il ajoute donc une surcharge minimale à votre code et donne des temporisations basées sur la ligne (et pas seulement sur la fonction). Il est plus adapté aux applications logicielles en temps réel comme les jeux, mais peut être moins précis que cProfile.

La version dans pypi est un peu ancienne, vous pouvez donc l'installer avec pipen spécifiant 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

z0r
la source
1

Je viens de développer mon propre profileur inspiré de pypref_time:

https://github.com/modaresimr/auto_profiler

En ajoutant un décorateur, il affichera un arbre de fonctions chronophages

@Profiler(depth=4, on_disable=show)

Install by: pip install auto_profiler

Exemple

import time # line number 1
import random

from auto_profiler import Profiler, Tree

def f1():
    mysleep(.6+random.random())

def mysleep(t):
    time.sleep(t)

def fact(i):
    f1()
    if(i==1):
        return 1
    return i*fact(i-1)


def show(p):
    print('Time   [Hits * PerHit] Function name [Called from] [Function Location]\n'+\
          '-----------------------------------------------------------------------')
    print(Tree(p.root, threshold=0.5))

@Profiler(depth=4, on_disable=show)
def main():
    for i in range(5):
        f1()

    fact(3)


if __name__ == '__main__':
    main()

Exemple de sortie


Time   [Hits * PerHit] Function name [Called from] [function location]
-----------------------------------------------------------------------
8.974s [1 * 8.974]  main  [auto-profiler/profiler.py:267]  [/test/t2.py:30]
├── 5.954s [5 * 1.191]  f1  [/test/t2.py:34]  [/test/t2.py:14]
   └── 5.954s [5 * 1.191]  mysleep  [/test/t2.py:15]  [/test/t2.py:17]
       └── 5.954s [5 * 1.191]  <time.sleep>
|
|
|   # The rest is for the example recursive function call fact
└── 3.020s [1 * 3.020]  fact  [/test/t2.py:36]  [/test/t2.py:20]
    ├── 0.849s [1 * 0.849]  f1  [/test/t2.py:21]  [/test/t2.py:14]
       └── 0.849s [1 * 0.849]  mysleep  [/test/t2.py:15]  [/test/t2.py:17]
           └── 0.849s [1 * 0.849]  <time.sleep>
    └── 2.171s [1 * 2.171]  fact  [/test/t2.py:24]  [/test/t2.py:20]
        ├── 1.552s [1 * 1.552]  f1  [/test/t2.py:21]  [/test/t2.py:14]
           └── 1.552s [1 * 1.552]  mysleep  [/test/t2.py:15]  [/test/t2.py:17]
        └── 0.619s [1 * 0.619]  fact  [/test/t2.py:24]  [/test/t2.py:20]
            └── 0.619s [1 * 0.619]  f1  [/test/t2.py:21]  [/test/t2.py:14]
Ali
la source
0

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

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

Ensuite, je peux ouvrir le rapport avec n'importe quel logiciel compatible avec callgrind, comme qcachegrind

Vincent Fenet
la source