2009-10-18 8 views
15

Estoy tratando de perfilar un método de instancia, por lo que he hecho algo como:Valor de retorno durante el uso de cprofile

import cProfile 

class Test(): 

    def __init__(self): 
     pass 

    def method(self): 
     cProfile.runctx("self.method_actual()", globals(), locals()) 

    def method_actual(self): 
     print "Run" 

if __name__ == "__main__": 
    Test().method() 

Pero ahora surgen problemas cuando quiero "método" para devolver un valor que se calcula por "method_actual". Realmente no quiero llamar "method_actual" dos veces.

¿Hay alguna otra manera, algo que pueda ser seguro para subprocesos? (En mi aplicación, los datos cprofile se guardan en archivos de datos con nombre de uno de los argumentos, por lo que no reciben una paliza y me pueden combinarlos más tarde.)

Respuesta

26

descubrí que se puede hacer esto:

prof = cProfile.Profile() 
retval = prof.runcall(self.method_actual, *args, **kwargs) 
prof.dump_stats(datafn) 

Lo malo es que no está documentado.

+2

Brilliant! Esto se ve perfecto, pero ¿qué es 'datafn'? –

+0

@JonathanHartley - El nombre de archivo para el archivo de datos IIRC. – detly

+0

Ah, gracias. Pensé que 'fn' significaba función, no nombre de archivo. –

6

Estaba luchando con el mismo problema y usé una función de envoltura para superar los valores de devolución directa. En lugar de

cP.runctx("a=foo()", globals(), locales()) 

creo una función de contenedor

def wrapper(b): 
    b.append(foo()) 

y el perfil de la llamada a la función de contenedor

b = [] 
cP.runctx("wrapper(b)", globals(), locals()) 
a = b[0] 

extraer el resultado del cálculo del foo del parámetro a cabo (b) posteriormente, .

+0

Funciona como un encanto. –

18

una opción para cualquier código de su elección:

import cProfile, pstats, sys 
pr = cProfile.Profile() 
pr.enable() 

my_return_val = my_func(my_arg) 

pr.disable() 
ps = pstats.Stats(pr, stream=sys.stdout) 
ps.print_stats() 

Tomado de https://docs.python.org/2/library/profile.html#profile.Profile

+0

Incluso podrías hacer un pequeño administrador de contexto para eso usando el decorador 'contextlib's' contextmanager'. – detly

+0

Me aparece 'Se ha usado una orden de publicación aleatoria '- ¿cómo puedo especificar el orden de publicación? –

+1

p.sort_stats ('acumulativo') – marsh

1

Creo @detly la .runcall() es básicamente la mejor respuesta, pero está completo, sólo quería tomar @ThomasH 's contestar a ser función independiente:

def wrapper(b, f, *myargs, **mykwargs): 
    try: 
     b.append(f(*myargs, **mykwargs)) 
    except TypeError: 
     print 'bad args passed to func.' 

# Example run 
def func(a, n): 
    return n*a + 1 

b = [] 
cProfile.runctx("wrapper(b, func, 3, n=1)", globals(), locals()) 
a = b[0] 
print 'a, ', a 
1

creé un decorador:

import cProfile 
import functools 
import pstats 

def profile(func): 

    @functools.wraps(func) 
    def inner(*args, **kwargs): 
     profiler = cProfile.Profile() 
     profiler.enable() 
     try: 
      retval = func(*args, **kwargs) 
     finally: 
      profiler.disable() 
      with open('profile.out', 'w') as profile_file: 
       stats = pstats.Stats(profiler, stream=profile_file) 
       stats.print_stats() 
     return retval 

    return inner 

Adorne su función o un método con él:

@profile 
def somefunc(...): 
    ... 

Ahora que la función se perfila.

Alternativamente, si desea los datos en bruto, sin procesar perfil (por ejemplo, debido a que desea ejecutar la excelente RunSnakeRun visor gráfico en él), entonces:

import cProfile 
import functools 
import pstats 

def profile(func): 

    @functools.wraps(func) 
    def inner(*args, **kwargs): 
     profiler = cProfile.Profile() 
     profiler.enable() 
     try: 
      retval = func(*args, **kwargs) 
     finally: 
      profiler.disable() 
      profiler.dump_stats('profile.out') 
     return retval 

    return inner 

Ésta es una pequeña mejora en varios de las otras respuestas en esta página.

Cuestiones relacionadas