2010-08-25 13 views
10

Dostosowuję aplikację, która intensywnie korzysta z generatorów, aby uzyskać jej wyniki w celu zapewnienia interfejsu internetowego web.py.Profilowanie generatorów Pythona

Do tej pory mogłem zawijać wywołanie do pętli for i instrukcji generujących wydruki w funkcji i wywoływać to za pomocą cProfile.run() lub runctx(). Koncepcyjnie:

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

cProfile.run('output()') 

W web.py muszę zawinąć go w następujący sposób, ponieważ chcę, aby od razu produkować wyjście z potencjalnie długotrwałych obliczeń w każdym kroku iteracji za pomocą yield:

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

Czy istnieje sposób na profilowanie wszystkich wywołań do generatora, jak w pierwszym przykładzie, gdy jest używany tak, jak w drugim?

+0

Czy chcesz po prostu zmierzyć całe wywołanie funkcji zamiast tylko jednej iteracji? Podobnie jak w 'cProfile.run ('list (index(). GET())')'? –

+0

Zasadniczo to właśnie osiąga pętla for-loop. Problem polega na tym, że nie mam żadnej kontroli nad wywołaniami funkcji 'GET()', jest ona obsługiwana przez 'web.py'. Co więcej, nie sądzę, aby produkcja była już produkowana w ten sposób (przy użyciu wartości zwracanej). –

Odpowiedz

5

końcu znalazłem rozwiązanie. Zwróć wartość profilowania przez 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() 

ja też mógł łączyć kilka takich wyników profilowania (raz zacznę dając unikalne nazwy pliku) poprzez documentation i sklep/analizować je łączyć.

0

Czy możesz po prostu użyć time.time() do profilu części, które Cię interesują? Po prostu pobierz aktualny czas i odejmij od ostatniego pomiaru.

+0

Mam już całkowity czas, ale "Przetwarzanie zajęło 5.382 sekundy" nie jest wystarczająco szczegółowe, aby znaleźć wąskie gardła wydajności. Używam dość duży i rozgałęziony łańcuch generatorów wewnętrznie i przeznaczony do przechowywania danych wejściowych użytkownika i wynikowej wydajności do późniejszej analizy. Mam kilka funkcji, które trwają średnio 0,000 sekundy na każde połączenie, ale mogą być nazywane dziesiątki tysięcy razy. –

+0

W takim przypadku możesz mieć licznik liczby całkowitej dla każdego wykonania tych funkcji i wykonywać tylko pomiar co 1000. wykonanie? W ten sposób można zmierzyć fragmenty kodu i zawęzić wąskie gardła. Rozumiem, że może to być trochę uciążliwe, w zależności od kodu. – karpathy

1

Wygląda na to, że próbujesz profilować każde połączenie do "następnego" w generatorze? Jeśli tak, możesz zawinąć generator w generator profilowania. Coś takiego, gdzie skomentowana część będzie wysyłała wyniki do dziennika lub bazy danych.

 
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 
 

Wtedy zamiast instancji generator jako generator() byłoby użyć iter_profiler(generator())

+1

Alternatywnie można zastosować zmodyfikowaną wersję jako dekorator w definicji generatora. – aaronasterling

+0

Zasadniczo masz rację. Już liczę milisekundy między przed i po pętli 'for' w drugim przykładzie mojego pierwszego postu, więc już mam tę informację (chociaż liczyć to nieco więcej niż ty, ale kilka mili nie ma znaczenia). Jednak bardziej zależy mi na "gorących punktach" w moim kodzie (gdzie czas obliczeń idzie na śmierć?) Niż dokładnie, który z kilkudziesięciu wyników trwał dłużej niż pozostałe. Dlatego liczyłem na rozwiązanie 'profile/cProfile' (które nie generuje jednego raportu na wynik). Jeśli istnieje sposób na scalenie raportów profilera, będzie to właściwa droga. –