2010-08-25 10 views
10

Sto adattando un'applicazione che fa un uso massiccio di generatori per produrre i suoi risultati per fornire un'interfaccia web web.py.Generatori di profili Python

Finora, ho potuto racchiudere la chiamata al ciclo for e le istruzioni di produzione in uscita in una funzione e chiamarla utilizzando cProfile.run() o runctx(). Concettualmente:

def output(): 
    for value in generator(): 
     print(value) 

cProfile.run('output()') 

In web.py, devo avvolgerla seguente modo, poiché voglio produrre immediatamente uscita dal calcolo potenzialmente esecuzione prolungata in ogni iterazione passaggio utilizzando yield:

class index: 
    def GET(self): 
     for value in generator(): 
      yield make_pretty_html(value) 

C'è un modo per profilare tutte le chiamate al generatore come nel primo esempio quando è usato come nel secondo?

+0

Vuoi semplicemente misurare l'intera chiamata di funzione anziché una sola iterazione? Come in 'cProfile.run ('list (index(). GET())')'? –

+0

In sostanza, questo è ciò che il ciclo for realizza. Il problema qui è che non ho il controllo sulle chiamate a 'GET()', è gestito da 'web.py'. Inoltre, non penso che l'output verrebbe prodotto in quel modo (usando il valore restituito). –

risposta

5

ho finalmente trovato una soluzione. Restituire il valore della profilazione tramite here.

import cProfile 
import pstats 
import glob 
import math 

def gen(): 
    for i in range(1, 10): 
     yield math.factorial(i) 

class index(object): 
    def GET(self): 
     p = cProfile.Profile() 

     it = gen() 
     while True: 
      try: 
       nxt = p.runcall(next, it) 
      except StopIteration: 
       break 
      print nxt 

     p.print_stats() 

index().GET() 

Ho anche potuto unire più tali risultati profiling (una volta che iniziare a dare i nomi di file unici) tramite documentation e memorizzare/analizzarli combinato.

0

Puoi semplicemente usare time.time() per profilare le parti che ti interessano? Ottieni l'ora corrente e sottrai dall'ultima volta che hai effettuato una misurazione.

+0

Ho già ottenuto il tempo totale, ma "L'elaborazione ha richiesto 5.382 secondi" non è abbastanza specifica per trovare colli di bottiglia nelle prestazioni. Uso internamente una catena di generatori piuttosto ampia e ramificata e intesa a memorizzare input dell'utente e prestazioni risultanti per analisi successive. Ho diverse funzioni che richiedono una media di 0.000 secondi per ogni chiamata, ma potrebbero essere chiamate decine di migliaia di volte. –

+0

In tal caso è possibile avere un contatore intero per ogni esecuzione di tali funzioni e eseguire solo una misurazione ogni 1000 esecuzioni? Potresti invece misurare blocchi di codice in questo modo e restringere i colli di bottiglia. Mi rendo conto che potrebbe essere un po 'noioso, a seconda del codice. – karpathy

1

Sembra che tu stia provando a profilare ogni chiamata a "prossimo" sul generatore? Se è così, potresti avvolgere il tuo generatore in un generatore di profili. Qualcosa del genere, in cui la parte commentata invia i risultati a un registro o database.

 
def iter_profiler(itr): 
    itr = iter(itr) 
    while True: 
    try: 
     start = time.time() 
     value = itr.next() 
     end = time.time() 
    except StopIteration: 
     break 
    # do something with (end - stop) times here 
    yield value 
 

Allora, invece di istanziare vostro generatore come generator() usereste iter_profiler(generator())

+1

in alternativa, è possibile applicare una versione modificata di esso come decoratore alla definizione del generatore. – aaronasterling

+0

Fondamentalmente, hai ragione. Ho già contato i millisecondi tra prima e dopo il ciclo 'for' nel secondo esempio del mio post originale, quindi ho già questa informazione (anche se conto un po 'di più di quanto vorresti, ma pochi millis non contano). Tuttavia, mi interessa di più degli "hot spot" nel mio codice (dove finisce il tempo di calcolo per morire?) Rispetto a quale delle decine di risultati ha richiesto più tempo rispetto agli altri. Quindi speravo in una soluzione basata su 'profile/cProfile' (che non genera un report per risultato). Se c'è un modo per unire i report del profiler, quella sarebbe la strada da percorrere. –