Почему 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,0 => 24,1 ~ 4 и 128,0 => 24,1 ~ 4 , но 2 => 29 и 128 => 62


TigerhawkT3база

Ответы (3)

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

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

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

/ * Теперь iv и iw конечны, iw не равно нулю, а iv равно
 * положительно и не равно 1,0. Наконец-то мы разрешаем
 * платформа может вмешаться и сделать все остальное.
 * /
errno = 0;
PyFPE_START_PROTECT ("pow", вернуть NULL)
ix = pow (iv, iw);

, где iv и iw - это наши оригинальные PyFloatObjects как C doubles.

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

The previous fact also explains the discrepancy between Python 2 and 3 so, I thought I'd address this comment too because it is interesting.

В Python 2 вы используете старый объект int, который отличается от объекта int в Python 3 (все объекты int в 3.x имеют тип PyLongObject). В Python 2 есть различие, которое зависит от значения объекта (или, если вы используете суффикс L / l):

# Python 2
type(30)  # 
type(30L) # 

вы видите здесь делает то же самое floats do, он безопасно конвертируется в C long , когда на нем выполняется возведение в степень (int_pow также указывает компилятору поместить их в регистр, если он может это сделать, чтобы могизменить ситуацию):

статический PyObject *
int_pow (PyIntObject * v, PyIntObject * w, PyIntObject * z)
{
    регистр long iv, iw, iz = 0, ix, temp, prev;
/ * Для краткости вырезано * /

это дает хороший прирост скорости.

Чтобы увидеть, насколько медленными s по сравнению с s, если вы обернули x name в вызове long в Python 2 (по сути, заставляя его использовать long_pow, как в Python 3), прирост скорости исчезает:

# 
(python2) ➜ python -m timeit "for x in range(1000):" " x**2"       
10000 loops, best of 3: 116 usec per loop
#  
(python2) ➜ python -m timeit "for x in range(1000):" " long(x)**2"
100 loops, best of 3: 2.12 msec per loop

Обратите внимание, что, хотя один фрагмент преобразует int в long, а другой - нет (как указано @pydsinger), это приведение не является движущей силой замедление. Реализация long_pow есть. (Время для операторов можно использовать только с long (x)).

[...] этого не происходит вне цикла. [...] Есть идеи по этому поводу?

Это оптимизатор глазка CPython, свертывающий константы за вас. Вы получаете одинаковое точное время в любом случае, поскольку нет фактических вычислений для нахождения результата возведения в степень, только загрузка значений:

dis.dis(compile('4 ** 4', '', 'exec'))
  1           0 LOAD_CONST               2 (256)
              3 POP_TOP
              4 LOAD_CONST               1 (None)
              7 RETURN_VALUE

Идентичный байт-код создается для '4 ** 4.' с той лишь разницей, что LOAD_CONST загружает float 256.0 вместо интервал 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

Значит, времена совпадают.


* Все вышеперечисленное применимо исключительно к CPython, эталонной реализации Python. Другие реализации могут работать иначе.

Если мы посмотрим на байт-код, мы увидим, что выражения полностью идентичны. Единственное отличие - это тип константы, которая будет аргументом 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

Обновление: давайте посмотрим на Objects / abstract.c в исходном коде CPython:

PyObject *
PyNumber_Power (PyObject * v, PyObject * w, PyObject * z)
{
    return ternary_op (v, w, z, NB_SLOT (nb_power), «** или pow ()»);
}

PyNumber_Power вызывает ternary_op, который слишком длинный для вставки сюда, поэтому вот ссылка.

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

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

статический PyObject *
float_pow (PyObject * v, PyObject * w, PyObject * z)
{
    двойной iv, iw, ix;
    int negate_result = 0;

    if ((PyObject *) z! = Py_None) {
        PyErr_SetString (PyExc_TypeError, "pow () 3-й аргумент нет"
            «разрешено, если все аргументы не являются целыми числами»);
        return NULL;
    }

    CONVERT_TO_DOUBLE (v, iv);
    CONVERT_TO_DOUBLE (ш, ив);
    ...

Поскольку одно правильное, другое является приблизительным.

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

2022 WebDevInsider