Pourquoi est - x**4.0
plus rapide que x**4
en Python 3*?
Python 3 int
objets à part entière un objet conçu pour supporter une taille arbitraire; en raison de ce fait, ils sont traités comme tels sur le niveau C (voir la façon dont toutes les variables sont déclarées en tant que PyLongObject *
type en long_pow
). Cela rend également leur élévation à la puissance beaucoup plus délicat et fastidieux , car vous avez besoin de jouer avec l' ob_digit
tableau, il utilise pour représenter sa valeur pour l'exécuter. (Source pour les courageux. - Voir: la Compréhension de l'allocation de mémoire pour les grands entiers en Python pour en savoir plus sur PyLongObject
s).
Python float
objets, au contraire, peut être transformé pour un C double
type (à l'aide d' PyFloat_AsDouble
) et les opérations peuvent être effectuées à l'aide de ces types natifs. C'est formidable parce que, après vérification de la pertinence de bord-cas, elle permet d'Python à utiliser les plates-formes' pow
(C pow
, qui est) pour gérer l'réelle de l'exponentiation:
/* Now iv and iw are finite, iw is nonzero, and iv is
* positive and not equal to 1.0. We finally allow
* the platform pow to step in and do the rest.
*/
errno = 0;
PyFPE_START_PROTECT("pow", return NULL)
ix = pow(iv, iw);
où iv
et iw
sont à l'origine, notre PyFloatObject
s que C double
s.
Pour ce que ça vaut: Python 2.7.13
, pour moi, est un facteur 2~3
plus rapide, et montre l'inverse de comportement.
Le précédent fait explique également la différence entre Python 2 et 3 alors, j'ai pensé que je voudrais vous adresse ce commentaire trop car il est intéressant.
En Python 2, vous êtes à l'aide de l'ancien int
objet qui diffère de l' int
objet en Python 3 (tous int
objets en 3.x sont des PyLongObject
type). En Python 2, il y a une distinction qui dépend de la valeur de l'objet (ou, si vous utilisez le suffixe L/l
):
# Python 2
type(30) # <type 'int'>
type(30L) # <type 'long'>
L' <type 'int'>
que vous voyez ici n'a la même chose, float
s n', il obtient en toute sécurité converti en un C long
lors de l'exponentiation est effectuée sur celui-ci (L' int_pow
également des conseils au compilateur de mettre 'em dans un registre si elle ne peut le faire, de sorte que pourrait faire une différence):
static PyObject *
int_pow(PyIntObject *v, PyIntObject *w, PyIntObject *z)
{
register long iv, iw, iz=0, ix, temp, prev;
/* Snipped for brevity */
cela permet un bon gain de vitesse.
Pour voir comment l'atonie <type 'long'>
s par rapport à d' <type 'int'>
s, si vous avez enveloppé l' x
nom long
appel en Python 2 (essentiellement en l'obligeant à utiliser long_pow
comme en Python 3), le gain de vitesse disparaît:
# <type 'int'>
(python2) ➜ python -m timeit "for x in range(1000):" " x**2"
10000 loops, best of 3: 116 usec per loop
# <type 'long'>
(python2) ➜ python -m timeit "for x in range(1000):" " long(x)**2"
100 loops, best of 3: 2.12 msec per loop
Prendre note que, bien que l'on extrait transforme l' int
de long
, tandis que l'autre n'a pas (comme l'a fait remarquer @pydsinger), cette distribution n'est pas l'contribuant à la force derrière le ralentissement. La mise en œuvre de l' long_pow
. (Énoncés uniquement avec long(x)
voir).
[...] il ne se passe pas à l'extérieur de la boucle. [...] Une idée à ce sujet?
C'est Disponible du judas optimiseur de pliage les constantes pour vous. Vous obtenez exactement la même timings les deux cas, car il n'y a pas de calcul pour trouver le résultat de l'élévation à la puissance, que le chargement de valeurs:
dis.dis(compile('4 ** 4', '', 'exec'))
1 0 LOAD_CONST 2 (256)
3 POP_TOP
4 LOAD_CONST 1 (None)
7 RETURN_VALUE
Identique byte-code est généré pour '4 ** 4.'
avec la seule différence étant que l' LOAD_CONST
des charges le flotteur 256.0
au lieu de int 256
:
dis.dis(compile('4 ** 4.', '', 'exec'))
1 0 LOAD_CONST 3 (256.0)
2 POP_TOP
4 LOAD_CONST 2 (None)
6 RETURN_VALUE
Ainsi, les temps sont identiques.
*Tous les ci-dessus s'appliquent uniquement pour Disponible, l'implémentation de référence de Python. D'autres implémentations peuvent fonctionner différemment.