【问题标题】:Why is complex multiplication nearly as fast as integer multiplication in python?为什么复数乘法几乎和python中的整数乘法一样快?
【发布时间】:2020-05-10 03:32:06
【问题描述】:

我的印象是复数乘法比实数乘法需要更长的时间,因为它需要3 multiplications

但是我尝试了以下方法:

a, b = 3, 4
c, d = 5, 6
print(a*c - b*d, a*d + b*c)

e = 3+4j
f = 5+6j
print(e*f)

%timeit a*c - b*d
%timeit a*d + b*c
%timeit e*f
%timeit a*b

得到了

-9 38
(-9+38j)
110 ns ± 0.2 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
102 ns ± 0.193 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
47.2 ns ± 0.942 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
43 ns ± 1.52 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

为什么复数乘法几乎和实数复数一样快?

我知道 Python 是一门高级语言,但这个结果仍然让我感到困惑......

我能想到的唯一解释是,我的两种计算都达到了如此低的速度,以至于瓶颈不是计算本身,而是其他东西..


这是我想问的原始问题


我有这个版本的离散傅里叶变换,它使用复杂的运算:

from math import e, pi, cos, sin, log2

def W(n, N):
    theta = n * -1 * 2 * pi / N
    return cos(theta) + 1j*sin(theta)

def dft(signal):
    N = len(signal)
    total = []
    for k in range(N):
        s = 0
        for n in range(N):
            s += signal[n] * W(n*k, N)
        total.append(s)
    return total

我也有这个版本的 DFT,它不使用复杂运算,而是使用两个实数信号,因为信号的虚部被编码到实数数组中。

def Wri(n, N):
    theta = n * -1 * 2 * pi / N
    return cos(theta), sin(theta)

def dft_ri(signal_r, signal_i):
    N = len(signal_r)
    tr, ti = [], []
    for k in range(N):
        sr = 0
        si = 0
        for n in range(N):
            a = signal_r[n]
            b = signal_i[n]
            c, d = Wri(n*k, N)
            sr += a*c - b*d
            si += a*d + b*c
        tr.append(sr)
        ti.append(si)
    return tr, ti

使用以下复数信号

x = np.linspace(0, T, 1024)
def signal2(t):
    return np.where(np.logical_and((t%T>=0), (t%T<np.pi)), t%T+t%T*1j, np.pi+np.pi*1j)

s = list(signal2(x))
sr = list(np.real(s))
si = list(np.imag(s))

两个 W 花费的时间大致相同

%timeit W(8, 3)
%timeit Wri(8, 3)

输出

464 ns ± 0.756 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
424 ns ± 2.21 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

然而

%timeit dft(s)
%timeit dft_ri(sr, si)

输出

935 ms ± 4.63 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
1.6 s ± 3.04 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

问题

为什么没有复杂运算的 DFT 所花费的时间几乎是具有复杂运算的 DFT 的两倍?

【问题讨论】:

    标签: python performance complex-numbers


    【解决方案1】:

    您的时间安排几乎是 100% 的开销。无论哪种方式,实际的硬件级乘法都只是成本的一​​小部分。

    尝试使用 NumPy 数组,它的每个元素开销要低得多,您会看到不同的情况:

    In [1]: import numpy
    
    In [2]: x = numpy.ones(10000)
    
    In [3]: y = x.astype(complex)
    
    In [4]: %timeit x*x
    4.2 µs ± 51.5 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
    
    In [5]: %timeit y*y
    16.6 µs ± 696 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
    
    In [6]: z = x.astype(int)
    
    In [7]: %timeit z*z
    6.54 µs ± 1 µs per loop (mean ± std. dev. of 7 runs, 100000 loops each)
    

    这是一个接近 1 纳秒的每个元素的时间,而您的时间更像是每个元素 40 到 100 个。


    您在 FFT 实现中看到的性能差异也是由于开销所致。您的运行时实际上很少用于数学计算。

    【讨论】:

    • 我添加了我想问的原始问题。这也是由于开销吗?如果需要,我也可以单独询问有关 DFT 的问题。
    • @charel-f:是的,这也是开销。使用 SciPy 的 FFT 函数。
    • DFT 的实际开销是多少?
    • @charel-f:对象分配、动态分派、字节码解释器循环等。高度动态编程语言的无 JIT 实现所涉及的所有机制。
    猜你喜欢
    • 1970-01-01
    • 2011-09-02
    • 1970-01-01
    • 2013-03-16
    • 2017-08-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多