交换分配比引入新变量慢?
swap assignment slower than introducing a new variable?
好的,我希望这个标题有点道理。我制作了一个快速傅里叶变换代码,想看看它如何运行得更快。所以这是我按照 FFT 指示进行交换的代码 ('code1'):
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)
更快的交换方法,因为我很难相信这是最有效的方法。
编辑:添加了实际代码而不是 pseudo-code。
更多编辑:
我在不使用 Numpy 的情况下尝试了 运行。两者都更快,所以这是积极的,但 t=a,a=f(a,b),b=g(t,b)
方法似乎比 a,b=f(a,b),g(a,b)
方法 (0.114) 更快 (0.104)。所以谜团依然存在。
新代码:
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))
和:
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 代码应该是这样,那就太好了。
像这样:
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
正如预期的那样,直接交换实际上稍快一些。您正在做的事情给您带来了其他结果有什么不同?
你说当你在更复杂的代码上下文中实现它时你看到了不同 - 然而,这表明你的代码本身很可能是罪魁祸首,这就是为什么 Whosebug 建议你分享一个最小的,可重现的例子,所以人们可以实际尝试你所说的,而不是必须相信你的话。
在大多数情况下,事实证明有人犯了错误,一切都如预期的那样。在某些情况下,您会得到一个有趣的答案。
在第一个版本中,我看到了 5 个索引操作,在第二个版本中,我看到了 6 个。我并不惊讶 6 个索引操作(以及您在其中使用的所有计算)比 5 个更昂贵。
与您在这些代码片段中进行的所有计算相比,创建一个临时变量或创建一个临时元组是小菜一碟。
好的,我希望这个标题有点道理。我制作了一个快速傅里叶变换代码,想看看它如何运行得更快。所以这是我按照 FFT 指示进行交换的代码 ('code1'):
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)
更快的交换方法,因为我很难相信这是最有效的方法。
编辑:添加了实际代码而不是 pseudo-code。
更多编辑:
我在不使用 Numpy 的情况下尝试了 运行。两者都更快,所以这是积极的,但 t=a,a=f(a,b),b=g(t,b)
方法似乎比 a,b=f(a,b),g(a,b)
方法 (0.114) 更快 (0.104)。所以谜团依然存在。
新代码:
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))
和:
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 代码应该是这样,那就太好了。
像这样:
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
正如预期的那样,直接交换实际上稍快一些。您正在做的事情给您带来了其他结果有什么不同?
你说当你在更复杂的代码上下文中实现它时你看到了不同 - 然而,这表明你的代码本身很可能是罪魁祸首,这就是为什么 Whosebug 建议你分享一个最小的,可重现的例子,所以人们可以实际尝试你所说的,而不是必须相信你的话。
在大多数情况下,事实证明有人犯了错误,一切都如预期的那样。在某些情况下,您会得到一个有趣的答案。
在第一个版本中,我看到了 5 个索引操作,在第二个版本中,我看到了 6 个。我并不惊讶 6 个索引操作(以及您在其中使用的所有计算)比 5 个更昂贵。
与您在这些代码片段中进行的所有计算相比,创建一个临时变量或创建一个临时元组是小菜一碟。