¿Por qué el código Python se ejecuta más rápido en una función?

Resuelto thedoctar asked hace 12 años • 3 respuestas
def main():
    for i in xrange(10**8):
        pass
main()

Este fragmento de código en Python se ejecuta (Nota: la sincronización se realiza con la función de tiempo en BASH en Linux).

real    0m1.841s
user    0m1.828s
sys     0m0.012s

Sin embargo, si el bucle for no se coloca dentro de una función,

for i in xrange(10**8):
    pass

luego se ejecuta durante mucho más tiempo:

real    0m4.543s
user    0m4.524s
sys     0m0.012s

¿Por qué es esto?

thedoctar avatar Jun 28 '12 16:06 thedoctar
Aceptado

Dentro de una función, el código de bytes es:

  2           0 SETUP_LOOP              20 (to 23)
              3 LOAD_GLOBAL              0 (xrange)
              6 LOAD_CONST               3 (100000000)
              9 CALL_FUNCTION            1
             12 GET_ITER            
        >>   13 FOR_ITER                 6 (to 22)
             16 STORE_FAST               0 (i)

  3          19 JUMP_ABSOLUTE           13
        >>   22 POP_BLOCK           
        >>   23 LOAD_CONST               0 (None)
             26 RETURN_VALUE        

En el nivel superior, el código de bytes es:

  1           0 SETUP_LOOP              20 (to 23)
              3 LOAD_NAME                0 (xrange)
              6 LOAD_CONST               3 (100000000)
              9 CALL_FUNCTION            1
             12 GET_ITER            
        >>   13 FOR_ITER                 6 (to 22)
             16 STORE_NAME               1 (i)

  2          19 JUMP_ABSOLUTE           13
        >>   22 POP_BLOCK           
        >>   23 LOAD_CONST               2 (None)
             26 RETURN_VALUE        

La diferencia es que STORE_FASTes más rápido (!) que STORE_NAME. Esto se debe a que en una función ies local pero en el nivel superior es global.

Para examinar el código de bytes, utilice el dismódulo . Pude desmontar la función directamente, pero para desmontar el código de nivel superior tuve que usar el compilearchivo incorporado .

ecatmur avatar Jun 28 '2012 09:06 ecatmur

Quizás se pregunte por qué es más rápido almacenar variables locales que globales. Este es un detalle de implementación de CPython.

Recuerde que CPython se compila en código de bytes, que ejecuta el intérprete. Cuando se compila una función, las variables locales se almacenan en una matriz de tamaño fijo ( no una dict) y los nombres de las variables se asignan a los índices. Esto es posible porque no se pueden agregar dinámicamente variables locales a una función. Luego, recuperar una variable local es literalmente una búsqueda de puntero en la lista y un aumento de recuento, lo PyObjectcual es trivial.

Compare esto con una búsqueda global ( LOAD_GLOBAL), que es una dictbúsqueda verdadera que involucra un hash, etc. Por cierto, es por eso que necesitas especificar global isi quieres que sea global: si alguna vez asignas una variable dentro de un alcance, el compilador emitirá STORE_FASTs para su acceso a menos que le indiques que no lo haga.

Por cierto, las búsquedas globales todavía están bastante optimizadas. ¡ Las búsquedas de atributos foo.barson las realmente lentas!

A continuación se muestra una pequeña ilustración sobre la eficiencia de las variables locales.

Katriel avatar Jun 28 '2012 10:06 Katriel