Pourquoi est x**4.0
plus rapide que x**4
? J'utilise 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
J'ai essayé de changer la puissance que j'ai augmentée pour voir comment il agit, et par exemple si j'élève x à la puissance de 10 ou 16, il passe de 30 à 35, mais si j'élève de 10,0 en tant que flotteur, c'est juste en mouvement environ 24,1 ~ 4.
Je suppose que cela a quelque chose à voir avec la conversion de flotteurs et les puissances de 2 peut-être, mais je ne sais pas vraiment.
J'ai remarqué que dans les deux cas, les puissances de 2 sont plus rapides, je suppose que ces calculs sont plus natifs / faciles pour l'interprète / l'ordinateur. Mais quand même, avec des flotteurs, il ne bouge presque pas. 2.0 => 24.1~4 & 128.0 => 24.1~4
mais 2 => 29 & 128 => 62
TigerhawkT3 souligné que cela ne se produit pas en dehors de la boucle. J'ai vérifié et la situation ne se produit (d'après ce que j'ai vu) que lorsque la base se soulève. Une idée à ce sujet?
python
performance
python-3.x
python-3.5
python-internals
arieljannai
la source
la source
x**4.0
et 3,9 pourx**4
.Réponses:
Les
int
objets Python 3 sont un objet à part entière conçu pour prendre en charge une taille arbitraire; de ce fait, ils sont traités comme tels au niveau C (voir comment toutes les variables sont déclarées commePyLongObject *
type inlong_pow
). Cela rend également leur exponentiation beaucoup plus délicate et fastidieuse car vous devez jouer avec leob_digit
tableau qu'il utilise pour représenter sa valeur pour l'exécuter. ( Source pour les courageux. - Voir: Comprendre l'allocation de mémoire pour les grands entiers en Python pour plus d'informations sur lesPyLongObject
s.)Les
float
objets Python , au contraire, peuvent être transformés endouble
type C (en utilisantPyFloat_AsDouble
) et les opérations peuvent être effectuées en utilisant ces types natifs . C'est génial car, après avoir vérifié les cas limites pertinents, cela permet à Python d' utiliser les plates-formespow
( Cpow
, c'est-à-dire ) pour gérer l'exponentiation réelle:où
iv
etiw
sont nos originauxPyFloatObject
comme Cdouble
s.Le fait précédent explique aussi l'écart entre Python 2 et 3 donc, j'ai pensé que je répondrais aussi à ce commentaire car il est intéressant.
Dans Python 2, vous utilisez l'ancien
int
objet qui diffère de l'int
objet dans Python 3 (tous lesint
objets dans 3.x sont dePyLongObject
type). Dans Python 2, il existe une distinction qui dépend de la valeur de l'objet (ou, si vous utilisez le suffixeL/l
):Le
<type 'int'>
voyez - vous ici fait la même chosefloat
ne s , il s'en toute sécurité convertie en Clong
lorsque exponentiation est effectuée sur elle (leint_pow
laisse aussi le compilateur de mettre « em dans un registre si elle peut le faire, de sorte que pourrait faire une différence) :cela permet un bon gain de vitesse.
Pour voir à quel point
<type 'long'>
s est lent par rapport à<type 'int'>
s, si vous enveloppez lex
nom dans unlong
appel en Python 2 (le forçant essentiellement à l'utiliserlong_pow
comme dans Python 3), le gain de vitesse disparaît:Notez que, bien que l'un des extraits de code transforme le
int
enlong
alors que l'autre ne le fait pas (comme l'a souligné @pydsinger), ce casting n'est pas la force contributive du ralentissement. La mise en œuvre delong_pow
est. (Chronométrez les déclarations uniquement aveclong(x)
pour voir).Il s'agit de l'optimiseur de judas de CPython pliant les constantes pour vous. Vous obtenez le même timing exact dans les deux cas puisqu'il n'y a pas de calcul réel pour trouver le résultat de l'exponentiation, seulement le chargement des valeurs:
Un octet-code identique est généré pour,
'4 ** 4.'
la seule différence étant que leLOAD_CONST
charge le float256.0
au lieu de l'int256
:Les temps sont donc identiques.
* Tout ce qui précède s'applique uniquement à CPython, l'implémentation de référence de Python. D'autres implémentations peuvent fonctionner différemment.
la source
range
, car le fait de ne chronométrer que l'**
opération elle-même ne donne aucune différence entre les entiers et les flottants.4**4
est tout aussi rapide que4**4.0
), et cette réponse ne touche pas du tout à cela.dis(compile('4 ** 4', '', 'exec'))
) donc l'heure devrait être exactement la même.long(x)**2.
est toujours plus rapide quelong(x)**2
par un facteur de 4-5. (Pas l'un des<type 'long'>
type en Python 3 s'explique probablement par les efforts faits pour simplifier le langage. Si vous pouvez avoir un type pour représenter des entiers, il est plus gérable que deux (et vous inquiétez de la conversion de l'un à l'autre lorsque cela est nécessaire, les utilisateurs sont confus, etc.). Le gain de vitesse est secondaire à cela. La section sur la justification de la PEP 237 offre également un aperçu supplémentaire.Si nous regardons le bytecode, nous pouvons voir que les expressions sont purement identiques. La seule différence est un type de constante qui sera un argument de
BINARY_POWER
. C'est donc très certainement dû à uneint
conversion en nombre à virgule flottante sur la ligne.Mise à jour: jetons un œil à Objects / abstract.c dans le code source CPython:
PyNumber_Power
appelsternary_op
, ce qui est trop long à coller ici, alors voici le lien .Il appelle la
nb_power
fente dex
, passanty
comme argument.Enfin,
float_pow()
à la ligne 686 de Objects / floatobject.c, nous voyons que les arguments sont convertis en Cdouble
juste avant l'opération réelle:la source
float_pow
lorsque cela ne fonctionne même pas pour le cas lent?4**4
et4**4.0
obtenez constamment plié. C'est un effet entièrement distinct.Parce que l'un est correct, un autre est l'approximation.
la source