交换赋值比引入一个新变量慢?

问题描述 投票:0回答:1

好吧,我希望标题有点意义。我做了一个快速傅立叶变换的代码,想看看如何能更快。所以这里是代码('code1'),我按照FFT的要求进行交换。

stp='''
import numpy as nn
D=2**18
N=12
w=(nn.linspace(0,D-1,D)%2**N<(2**N/2))-.5 #square wave
f=w[0:2**N]+nn.zeros(2**N)*1j #it takes only one wavelength from w
'''
code1='''
for m in range(N):
    for l in range(2**m):
        for k in range(2**(N-1-m)):
            t=f[k+l*2**(N-m)]
            f[k+l*2**(N-m)]=(t+f[k+l*2**(N-m)+2**(N-1-m)])
            f[k+l*2**(N-m)+2**(N-1-m)]=(t-f[k+l*2**(N-m)+2**(N-1-m)])*nn.e**(-2*nn.pi*1j*k*2**(m)/2**N)
'''
print(timeit.timeit(setup=stp,stmt=code1,number=1))

我引入了一个新的变量't'. 这样输出的时间是0.132。

所以我想如果我这样做,应该会更快(设置和以前一样)。

code2='''
for m in range(N):
    for l in range(2**m):
        for k in range(2**(N-1-m)):
            f[k+l*2**(N-m)],f[k+l*2**(N-m)+2**(N-1-m)]=f[k+l*2**(N-m)]+f[k+l*2**(N-m)+2**(N-1-m)],(f[k+l*2**(N-m)]-f[k+l*2**(N-m)+2**(N-1-m)])*nn.e**(-2*nn.pi*1j*k*2**(m)/2**N)
'''
print(timeit.timeit(setup=stp,stmt=code2,number=1))

因为现在我做的是两个作业,而不是三个(这是我的想法)。但看起来这样做其实更慢(0.152)。有人知道为什么吗?有谁知道有什么方法可以使这种交换比之前的 t=a,a=f(a,b),b=g(t,b) 我之前介绍过,因为我很难相信这是最有效的方法。

EDIT:添加了实际代码,而不是伪代码。

更多EDIT:我试着在不使用Numpy的情况下运行同样的东西。两者都更快,所以这是积极的,但同样是 t=a,a=f(a,b),b=g(t,b) 的方法似乎要快一些(0.104)。a,b=f(a,b),g(a,b) 法(0.114)。所以,谜底依然存在。

新代码。

stpsansnumpy='''
import cmath as mm
D=2**18
N=12
w=[0]*D
for i in range(D):
    w[i]=(i%2**N<(2**N/2))-.5 #square wave
f=w[0:2**N]+[0*1j]*2**N #it takes only one wavelength from w
'''
code1math='''
for m in range(N):
    for l in range(2**m):
        for k in range(2**(N-1-m)):
            t=f[k+l*2**(N-m)]
            f[k+l*2**(N-m)]=(t+f[k+l*2**(N-m)+2**(N-1-m)])
            f[k+l*2**(N-m)+2**(N-1-m)]=(t-f[k+l*2**(N-m)+2**(N-1-m)])*mm.exp(-2*mm.pi*1j*k*2**(m)/2**N)
'''
print(timeit.timeit(setup=stpsansnumpy,stmt=code1math,number=1))

and:

code2math='''
for m in range(N):
    for l in range(2**m):
        for k in range(2**(N-1-m)):
            f[k+l*2**(N-m)],f[k+l*2**(N-m)+2**(N-1-m)]=f[k+l*2**(N-m)]+f[k+l*2**(N-m)+2**(N-1-m)],(f[k+l*2**(N-m)]-f[k+l*2**(N-m)+2**(N-1-m)])*mm.exp(-2*mm.pi*1j*k*2**(m)/2**N)
'''
print(timeit.timeit(setup=stpsansnumpy,stmt=code2math,number=1))
python fft
1个回答
1
投票

在第一个版本中,我看到5个索引操作,而在第二个版本中,我看到6个。我并不奇怪,6个索引操作(包括你在其中使用的所有计算)比5个更贵。

与你在这些代码片段中所做的所有计算相比,创建一个临时变量,或者创建一个临时元组,都是小菜一碟。


3
投票

如果你能分享一下为什么你认为其中一个比另一个慢,以及你的代码是否会像Python代码那样被正确格式化,那就更好了。

类似这样。

from timeit import timeit


def f(a, b):
    return a


def g(a, b):
    return a


def extra_var(a, b):
    t = a
    a = f(a, b)
    b = g(t, b)
    return a, b


def swap_direct(a, b):
    a, b = f(a, b), g(a, b)
    return a, b


print(timeit(lambda: extra_var(1, 2)))
print(timeit(lambda: swap_direct(1, 2)))

然而,如果你分享了,你可能会发现和我一样的结果。

0.2162299
0.21171479999999998

结果是如此的接近,以至于在连续的运行中,任何一个函数都会显得快一点或者慢一点。

所以,你会增加体积。

print(timeit(lambda: extra_var(1, 2), number=10000000))
print(timeit(lambda: swap_direct(1, 2), number=10000000))

神秘感就会消失

2.1527828999999996
2.1225841

直接交换实际上是稍微快了一点,就像预期的那样. 你刚才的做法有什么不同,给你带来了其他的结果?

你说,当你在更复杂的代码上下文中实现它时,你看到了差异--然而,这表明很可能你的代码本身就是可能的罪魁祸首,这就是为什么StackOverflow建议你分享一个最小的、可复制的例子,这样人们就可以真正尝试你说的发生的事情,而不是不得不相信你的话。

在大多数情况下,事实证明有人犯了一个错误,一切都在预期之中。在某些情况下,你会得到一个有趣的答案。

© www.soinside.com 2019 - 2024. All rights reserved.