suptitle subplots python casting floating-point integer

subplots - ¿Por qué es más lento llamar a float() en un número que al agregar 0.0 en Python?



suptitle (5)

En pocas palabras, no estás lanzando nada. Un tipo de conversión le dice al compilador que trate el valor en una variable como si tuviera un tipo diferente; Se utilizan los mismos bits subyacentes. El float(1) de Python float(1) , sin embargo, construye un nuevo objeto en la memoria distinto del argumento para float .

Cuando agrega 1 + 0.0 , esto simplemente llama (1).__add__(0.0) , y el método __add__ de la clase int incorporada sabe cómo tratar los objetos float . No es necesario construir objetos adicionales (aparte del valor de retorno).

Las versiones más nuevas de Python tienen un optimizador para que las expresiones constantes como 1 + 0.0 puedan reemplazarse en tiempo de compilación con 1.0 ; no es necesario ejecutar ninguna función en el tiempo de ejecución. Reemplace 1 + 0.0 con x = 1 (antes del bucle) y x + 0.0 para forzar que int.__add__ sea ​​llamado en tiempo de ejecución para observar la diferencia. Será más lento que 1 + 0.0 , pero aún más rápido que float(1) .

¿Cuál es la razón por la que convertir un entero en un flotador es más lento que agregar 0.0 a ese int en Python?

import timeit def add_simple(): for i in range(1000): a = 1 + 0.0 def cast_simple(): for i in range(1000): a = float(1) def add_total(): total = 0 for i in range(1000): total += 1 + 0.0 def cast_total(): total = 0 for i in range(1000): total += float(1) print "Add simple timing: %s" % timeit.timeit(add_simple, number=1) print "Cast simple timing: %s" % timeit.timeit(cast_simple, number=1) print "Add total timing: %s" % timeit.timeit(add_total, number=1) print "Cast total timing: %s" % timeit.timeit(cast_total, number=1)

La salida de la cual es:

Añadir tiempo simple: 0.0001220703125

Tiempo de lanzamiento simple: 0.000469923019409

Agregue el tiempo total: 0.000164985656738

Tiempo total de lanzamiento: 0.00040078163147


La adición se puede hacer en C. Hacer la conversión hace que se llame a una función y luego se active la función C lib. Hay sobrecarga para esa llamada de función.


Si está utilizando Python 3 o una versión reciente de Python 2 (2.5 o superior), se realiza un plegado constante en el tiempo de generación del código de bytes. Esto significa que 1 + 0.0 se reemplaza con 1.0 antes de que se ejecute el código.


Si nos fijamos en el bytecode para add_simple :

>>> dis.dis(add_simple) 2 0 SETUP_LOOP 26 (to 29) 3 LOAD_GLOBAL 0 (range) 6 LOAD_CONST 1 (1000) 9 CALL_FUNCTION 1 12 GET_ITER >> 13 FOR_ITER 12 (to 28) 16 STORE_FAST 0 (i) 3 19 LOAD_CONST 4 (1.0) 22 STORE_FAST 1 (a) 25 JUMP_ABSOLUTE 13 >> 28 POP_BLOCK >> 29 LOAD_CONST 0 (None) 32 RETURN_VALUE

Verás que 0.0 no es en realidad en ningún lugar allí. Simplemente carga la constante 1.0 y la almacena en a. Python calculó el resultado en tiempo de compilación, por lo que no está sincronizando la suma.

Si usa una variable para 1 , entonces el optimizador de mirilla primitivo de Python no puede hacer la adición en tiempo de compilación, agregar 0.0 aún tiene una ventaja:

>>> timeit.timeit(''float(a)'', ''a=1'') 0.22538208961486816 >>> timeit.timeit(''a+0.0'', ''a=1'') 0.13347005844116211

Llamar a float requiere dos búsquedas de dict para determinar qué es float , uno en el espacio de nombres global del módulo y otro en los incorporados. También tiene una sobrecarga de llamadas a la función Python, que es más costosa que una llamada a la función C.

Agregar 0.0 solo requiere indexar en los co_consts del objeto de código de la co_consts para cargar la constante 0.0 , y luego llamar a las funciones nb_add nivel nb_add de los tipos int y float para realizar la suma. Esta es una menor cantidad de gastos generales en general.


Si usa el módulo dis , puede comenzar a ver por qué:

In [11]: dis.dis(add_simple) 2 0 SETUP_LOOP 26 (to 29) 3 LOAD_GLOBAL 0 (range) 6 LOAD_CONST 1 (1000) 9 CALL_FUNCTION 1 (1 positional, 0 keyword pair) 12 GET_ITER >> 13 FOR_ITER 12 (to 28) 16 STORE_FAST 0 (i) 3 19 LOAD_CONST 4 (1.0) 22 STORE_FAST 1 (a) 25 JUMP_ABSOLUTE 13 >> 28 POP_BLOCK >> 29 LOAD_CONST 0 (None) 32 RETURN_VALUE In [12]: dis.dis(cast_simple) 2 0 SETUP_LOOP 32 (to 35) 3 LOAD_GLOBAL 0 (range) 6 LOAD_CONST 1 (1000) 9 CALL_FUNCTION 1 (1 positional, 0 keyword pair) 12 GET_ITER >> 13 FOR_ITER 18 (to 34) 16 STORE_FAST 0 (i) 3 19 LOAD_GLOBAL 1 (float) 22 LOAD_CONST 2 (1) 25 CALL_FUNCTION 1 (1 positional, 0 keyword pair) 28 STORE_FAST 1 (a) 31 JUMP_ABSOLUTE 13 >> 34 POP_BLOCK >> 35 LOAD_CONST 0 (None) 38 RETURN_VALUE

Tenga en cuenta el CALL_FUNCTION

Las llamadas a funciones en Python son (relativamente) lentas. Como son búsquedas Porque el casting para float requiere una llamada de función, por eso es más lento.