为什么在Python3中复杂的指数运算如此之快?

2024-04-30 01:59:04 发布

您现在位置:Python中文网/ 问答频道 /正文

我最近在玩timeit和Python的指数时发现了相当多的奇怪之处。你知道吗

首先,我知道数学.sin(1) ==(e**1j)imag,我对它们的相对速度很好奇。以下是我的发现:

>>> 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)时,它怎么能比sin快这么多呢?怎么能和整数乘法一样快呢?我怀疑部分原因是时间开销的噪音(一定有一个循环),但即使这样也不能解释一切。如果你能帮助我理解,我将不胜感激。你知道吗


Tags: 答案fromimport规则原因数字数学math
1条回答
网友
1楼 · 发布于 2024-04-30 01:59:04

您可以通过查看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的调用。你知道吗

相关问题 更多 >