J'aimerais décrire une méthode d'une fonction en Python à l'aide de cProfile. J'ai essayé ce qui suit:
import cProfile as profile
# Inside the class method...
profile.run("self.myMethod()", "output_file")
Mais ça ne marche pas. Comment puis-je appeler un self.method avec "run"?
Utilisez le décorateur de hooks profil
EDIT: Désolé, je n'ai pas réalisé que l'appel de profil était in une méthode de classe.
run
essaie juste de exec
la chaîne que vous lui transmettez. Si self
n'est lié à rien dans la portée du profileur que vous utilisez, vous ne pouvez pas l'utiliser dans run
! Utilisez la méthode runctx
pour transmettre les variables locales et globales dans l'étendue de l'appel au profileur:
>>> import time
>>> import cProfile as profile
>>> class Foo(object):
... def bar(self):
... profile.runctx('self.baz()', globals(), locals())
...
... def baz(self):
... time.sleep(1)
... print 'slept'
... time.sleep(2)
...
>>> foo = Foo()
>>> foo.bar()
slept
5 function calls in 2.999 CPU seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 2.999 2.999 <stdin>:5(baz)
1 0.000 0.000 2.999 2.999 <string>:1(<module>)
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
2 2.999 1.499 2.999 1.499 {time.sleep}
Remarquez la dernière ligne: time.sleep
est ce qui prend le temps.
Je ne recommanderais pas de profiler une seule routine, car cela implique de savoir à l'avance qu'il y a un problème.
Un aspect fondamental des problèmes de performance est qu'ils sont sournois. Ils ne sont pas là où vous pensez, parce que s'ils l'avaient été, vous les auriez déjà résolus.
Il est préférable d'exécuter le programme dans son ensemble avec une charge de travail réaliste et de laisser la technique de profilage vous indiquer les problèmes.
Voici un exemple où le profilage trouve le problème, et ce n’est pas ce à quoi on s’attendait.
Si votre fonction sous profil renvoie des valeurs, vous devez modifier légèrement l'excellente réponse de @katrielalex:
... profile.runctx('val = self.baz()', globals(), locals())
... print locals()['val']
Si vous voulez créer un profileur cumulatif, Signifie exécuter la fonction plusieurs fois de suite et regarder la somme des résultats.
vous pouvez utiliser ce décorateur cumulative_profiler
:
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
profilage de la fonction baz
import time
@cumulative_profiler
def baz():
time.sleep(1)
time.sleep(2)
return 1
baz()
baz
a été exécuté 5 fois et a 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}
en précisant le nombre de fois
@cumulative_profiler(3)
def baz():
...