web-dev-qa-db-fra.com

profiler une méthode d'une classe en Python en utilisant cProfile?

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"?

30
user248237dfsf

Utilisez le décorateur de hooks profil

http://pypi.python.org/pypi/profilehooks

23
Falmarri

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.

43
Katriel

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.

2
Mike Dunlavey

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']
1
sqqqrly

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():
    ...
0
moshevi