2010-08-25 11 views
10

Estoy adaptando una aplicación que hace un uso intensivo de generadores para producir sus resultados para proporcionar una interfaz web web.py.Perfilando generadores Python

Hasta ahora, podía envolver la llamada a las sentencias for-loop y output-producing en una función y llamarla usando cProfile.run() o runctx(). Conceptualmente:

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

cProfile.run('output()') 

En web.py, tengo que envuelve la siguiente manera, ya que quiero para producir inmediatamente la salida del cómputo potencialmente larga en cada paso de iteración utilizando yield:

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

¿Hay alguna manera de perfilar todas las llamadas al generador como en el primer ejemplo cuando se usa como en el segundo?

+0

¿Simplemente desea medir la llamada a la función completa en lugar de solo una iteración? Como en 'cProfile.run ('list (index(). GET())')'? –

+0

En esencia, esto es lo que logra el bucle for. El problema aquí es que no tengo control sobre las llamadas a 'GET()', es manejado por 'web.py'. Además, no creo que la producción se produzca de esa manera (usando el valor de retorno). –

Respuesta

5

finalmente he encontrado una solución. Devuelve el valor de los perfiles a través del 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() 

También podría combinar varios dichos resultados de descripción (una vez que empezar a dar los nombres de archivos únicos) a través de documentation y almacenar/analizarlos combinado.

0

¿Puedes simplemente usar time.time() para perfilar las partes que te interesan? Simplemente obtenga la hora actual y reste de la última vez que realizó una medición.

+0

Ya recibo el tiempo total, pero "El procesamiento tardó 5.382 segundos" no es lo suficientemente específico para encontrar los cuellos de botella de rendimiento. Utilizo una cadena de generadores bastante grande y ramificada internamente y tengo la intención de almacenar la entrada del usuario y el rendimiento resultante para su posterior análisis. Tengo varias funciones que toman un promedio de 0.000 segundos en cada llamada, pero pueden llamarse decenas de miles de veces. –

+0

En ese caso, puede tener un contador de enteros para cada ejecución de esas funciones y solo hacer una medición cada milésima de ejecución? En su lugar, podría medir trozos de código y reducir los cuellos de botella. Me doy cuenta de que puede ser un poco tedioso, dependiendo del código. – karpathy

1

Parece que está intentando perfilar cada llamada al 'siguiente' en el generador? Si es así, podría envolver su generador en un generador de perfiles. Algo así, donde la parte comentada enviará los resultados a un registro o base de datos.

 
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 
 

Entonces, en lugar de crear instancias de su generador como generator() que usaría iter_profiler(generator())

+1

alternativamente, puede aplicar una versión modificada de la misma como decorador en la definición del generador. – aaronasterling

+0

Básicamente, tienes razón. Ya cuento los milisegundos entre antes y después del bucle 'for' en el segundo ejemplo de la publicación original, por lo que ya tengo esta información (aunque cuento un poco más de lo que haría, pero algunos millis no importan). Sin embargo, me preocupo más por los "puntos calientes" en mi código (¿dónde va a morir el tiempo de cálculo?) Que exactamente cuál de las decenas de resultados tomó más tiempo que los demás. Por lo tanto, esperaba una solución basada en el perfil/cProfile (que no genera un informe por resultado). Si hay una manera de combinar informes de perfil, ese sería el camino a seguir. –