¿Por qué es x**4.0
más rápido que x**4
? Estoy usando CPython 3.5.2.
$ python -m timeit "for x in range(100):" " x**4.0"
10000 loops, best of 3: 24.2 usec per loop
$ python -m timeit "for x in range(100):" " x**4"
10000 loops, best of 3: 30.6 usec per loop
Intenté cambiar la potencia con la que aumenté para ver cómo actúa, y por ejemplo, si elevo x a la potencia de 10 o 16, está saltando de 30 a 35, pero si estoy aumentando 10.0 como flotador, solo se mueve alrededor de 24.1 ~ 4.
Supongo que tiene algo que ver con la conversión de flotación y potencias de 2 tal vez, pero realmente no lo sé.
Noté que en ambos casos las potencias de 2 son más rápidas, supongo que dado que esos cálculos son más nativos / fáciles para el intérprete / computadora. Pero aún así, con las carrozas casi no se mueve. 2.0 => 24.1~4 & 128.0 => 24.1~4
pero 2 => 29 & 128 => 62
TigerhawkT3 señaló que no sucede fuera del circuito. Verifiqué y la situación solo ocurre (por lo que he visto) cuando la base se está elevando. ¿Alguna idea sobre eso?
python
performance
python-3.x
python-3.5
python-internals
arieljannai
fuente
fuente
x**4.0
y 3.9 parax**4
.Respuestas:
Los
int
objetos de Python 3 son un objeto completo diseñado para admitir un tamaño arbitrario; debido a ese hecho, se manejan como tales en el nivel C (vea cómo se declaran todas las variables comoPyLongObject *
tipolong_pow
). Esto también hace que su exponenciación sea mucho más complicada y tediosa, ya que debe jugar con laob_digit
matriz que utiliza para representar su valor para realizarla. ( Fuente para los valientes. - Ver: Entender la asignación de memoria para enteros grandes en Python para más información sobrePyLongObject
s.)Los
float
objetos de Python , por el contrario, se pueden transformar a undouble
tipo C (mediante el usoPyFloat_AsDouble
) y las operaciones se pueden realizar utilizando esos tipos nativos . Esto es genial porque, después de verificar los casos relevantes, le permite a Python usar las plataformaspow
( Cpow
, es decir ) para manejar la exponenciación real:donde
iv
yiw
son nuestrosPyFloatObject
s originales como Cdouble
s.El hecho anterior también explica la discrepancia entre Python 2 y 3, por lo que pensé en abordar este comentario también porque es interesante.
En Python 2, está utilizando el
int
objeto antiguo que difiere delint
objeto en Python 3 (todos losint
objetos en 3.x son dePyLongObject
tipo). En Python 2, hay una distinción que depende del valor del objeto (o, si usa el sufijoL/l
):Lo
<type 'int'>
que ves aquí hace lo mismo quefloat
hace , se convierte de forma segura en una Clong
cuando se realiza la exponenciación (int_pow
también sugiere que el compilador los ponga en un registro si puede hacerlo, para que eso pueda marcar la diferencia) :Esto permite una buena ganancia de velocidad.
Para ver cuán lentos
<type 'long'>
son los s en comparación con<type 'int'>
s, si envuelve elx
nombre en unalong
llamada en Python 2 (esencialmente forzándolo a usarlolong_pow
como en Python 3), la ganancia de velocidad desaparece:Tomar nota de que, a pesar de las transformaciones de un fragmento de las
int
along
, mientras que el otro no (como en punta a cabo por @pydsinger), este reparto no es la fuerza que contribuye detrás de la desaceleración. La implementación delong_pow
es. (Mida las declaraciones únicamentelong(x)
para ver).Este es el optimizador de mirilla de CPython que dobla las constantes por usted. Obtiene los mismos tiempos exactos en cualquier caso, ya que no hay un cálculo real para encontrar el resultado de la exponenciación, solo carga de valores:
Se genera un código de bytes idéntico
'4 ** 4.'
con la única diferencia de queLOAD_CONST
carga el flotante en256.0
lugar de int256
:Entonces los tiempos son idénticos.
* Todo lo anterior se aplica únicamente a CPython, la implementación de referencia de Python. Otras implementaciones pueden funcionar de manera diferente.
fuente
range
, ya que la sincronización de la**
operación en sí no produce diferencias entre enteros y flotantes.4**4
es tan rápido como4**4.0
), y esta respuesta no toca eso en absoluto.dis(compile('4 ** 4', '', 'exec'))
), por lo que el tiempo debe ser exactamente el mismo.long(x)**2.
sigue siendo más rápido quelong(x)**2
por un factor de 4-5. (Sin embargo<type 'long'>
tipo en Python 3 probablemente se explica por los esfuerzos realizados para simplificar el lenguaje. Si puede tener un tipo para representar enteros, es más manejable que dos (y preocuparse por la conversión de uno a otro cuando sea necesario, los usuarios se confunden, etc.). La ganancia de velocidad es secundaria a eso. La sección de fundamentos de PEP 237 también ofrece más información.Si observamos el código de bytes, podemos ver que las expresiones son puramente idénticas. La única diferencia es un tipo de constante que será un argumento de
BINARY_POWER
. Por lo tanto, sin duda se debe a unint
ser convertido a un número de coma flotante en la línea.Actualización: echemos un vistazo a Objects / abstract.c en el código fuente de CPython:
PyNumber_Power
llamadasternary_op
, que es demasiado largo para pegar aquí, así que aquí está el enlace .Llama a la
nb_power
ranura dex
, pasandoy
como argumento.Finalmente, en la
float_pow()
línea 686 de Objects / floatobject.c vemos que los argumentos se convierten a Cdouble
justo antes de la operación real:fuente
float_pow
miras cuando eso ni siquiera funciona para el caso lento?4**4
y4**4.0
se dobla constantemente. Ese es un efecto completamente separado.Porque uno es correcto, otro es aproximación.
fuente