2012-01-05 8 views
5

Atención, esto es un poco recursivo;)funciones de temporización

respondí esta pregunta: Python:How can i get all the elements in a list before the longest element?

Y después de que presenté allí donde otra respuesta que debe ser más rápido (que se cree el autor, y también lo hizo I) . Traté de cronometrar las diferentes soluciones, pero la solución que debería ser más lenta fue en realidad más rápida. Esto me hizo pensar que hay algo mal con mi código. ¿O es eso?

import string 
import random 
import time 

def solution1(lst): 
    return lst[:lst.index(max(lst, key=len))] 

def solution2(lst): 
    idx, maxLenStr = max(enumerate(lst), key=lambda x:len(x[1])) 
    return lst[:idx] 

# Create a 100000 elements long list that contains 
# random data and random element length 
lst = [] 
for i in range(100000): 
    s = "".join([random.choice(string.letters+string.digits) for x in range(1, random.randint(1,50))]) 
    lst.append(s) 

# Time the first solution 
start = time.time() 
solution1(lst) 
print 'Time for solution1', (time.time() - start) 

# Time the second solution 
start = time.time() 
solution2(lst) 
print 'Time for solution2', (time.time() - start) 

actualización

Antes de que alguien menciona qué pongo esto es como una nueva pregunta. La pregunta es más acerca de mí aprendiendo cómo medir el tiempo de ejecución ...

+1

estas dos funciones no devuelven el mismo tipo de objeto – joaquin

+0

Doh! ¡Por supuesto, gracias! –

+0

Se arregló. Pero eso incluso hace que mi código sea más rápido ... Y todavía pensaba que la solución2 debería ser más rápida. –

Respuesta

3

La lambda está costando más caro en la segunda solución.

I perfilado tanto los códigos y los datos del perfil, lo que parece, la primera solución es más rápido

A medida que el wiki diría llamada de función es costoso y en la segunda solución, la lambda y las llamadas a la función len son lo que es correr más lento

Tenga en cuenta, he recortado por la lista para una longitud de 1000 elementos

>>> cProfile.run('solution1(lst)') 
     5 function calls in 0.000 CPU seconds 

    Ordered by: standard name 

    ncalls tottime percall cumtime percall filename:lineno(function) 
     1 0.000 0.000 0.000 0.000 <pyshell#305>:1(solution1) 
     1 0.000 0.000 0.000 0.000 <string>:1(<module>) 
     1 0.000 0.000 0.000 0.000 {max} 
     1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} 
     1 0.000 0.000 0.000 0.000 {method 'index' of 'list' objects} 


>>> cProfile.run('solution2(lst)') 
     2004 function calls in 0.012 CPU seconds 

    Ordered by: standard name 

    ncalls tottime percall cumtime percall filename:lineno(function) 
     1 0.000 0.000 0.012 0.012 <pyshell#306>:1(solution2) 
    1000 0.006 0.000 0.009 0.000 <pyshell#306>:2(<lambda>) 
     1 0.000 0.000 0.012 0.012 <string>:1(<module>) 
    1000 0.003 0.000 0.003 0.000 {len} 
     1 0.003 0.003 0.012 0.012 {max} 
     1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} 
+0

Me gustaría aceptar todas las respuestas porque son valiosas. Pero solo puedo elegir uno y creo que esta fue la mejor explicación. Gracias :) –

2

El tiempo se ve bien.

solution1 puede ser más rápido, ya que no utiliza lambdas, por lo que no necesita llamar al código de Python en el ciclo. Sí itera la lista dos veces, pero no es un gran problema.

+0

@joaquin Sí, tienes razón. Estoy borrando mi comentario para evitar confusiones. Gracias por hacérmelo saber. – jcollado

3

Parece que la primera versión realiza muchas menos llamadas que la segunda.

por cierto, esta es probablemente otro ejemplo de cómo el código idiomático, simple suele ser también el más rápido en Python

>>> dis.dis(solution1) 
    2   0 LOAD_FAST    0 (lst) 
       3 LOAD_FAST    0 (lst) 
       6 LOAD_ATTR    0 (index) 
       9 LOAD_GLOBAL    1 (max) 
      12 LOAD_FAST    0 (lst) 
      15 LOAD_CONST    1 ('key') 
      18 LOAD_GLOBAL    2 (len) 
      21 CALL_FUNCTION   257 
      24 CALL_FUNCTION   1 
      27 SLICE+2    
      28 RETURN_VALUE   
>>> dis.dis(solution2) 
    2   0 LOAD_GLOBAL    0 (max) 
       3 LOAD_GLOBAL    1 (enumerate) 
       6 LOAD_FAST    0 (lst) 
       9 CALL_FUNCTION   1 
      12 LOAD_CONST    1 ('key') 
      15 LOAD_CONST    2 (<code object <lambda> at 000000000422BEB8, file "<input>", line 2>) 
      18 MAKE_FUNCTION   0 
      21 CALL_FUNCTION   257 
      24 UNPACK_SEQUENCE   2 
      27 STORE_FAST    1 (idx) 
      30 STORE_FAST    2 (maxLenStr) 

    3   33 LOAD_FAST    0 (lst) 
      36 LOAD_FAST    1 (idx) 
      39 SLICE+2    
      40 RETURN_VALUE 
+0

No importa. Las funciones llamadas toman mucho más tiempo, para cualquier información razonablemente grande. – zch

+0

@zch lo siento, no lo entiendo, ¿podrías explicar tu comentario? – joaquin

+0

Todas las instrucciones se llaman una sola vez por prueba. El tiempo de ejecución de la función 'max' es proporcional a la longitud de la lista de entrada. Entonces, para una lista lo suficientemente grande (en este caso no muy grande), ejecutar 'max' tomaría mucho más tiempo que ejecutar otras instrucciones. – zch

Cuestiones relacionadas