简体   繁体   English

为什么Python 3中的复指数如此之快?

[英]Why is complex exponentiation so fast in Python 3?

There's a fair amount of weirdness that I've observed recently when playing around with timeit and Python's exponents recently. 我最近在玩timeit和Python的指数时最近观察到了相当多的怪异。

First, knowing that math.sin(1) == (e**1j).imag, I was curious about their relative speeds. 首先,知道math.sin(1)==(e ** 1j).imag,我很好奇他们的相对速度。 Here is what I found: 这是我发现的:

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

This is bizarre to me. 这对我来说很奇怪。 Why is using the number itself and ** so much faster than anything else? 为什么使用数字本身和**比其他任何东西都快? Why is it faster than sin? 为什么它比罪更快? I know it's not due to the import; 我知道这不是由于进口; I ruled that out separably. 我把它分开了。 Also consider: 还要考虑:

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

>>> sin(1)
0.8414709848078965

So, it is giving the correct answers. 所以,它给出了正确的答案。

I decided to dig a bit deeper, and found that .imag is the real culprit of the slowness of (2.718281828459045**1j).imag . 我决定深入挖掘一下,发现.imag是(2.718281828459045**1j).imag慢的真正罪魁祸首。 In fact, 事实上,

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

It doesn't seem to be anything specific to 1j; 它似乎与1j没有任何关系; I can use 2j or 0.95j and get the same speed. 我可以使用2j或0.95j并获得相同的速度。 Additionally, it's even as fast as complex and regular multiplication! 此外,它甚至可以像复杂和常规的乘法一样快!

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

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

I am thoroughly confused. 我很困惑。 How can it be so much faster than sin, when it's doing at least as much work (and also calculating cos)? 当它至少做同样多的工作(以及计算cos)时,它怎么能比罪恶快得多? How can it be as fast as integer multiplication? 它怎么能像整数乘法一样快? I suspect part of this is due to noise from the overhead of timeit (there's gotta be a loop somewhere), but even that doesn't explain everything. 我怀疑部分原因是由于时间开销产生的噪音(某处有一个循环),但即使这样也无法解释所有问题。 I would appreciate any help with understanding. 我将不胜感激任何帮助。

You can explain your observation by looking at the bytecode generated by CPython using the dis module. 您可以通过使用dis模块查看CPython生成的字节码来解释您的观察。 Lets take a look. 让我们来看看。

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

As you can see, your last example is so fast because the interpreter is turning the value into a constant when the bytecode is created. 如您所见,您的最后一个示例非常快,因为解释器在创建字节码时将值转换为常量。 You aren't actually doing any work in your last timeit except for the call to imag . 你是不是真正做除了呼吁在你的上timeit任何工作imag

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM