¿Por qué es x**4.0má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.0y 3.9 parax**4.Respuestas:
Los
intobjetos 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_digitmatriz 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 sobrePyLongObjects.)Los
floatobjetos de Python , por el contrario, se pueden transformar a undoubletipo 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
ivyiwson nuestrosPyFloatObjects originales como Cdoubles.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
intobjeto antiguo que difiere delintobjeto en Python 3 (todos losintobjetos en 3.x son dePyLongObjecttipo). 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 quefloathace , se convierte de forma segura en una Clongcuando se realiza la exponenciación (int_powtambié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 elxnombre en unalongllamada en Python 2 (esencialmente forzándolo a usarlolong_powcomo en Python 3), la ganancia de velocidad desaparece:Tomar nota de que, a pesar de las transformaciones de un fragmento de las
intalong, 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_powes. (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_CONSTcarga el flotante en256.0lugar 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**4es 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)**2por 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 unintser 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_Powerllamadasternary_op, que es demasiado largo para pegar aquí, así que aquí está el enlace .Llama a la
nb_powerranura dex, pasandoycomo argumento.Finalmente, en la
float_pow()línea 686 de Objects / floatobject.c vemos que los argumentos se convierten a Cdoublejusto antes de la operación real:fuente
float_powmiras cuando eso ni siquiera funciona para el caso lento?4**4y4**4.0se dobla constantemente. Ese es un efecto completamente separado.Porque uno es correcto, otro es aproximación.
fuente