2009-10-18 15 views
15

Sto cercando di profilare un metodo di istanza, così ho fatto qualcosa di simile:Valore restituito durante l'utilizzo 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() 

Ma ora problemi sorgono quando voglio "metodo" per restituire un valore che viene calcolato da "method_actual". Non voglio davvero chiamare "method_actual" due volte.

C'è un altro modo, qualcosa che può essere thread sicuro? (Nella mia applicazione, i dati vengono salvati Cprofile file di dati di nome da uno dei args, in modo che non vengono rovinati ed io li possono combinare in seguito.)

risposta

26

ho scoperto che si può fare questo:

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

Lo svantaggio è che non è documentato.

+2

Brillante! Questo sembra perfetto - ma cosa è "datafn"? –

+0

@JonathanHartley - Il nome file per il file di dati IIRC. – detly

+0

Ah, grazie. Pensavo che "fn" significasse funzione, non nomefile. –

6

Ero alle prese con lo stesso problema e ho utilizzato una funzione wrapper per ottenere valori di ritorno diretto. Invece di

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

Creo una funzione avvolgitore

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

e il profilo chiamata alla funzione involucro

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

estraendo il risultato del calcolo di foo dal param out (b) dopo .

+0

Funziona come un fascino. –

18

Un'opzione per qualsiasi codice arbitrario:

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() 

Tratto da https://docs.python.org/2/library/profile.html#profile.Profile

+0

Si potrebbe anche creare un piccolo gestore di contesto per quello usando 'decoratore' contextmanager' di contextlib's. – detly

+0

Viene visualizzato "Ordine di elenco casuale" - come posso specificare l'ordine di inserzione? –

+1

ps.sort_stats ('cumulativo') – marsh

1

Penso @detly il .runcall() è fondamentalmente la risposta migliore, ma per completezza, ho solo voluto prendere @ThomasH 's rispondere ad essere funzione indipendente:

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

ho creato un decoratore:

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 

decorare la vostra funzione o metodo con esso:

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

Ora che la funzione sarà profilata.

In alternativa, se vuoi i dati grezzi, non lavorati profilo (ad esempio perché si desidera eseguire l'eccellente RunSnakeRun visualizzatore grafico su di esso), allora:

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 

Questo è un minore miglioramento su alcuni dei le altre risposte su questa pagina.