Почему x * * 4.0 быстрее, чем x**4 в Python 3?


почему x**4.0 быстрее x**4? Я использую 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

Я попытался изменить силу, которую я поднял, чтобы увидеть, как она действует, и, например, если я поднимаю x до степени 10 или 16, он прыгает с 30 до 35, но если я поднимаю на 10.0 как поплавок, он просто движется вокруг 24.1~4.

Я думаю, что это имеет какое-то отношение к преобразованию поплавка и мощности 2, возможно, но я действительно не знаю.

я заметил, что в обоих случаях полномочия 2 быстрее, я думаю, так как эти вычисления являются более родными/легкими для интерпретатора/компьютера. Но все же, с поплавками он почти не двигается. 2.0 => 24.1~4 & 128.0 => 24.1~4но2 => 29 & 128 => 62


TigerhawkT3 указал, что это не происходит вне цикла. Я проверил и ситуация возникает только (из того, что я видел), когда базовый становится подняли. Есть идеи по этому поводу?
3 155

3 ответа:

почему x**4.0быстрее чем x**4 в Python 3*?

Python 3 int объекты - это полноценный объект, предназначенный для поддержки произвольного размера; в связи с этим они обрабатывается как таковой на уровне C (смотрите, как все переменные объявляются как PyLongObject * тип long_pow). Это также делает их возведение в степень намного больше хитрее и нудно, так как вам нужно поиграйте с ob_digit массив, который он использует для представления своего значения для его выполнения. (источник для смелых. -- вижу: понимание выделения памяти для больших целых чисел в Python подробнее о PyLongObject s.)

Python float объекты, наоборот, может быть преобразован до double тип (с помощью PyFloat_AsDouble) и операции могут быть выполнены используя эти собственные типы. это отлично потому что, после проверки соответствующих крайних случаев, он позволяет Python использовать платформы' pow (с pow, что составляет) для обработки фактического возведения в степень:

/* 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 и iw наш оригинальный PyFloatObjectкак с double s.

для чего это стоит: Python 2.7.13 для меня это фактор 2~3 быстрее, и показывает обратное поведение.

предыдущей факт также объясняет несоответствие между Python 2 и 3 Итак, я подумал, что я бы тоже обратился к этому комментарию, потому что это интересно.

в Python 2, вы используете старую int объект, который отличается от

если мы посмотрим на байт-код, то увидим, что выражения полностью идентичны. Единственное отличие-это тип константы, которая будет аргументом BINARY_POWER. Так что это, безусловно, из-за int преобразуется в число с плавающей точкой вниз по линии.

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

обновление: давайте взглянем на объекты/реферат.c в исходном коде CPython:

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

PyNumber_Power звонки ternary_op, который слишком долго, чтобы вставить сюда, так вот ссылка.

он называет nb_power слот x, передает y в качестве аргумента.

наконец, в float_pow() в строке 686 от объекты/floatobject.c мы видим, что аргументы преобразуются в C double прямо перед фактической деятельности:

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

потому что одно правильно, другое-приближение.

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