Почему x**4.0 быстрее, чем x**4в Python 3 * ?
intОбъекты Python 3 - это полноценный объект, разработанный для поддержки произвольного размера; в связи с этим они обрабатываются как таковые на уровне C (смотрите, как все переменные объявляются как PyLongObject *type in long_pow). Это также делает их возведение в степень более хитрым и утомительным, поскольку вам нужно поиграться с ob_digitмассивом, который он использует для представления его значения для его выполнения. ( Источник для смелых. - См .: Понимание распределения памяти для больших целых чисел в Python для получения дополнительной информации о PyLongObjects.)
floatОбъекты Python , напротив, могут быть преобразованы в doubleтип C (с помощью PyFloat_AsDouble), а операции могут выполняться с использованием этих собственных типов . Это здорово , потому что, после проверки соответствующих реберных случаев, это позволяет Python , чтобы использовать платформыpow ( C - х 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наши оригинальные PyFloatObjects как C doubles.
Для чего это стоит: Python 2.7.13для меня является фактором 2~3быстрее и показывает обратное поведение.
Предыдущий факт также объясняет несоответствие между Python 2 и 3, поэтому я подумал, что я тоже рассмотрю этот комментарий, потому что он интересен.
В Python 2 вы используете старый intобъект, который отличается от intобъекта в Python 3 (все intобъекты в 3.x имеют PyLongObjectтип). В Python 2 есть различие, которое зависит от значения объекта (или, если вы используете суффикс L/l):
# Python 2
type(30) # <type 'int'>
type(30L) # <type 'long'>
<type 'int'>Вы видите здесь делает то же самое floatс делать , он получает благополучно превращается в C , long когда экспоненцирование выполняется на нем ( int_powтакже намекает на компилятор поместил их в реестр , если он может сделать это, так что может сделать разницу) :
static PyObject *
int_pow(PyIntObject *v, PyIntObject *w, PyIntObject *z)
{
register long iv, iw, iz=0, ix, temp, prev;
/* Snipped for brevity */
это позволяет получить хороший прирост скорости.
Чтобы увидеть, насколько медлительны <type 'long'>s по сравнению с <type 'int'>s, если вы обернули xимя в longвызове в Python 2 (по сути, заставляя его использовать long_powкак в Python 3), прирост скорости исчезает:
# <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
Обратите внимание, что, хотя один фрагмент преобразовывает значение 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вместо 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
Так что времена совпадают.
* Все вышеперечисленное относится исключительно к CPython, эталонной реализации Python. Другие реализации могут работать по-другому.