我正在将我在MATLAB中编写的数值方法转换为Python。出于某种原因,Python代码几乎完全相同,运行速度要慢得多。这里U
和V
是在每个时间步都解决的未知数。 U[:,n]
和V[:,n]
的大小为700x1。其余变量(dt
,A
和denom
)是常量。以下是循环(numpy
已导入为*
):
for n in range(0, 400):
UnVn2 = fft.fft(U[:, n] * V[:, n] ** 3)
U[:, n +1 ] = fft.ifft((fft.fft(U[:, n]) / dt - UnVn2 + A) / denom)
V[:, n + 1] = fft.ifft((fft.fft(V[:, n]) / dt + UnVn2) / denom)
有什么建议吗?非常感谢。
答案 0 :(得分:3)
答案 1 :(得分:2)
我不确定为什么Python比Matlab慢,但是......
作为傅立叶变换,FFT具有多个properties,这使得大部分(全部)FFT运算成为不必要的:
def func1(U, V, dt, denom, A) :
UnVn2 = np.fft.fft(U * V**3)
U_ = np.fft.ifft((np.fft.fft(U) / dt - UnVn2 + A) / denom)
V_ = np.fft.ifft((np.fft.fft(V) / dt + UnVn2) / denom)
return np.vstack((U_, V_))
def func2(U, V, dt, denom, A) :
UnVn2 = U * V**3
U_ = (U / dt - UnVn2) / denom
U_[0] += A / denom
V_ = (V / dt + UnVn2) / denom
return np.vstack((U_, V_))
U = np.random.rand(700)
V = np.random.rand(700)
dt, denom, A = tuple(np.random.rand(3))
>>> func1(U, V, dt, denom, A)
array([[ 2.35201751 -1.11022302e-16j, 0.81099082 -2.45463372e-16j,
0.48451858 +2.15658782e-18j, ..., 2.23237712 -5.24753851e-16j,
1.15264205 -2.31140087e-16j, 1.06670009 +1.28369537e-16j],
[ 2.89314136 +8.67361738e-17j, 3.65612404 -7.80625564e-17j,
3.31383830 +8.96916836e-17j, ..., 0.90415910 +6.27969898e-16j,
3.03505664 +4.72358723e-16j, 0.64669863 +4.99600361e-16j]])
>>> func2(U, V, dt, denom, A)
array([[ 2.35201751, 0.81099082, 0.48451858, ..., 2.23237712,
1.15264205, 1.06670009],
[ 2.89314136, 3.65612404, 3.3138383 , ..., 0.9041591 ,
3.03505664, 0.64669863]])
>>> np.max(np.abs(func1(U, V, dt, denom, A) - func2(U, V, dt, denom, A)))
1.5151595604785605e-15
当然:
>>> import timeit
>>> timeit.timeit('func1(U, V, dt, denom, A)', 'from __main__ import func1, U, V, dt, denom, A', number=400)
0.14169366197616284
>>> timeit.timeit('func2(U, V, dt, denom, A)', 'from __main__ import func2, U, V, dt, denom, A', number=400)
0.06098524703428154
我必须承认的比我预期的要少,但它仍然快了近3倍。
修改强>
不执行FFT的速度似乎太小,因此我修改了func1
和func2
以返回带有(U_, V_)
的元组并运行以下代码:
from time import clock
U = np.zeros((700,400), dtype=np.float)
V = np.zeros((700,400), dtype=np.float)
U[:,0] = np.random.rand(700)
V[:,0] = np.random.rand(700)
dt, denom, A = tuple(np.random.rand(3))
t = clock()
for j in xrange(399) :
U[:, j+1], V[:, j+1] = func1(U[:, j], V[:, j], dt, denom, A)
print clock() - t
t = clock()
for j in xrange(399) :
U[:, j+1], V[:, j+1] = func2(U[:, j], V[:, j], dt, denom, A)
print clock() - t
打印输出为11.5148652438
和0.321673111194
,因此实际问题设置的加速更像是x30。
我还为pwuertz的提案定时,11.1805414552
和0.297830755317
以下代码没有显着改进:
U = np.zeros((400, 700), dtype=np.float)
V = np.zeros((400, 700), dtype=np.float)
U[0] = np.random.rand(700)
V[0] = np.random.rand(700)
dt, denom, A = tuple(np.random.rand(3))
t = clock()
for j in xrange(399) :
U[j+1], V[j+1] = func1(U[j], V[j], dt, denom, A)
print clock() - t
t = clock()
for j in xrange(399) :
U[j+1], V[j+1] = func2(U[j], V[j], dt, denom, A)
print clock() - t
但它确实看起来更整洁。
答案 2 :(得分:1)
我不确定MatLab如何在多维数组中组织轴,但我很确定numpy使用类似C的row-major order(编辑:维基百科甚至提到MatLab使用列主要顺序; ))。
由于您在单列上操作,因此只有所有操作都必须遍历行。对于行主要排序,这通常比遍历整行的效率低。考虑转置2d阵列的布局,你应该会明显提高性能。