164 votes

Pourquoi x ** 4.0 est-il plus rapide que x ** 4 en Python 3?

Pourquoi est - x**4.0 plus rapide que l' x**4? Je suis aide Disponible 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 soulevés par voir comment il agit, et par exemple si je relance x à la puissance de 10 ou 16 c'est le saut de 30 à 35 ans, mais si je suis élever par 10.0 comme un float, c'est juste se déplaçant autour de 24,1~4.

Je suppose que cela a quelque chose à faire avec flotteur de conversion et des puissances de 2, peut-être, mais je ne sais pas vraiment.

J'ai remarqué que dans les deux cas, des puissances de 2 sont plus rapides, je pense que ces calculs sont natifs de l'/facile pour l'interprète/ordinateur. Mais encore, avec des chars c'est presque pas bouger. 2.0 => 24.1~4 & 128.0 => 24.1~4 mais 2 => 29 & 128 => 62


TigerhawkT3

a souligné qu'il n'arrive pas à l'extérieur de la boucle. J'ai vérifié et la situation ne se produit (de ce que j'ai vu) quand la base est posée. Une idée à ce sujet?

161voto

Jim Points 8793

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 PyLongObjects).

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); 

iv et iw sont à l'origine, notre PyFloatObjects que C doubles.

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, floats 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.

25voto

leovp Points 2276

Si l'on regarde le pseudo-code, on peut voir que les expressions sont strictement identiques. La seule différence est un type d'une constante qui sera un argument de BINARY_POWER. Donc c'est très certainement dû à un int à être converties en un nombre à virgule flottante en bas de la ligne.

>>> def func(n):
...    return n**4
... 
>>> def func1(n):
...    return n**4.0
... 
>>> from dis import dis
>>> dis(func)
  2           0 LOAD_FAST                0 (n)
              3 LOAD_CONST               1 (4)
              6 BINARY_POWER
              7 RETURN_VALUE
>>> dis(func1)
  2           0 LOAD_FAST                0 (n)
              3 LOAD_CONST               1 (4.0)
              6 BINARY_POWER
              7 RETURN_VALUE

Mise à jour: laissez jeter un oeil à des Objets abstraits.c Disponible dans le code source:

PyObject *
PyNumber_Power(PyObject *v, PyObject *w, PyObject *z)
{
    return ternary_op(v, w, z, NB_SLOT(nb_power), "** or pow()");
}

PyNumber_Power des appels ternary_op, ce qui est trop long pour coller ici, donc voici le lien.

Il appelle l' nb_power emplacement de x, passant y comme argument.

Enfin, en float_pow() à la ligne 686 des Objets/floatobject.c , nous voyons que les arguments sont convertis à un C double juste avant l'opération proprement dite:

static PyObject *
float_pow(PyObject *v, PyObject *w, PyObject *z)
{
    double iv, iw, ix;
    int negate_result = 0;

    if ((PyObject *)z != Py_None) {
        PyErr_SetString(PyExc_TypeError, "pow() 3rd argument not "
            "allowed unless all arguments are integers");
        return NULL;
    }

    CONVERT_TO_DOUBLE(v, iv);
    CONVERT_TO_DOUBLE(w, iw);
    ...

-1voto

Veky Points 142

Parce que l'un est correct, l'autre est approximation.

 >>> 334453647687345435634784453567231654765 ** 4.0
1.2512490121794596e+154
>>> 334453647687345435634784453567231654765 ** 4
125124901217945966595797084130108863452053981325370920366144
719991392270482919860036990488994139314813986665699000071678
41534843695972182197917378267300625
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X