Почему сложное возведение в степень так быстро в Python 3? - PullRequest
3 голосов
/ 11 марта 2019

За последнее время я наблюдал довольно много странностей, когда играл с timeit и показателями Python в последнее время.

Во-первых, зная, что math.sin (1) == (e ** 1j).имаг, мне было интересно узнать их относительные скорости.Вот что я нашел:

>>> timeit('sin(1)', 'from math import sin')
0.12068345113220857

>>> timeit('(e**1j).imag', 'from math import e')
0.27201285586511403

>>> timeit('exp(1j).imag', 'from cmath import exp')
0.25259275173584683

>>> timeit('(2.718281828459045**1j).imag')
0.04272853350335026

Это странно для меня.Почему использование самого номера и ** намного быстрее, чем что-либо еще?Почему это быстрее, чем грех?Я знаю, что это не из-за импорта;Я исключил это по отдельности.Также рассмотрим:

>>> (2.718281828459045**1j).imag
0.8414709848078965

>>> sin(1)
0.8414709848078965

Итак, он дает правильные ответы.

Я решил покопаться немного глубже и обнаружил, что .imag - настоящий виновник медлительности (2.718281828459045**1j).imag.На самом деле,

>>> timeit('2.718281828459045**1j')
0.013987474140321865

Кажется, это не что-то конкретное для 1j;Я могу использовать 2j или 0,95j и получить ту же скорость.Кроме того, это даже так быстро, как сложное и регулярное умножение!

>>> timeit('1*1j')
0.01617102287718808

>>> timeit('1*1')
0.016536898499907693

Я полностью сбит с толку.Как это может быть намного быстрее, чем грех, когда он выполняет как минимум столько же работы (а также вычисляет cos)?Как это может быть так быстро, как целочисленное умножение?Я подозреваю, что отчасти это происходит из-за шума из-за накладных расходов времени (где-то должен быть цикл), но даже это не все объясняет.Буду признателен за любую помощь с пониманием.

1 Ответ

3 голосов
/ 11 марта 2019

Вы можете объяснить свое наблюдение, посмотрев на байт-код, сгенерированный CPython с помощью модуля dis . Давайте посмотрим.

********************************************************************************
from match import sin; sin(1)
  1           0 LOAD_CONST               0 (0)
              2 LOAD_CONST               1 (('sin',))
              4 IMPORT_NAME              0 (match)
              6 IMPORT_FROM              1 (sin)
              8 STORE_NAME               1 (sin)
             10 POP_TOP
             12 LOAD_NAME                1 (sin)
             14 LOAD_CONST               2 (1)
             16 CALL_FUNCTION            1
             18 POP_TOP
             20 LOAD_CONST               3 (None)
             22 RETURN_VALUE
********************************************************************************
from math import e; (e**1j).imag
  1           0 LOAD_CONST               0 (0)
              2 LOAD_CONST               1 (('e',))
              4 IMPORT_NAME              0 (math)
              6 IMPORT_FROM              1 (e)
              8 STORE_NAME               1 (e)
             10 POP_TOP
             12 LOAD_NAME                1 (e)
             14 LOAD_CONST               2 (1j)
             16 BINARY_POWER
             18 LOAD_ATTR                2 (imag)
             20 POP_TOP
             22 LOAD_CONST               3 (None)
             24 RETURN_VALUE
********************************************************************************
from cmath import exp; exp(1j).imag
  1           0 LOAD_CONST               0 (0)
              2 LOAD_CONST               1 (('exp',))
              4 IMPORT_NAME              0 (cmath)
              6 IMPORT_FROM              1 (exp)
              8 STORE_NAME               1 (exp)
             10 POP_TOP
             12 LOAD_NAME                1 (exp)
             14 LOAD_CONST               2 (1j)
             16 CALL_FUNCTION            1
             18 LOAD_ATTR                2 (imag)
             20 POP_TOP
             22 LOAD_CONST               3 (None)
             24 RETURN_VALUE
********************************************************************************
(2.718281828459045**1j).imag
  1           0 LOAD_CONST               0 ((0.5403023058681398+0.8414709848078965j))
              2 LOAD_ATTR                0 (imag)
              4 RETURN_VALUE

Как видите, ваш последний пример очень быстрый, потому что интерпретатор превращает значение в константу при создании байт-кода. Вы не выполняете никакой работы в последний раз, кроме звонка на imag.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...