2011-06-08 14 views
6

Estoy tratando de comprender el rendimiento de una función de generador. He utilizado cProfile y el módulo pstats para recopilar e inspeccionar datos de creación de perfiles. La función en cuestión es la siguiente:Función del generador Rendimiento

def __iter__(self): 
    delimiter = None 
    inData  = self.inData 
    lenData = len(inData) 
    cursor  = 0 
    while cursor < lenData: 
     if delimiter: 
      mo = self.stringEnd[delimiter].search(inData[cursor:]) 
     else: 
      mo = self.patt.match(inData[cursor:]) 
     if mo: 
      mo_lastgroup = mo.lastgroup 
      mstart  = cursor 
      mend   = mo.end() 
      cursor  += mend 
      delimiter = (yield (mo_lastgroup, mo.group(mo_lastgroup), mstart, mend)) 
     else: 
      raise SyntaxError("Unable to tokenize text starting with: \"%s\"" % inData[cursor:cursor+200]) 

self.inData es una cadena de texto Unicode, self.stringEnd es un diccionario con 4 simples de expresiones regulares, self.patt es una gran expresión regular. Todo es dividir la cuerda grande en cuerdas más pequeñas, una por una.

perfilar un programa que utiliza He encontrado que la mayor parte del tiempo de ejecución del programa se gasta en esta función:

In [800]: st.print_stats("Scanner.py:124") 

     463263 function calls (448688 primitive calls) in 13.091 CPU seconds 

    Ordered by: cumulative time 
    List reduced from 231 to 1 due to restriction <'Scanner.py:124'> 

    ncalls tottime percall cumtime percall filename:lineno(function) 
    10835 11.465 0.001 11.534 0.001 Scanner.py:124(__iter__) 

Pero mirando el perfil de la función en sí, no hay mucho tiempo dedicado en la sub-llamadas de funciones:

In [799]: st.print_callees("Scanner.py:124") 
    Ordered by: cumulative time 
    List reduced from 231 to 1 due to restriction <'Scanner.py:124'> 

Function     called... 
           ncalls tottime cumtime 
Scanner.py:124(__iter__) -> 10834 0.006 0.006 {built-in method end} 
           10834 0.009 0.009 {built-in method group} 
           8028 0.030 0.030 {built-in method match} 
           2806 0.025 0.025 {built-in method search} 
            1 0.000 0.000 {len} 

el resto de la función no es mucho, además, mientras que las tareas y si-si no. Incluso el método send en el generador que yo uso es rápido:

ncalls tottime percall cumtime percall filename:lineno(function) 
13643/10835 0.007 0.000 11.552 0.001 {method 'send' of 'generator' objects} 

¿Es posible que el yield, pasando un valor de vuelta al consumidor, se está llevando a la mayoría de las veces ?! ¿Algo más de lo que no estoy enterado?

EDITAR:

probablemente debería haber mencionado que la función de generador de __iter__ es un método de una clase pequeña, por lo self se refiere a una instancia de esta clase.

+3

¿Qué tan grande es inData? Cortarlo repetidamente puede no ser muy eficiente. Tal vez si intentaras usar islice en itertools en su lugar. Ve si eso hace alguna diferencia. – Dunes

+0

@Dunes Gracias, lo intentaré. Los datos de rendimiento se tomaron en una cadena de aproximadamente 1 MB. - Si pones esto en una respuesta, podría volver a votarlo. – ThomasH

+0

¿Has probado [esto] (http://stackoverflow.com/questions/4295799/how-to-improve-performance-of-this-code/4299378#4299378)? –

Respuesta

2

Esta es en realidad la respuesta de Dunes, que desafortunadamente solo la mencionó como comentario y no parece inclinada a ponerla en una respuesta correcta.

El principal culpable del rendimiento fueron las rebanadas de cuerda. Algunas mediciones de tiempo mostraron que el rendimiento de corte se degrada perceptiblemente con rodajas grandes (es decir, tomar una porción grande de una cadena ya grande). Para evitar que ahora utilizo el parámetro pos de los métodos del objeto de expresiones regulares:

if delimiter: 
     mo = self.stringEnd[delimiter].search(inData, pos=cursor) 
    else: 
     mo = self.patt.match(inData, pos=cursor) 

Gracias a all que ayudó.

+1

Ah, lo siento. Estuve muy ocupado en el trabajo los últimos días. Solo resolví el problema, ya que mi solución no era adecuada. Entonces, crédito para encontrar la solución. – Dunes

+0

@Dunes Estaba preguntando por el problema, por lo que su comentario fue bastante adecuado. La próxima vez :-) . – ThomasH

1

Si lee su muestra correctamente, está tomando un objeto generador, poniéndolo en delimiter, y utilizándolo para una búsqueda de matriz. Puede que ese no sea tu problema de velocidad, pero estoy bastante seguro de que es un error.

+1

Si se refiere a la parte 'delimiter = (yield ...)', no. Esta función es ** coroutine **, que permite al usuario hacer 'co.send (x)', que reanuda la ejecución (como 'next (generator)') y hace '(yield ...)' evaluate to ' x' (si simplemente lo usa como iterable, se evalúa como 'None' IIRC). – delnan

+0

Sí, como escribió delnan, a veces paso cadenas cortas al generador (utilizando .send desde el exterior), para que cambie a usar una expresión regular diferente para el siguiente fragmento. – ThomasH

Cuestiones relacionadas