2009-05-15 16 views
56

El otro día estaba haciendo una evaluación comparativa de Python y me encontré con algo interesante. A continuación hay dos bucles que hacen más o menos lo mismo. El bucle 1 tarda aproximadamente el doble que el bucle 2 en ejecutarse.¿Por qué el bucle en range() en Python es más rápido que usar un bucle while?

Loop 1:

int i = 0 
while i < 100000000: 
    i += 1 

Loop 2:

for n in range(0,100000000): 
    pass 

¿Por qué el primer bucle de manera mucho más lenta? Sé que es un ejemplo trivial, pero despertó mi interés. ¿Hay algo especial acerca de la función range() que lo hace más eficiente que incrementar una variable de la misma manera?

Respuesta

113

ver el desmontaje de código de bytes pitón, es posible obtener una idea más concreta

uso bucle while:

1   0 LOAD_CONST    0 (0) 
      3 STORE_NAME    0 (i) 

2   6 SETUP_LOOP    28 (to 37) 
     >> 9 LOAD_NAME    0 (i)    # <- 
      12 LOAD_CONST    1 (100000000)  # <- 
      15 COMPARE_OP    0 (<)    # <- 
      18 JUMP_IF_FALSE   14 (to 35)   # <- 
      21 POP_TOP          # <- 

3   22 LOAD_NAME    0 (i)    # <- 
      25 LOAD_CONST    2 (1)    # <- 
      28 INPLACE_ADD         # <- 
      29 STORE_NAME    0 (i)    # <- 
      32 JUMP_ABSOLUTE   9     # <- 
     >> 35 POP_TOP 
      36 POP_BLOCK 

El cuerpo del bucle tiene 10 op

rango de uso:

1   0 SETUP_LOOP    23 (to 26) 
      3 LOAD_NAME    0 (range) 
      6 LOAD_CONST    0 (0) 
      9 LOAD_CONST    1 (100000000) 
      12 CALL_FUNCTION   2 
      15 GET_ITER 
     >> 16 FOR_ITER     6 (to 25)  # <- 
      19 STORE_NAME    1 (n)   # <- 

2   22 JUMP_ABSOLUTE   16    # <- 
     >> 25 POP_BLOCK 
     >> 26 LOAD_CONST    2 (None) 
      29 RETURN_VALUE 

El cuerpo del lazo tiene 3 op

El tiempo para ejecutar el código C es mucho más corto que el intepretor y se puede ignorar.

+32

+1 para explicar una respuesta con un desmontaje – TwentyMiles

+2

En realidad, el cuerpo del bucle en el primer desmontaje tiene 10 operaciones (el salto de la posición 32 a 9).En la implementación actual de CPython, la interpretación de cada bytecode resulta con una probabilidad bastante alta en una costosa derivación incorrecta de la rama indirecta en la CPU (el salto a la implementación del siguiente bytecode). Esto es una consecuencia de la implementación actual de CPython, los JITs implementados por tragamonedas en vacío, PyPy y otros probablemente perderán esa sobrecarga. Los mejores de ellos también podrán hacer una especialización de tipo para un orden de magnitud de aceleración. –

+2

+1 - @kcwu: ¿cómo lo desarmaste? –

3

Porque está ejecutando más a menudo en el código escrito en C en el intérprete. es decir, i + = 1 está en Python, tan lento (comparativamente), mientras que el rango (0, ...) es una llamada C, el ciclo for se ejecutará principalmente en C también.

29

range() se implementa en C, mientras que i += 1 se interpreta.

El uso de xrange() podría hacerlo aún más rápido para números grandes. Comenzando con Python 3.0 range() es el mismo que anteriormente xrange().

2

La mayoría de las llamadas a métodos de Python se ejecutan como código C. El código que debe interpretarse es mucho más lento. En términos de eficiencia de memoria y velocidad de ejecución, la diferencia es enorme. Las partes internas de Python se han optimizado al extremo, y es mejor aprovechar esas optimizaciones.

11

Se debe decir que hay una gran cantidad de creación y destrucción de objetos con el ciclo while.

i += 1 

es lo mismo que:

i = i + 1 

Pero debido enteros Python son inmutables, que no modifica el objeto existente; más bien crea un objeto nuevo con un nuevo valor. Básicamente es:

i = new int(i + 1) # Using C++ or Java-ish syntax 

El recolector de basura también tendrá que realizar una gran cantidad de tareas de limpieza. "La creación de objetos es cara".

Cuestiones relacionadas