与 Python For 循环速度作斗争
Struggling With Python For-Loop Speed
在我开始之前,我会说我知道以前有人问过这个问题,但我一直在努力实现所建议的方法(例如 运行通过 PyPy 实现它)。这是加速代码的最后尝试。
基本上我有一段大约 600 行长的代码。大部分代码需要大约 30 秒才能完成 运行,但是一小部分(4 行长)需要 5-15 分钟才能完成 运行。原因很简单,它是一个 for 循环中的数学方程式,在 for 循环中,在 for 循环中。所以这个方程式被计算了大约 5000 万次。我承认这需要一段时间,但是当同样的事情在 MATLAB 中是 运行 时,它通常在一分钟内完成。我相信这是因为 JIT 加速;但我可能是错的。无论哪种方式,这让我觉得必须有一种方法可以加快速度。代码部分如下(所使用的矩阵非常大,所以我想我只是说它们的维度,因为它们中的数字可能会有所不同)。
for k in range(7500):
for jj in range(2):
for ii in range(k+1):
Y[k][jj,0] += S[ii][jj] * c[k-ii][jj,jj] * U[ii][jj,jj]
其中矩阵(/数组)的大小为:
numpy.shape(Y) = (7500, 2, 2)
numpy.shape(S) = (7500, 2, 1)
numpy.shape(c) = (7500, 2, 2)
numpy.shape(U) = (7500, 2, 2)
有人看到我可以做些什么来加快速度吗?
编辑 1:
根据要求,这里是上面的 MATLAB 版本:
for k=1:7500
for j=1:2
for i=1:7500
Y(j,1,k)=Y(j,1,k)+S(j,1,i)*c(j,j,k+1-i)*U(j,j,i);
end
end
end
编辑 2:
应该加了,我用的是3.4.2
此外,遗憾的是我没有代码背后的源数学。我有大约 2/3 的代码,但没有后三分之一。我只有要转换的 MATLAB 代码。 (至少现在)
使用np.convolve
即可得到结果。
import numpy as np
S = np.random.rand(1000, 2, 1)
c = np.random.rand(1000, 2, 2)
U = np.random.rand(1000, 2, 2)
Y = np.zeros_like(U)
for k in range(1000):
for jj in range(2):
for ii in range(k+1):
Y[k,jj,0] += S[ii,jj,0] * c[k-ii,jj,jj] * U[ii,jj,jj]
Yx = np.zeros_like(Y)
for jj in range(2):
Yx[:,jj,0] += np.convolve(S[:,jj,0] * U[:,jj,jj], c[:,jj,jj], mode='full')[:Yx.shape[0]]
print(abs(Y - Yx).max())
# -> 3.12638803734e-13
如何找到这个?请注意,事物只是沿 jj 轴相乘,并且 ii 求和实际上是卷积。然后只需在 numpy 函数中正确调整索引即可。
如果您想要更高的速度,将 convolve
替换为 scipy.signal.fftconvolve
可能会加快速度。一些时间:
for loops: 77 s
np.convolve: 33.6 ms
fftconvolve: 1.48 ms
这提供了不错的 ~ 50000 倍加速。
另请注意,您应该始终编写 Y[k,jj,0]
而不是 Y[k][jj,0]
—— 因为没有 JIT,后者会创建一个临时数组视图,如果您评估多次表达。将 for 循环表达式中的行重写为
Y[k,jj,0] += S[ii,jj,0] * c[k-ii,jj,jj] * U[ii,jj,jj]
已经将计算速度提高了 4 倍 (!)。
在我开始之前,我会说我知道以前有人问过这个问题,但我一直在努力实现所建议的方法(例如 运行通过 PyPy 实现它)。这是加速代码的最后尝试。
基本上我有一段大约 600 行长的代码。大部分代码需要大约 30 秒才能完成 运行,但是一小部分(4 行长)需要 5-15 分钟才能完成 运行。原因很简单,它是一个 for 循环中的数学方程式,在 for 循环中,在 for 循环中。所以这个方程式被计算了大约 5000 万次。我承认这需要一段时间,但是当同样的事情在 MATLAB 中是 运行 时,它通常在一分钟内完成。我相信这是因为 JIT 加速;但我可能是错的。无论哪种方式,这让我觉得必须有一种方法可以加快速度。代码部分如下(所使用的矩阵非常大,所以我想我只是说它们的维度,因为它们中的数字可能会有所不同)。
for k in range(7500):
for jj in range(2):
for ii in range(k+1):
Y[k][jj,0] += S[ii][jj] * c[k-ii][jj,jj] * U[ii][jj,jj]
其中矩阵(/数组)的大小为:
numpy.shape(Y) = (7500, 2, 2)
numpy.shape(S) = (7500, 2, 1)
numpy.shape(c) = (7500, 2, 2)
numpy.shape(U) = (7500, 2, 2)
有人看到我可以做些什么来加快速度吗?
编辑 1:
根据要求,这里是上面的 MATLAB 版本:
for k=1:7500
for j=1:2
for i=1:7500
Y(j,1,k)=Y(j,1,k)+S(j,1,i)*c(j,j,k+1-i)*U(j,j,i);
end
end
end
编辑 2:
应该加了,我用的是3.4.2
此外,遗憾的是我没有代码背后的源数学。我有大约 2/3 的代码,但没有后三分之一。我只有要转换的 MATLAB 代码。 (至少现在)
使用np.convolve
即可得到结果。
import numpy as np
S = np.random.rand(1000, 2, 1)
c = np.random.rand(1000, 2, 2)
U = np.random.rand(1000, 2, 2)
Y = np.zeros_like(U)
for k in range(1000):
for jj in range(2):
for ii in range(k+1):
Y[k,jj,0] += S[ii,jj,0] * c[k-ii,jj,jj] * U[ii,jj,jj]
Yx = np.zeros_like(Y)
for jj in range(2):
Yx[:,jj,0] += np.convolve(S[:,jj,0] * U[:,jj,jj], c[:,jj,jj], mode='full')[:Yx.shape[0]]
print(abs(Y - Yx).max())
# -> 3.12638803734e-13
如何找到这个?请注意,事物只是沿 jj 轴相乘,并且 ii 求和实际上是卷积。然后只需在 numpy 函数中正确调整索引即可。
如果您想要更高的速度,将 convolve
替换为 scipy.signal.fftconvolve
可能会加快速度。一些时间:
for loops: 77 s
np.convolve: 33.6 ms
fftconvolve: 1.48 ms
这提供了不错的 ~ 50000 倍加速。
另请注意,您应该始终编写 Y[k,jj,0]
而不是 Y[k][jj,0]
—— 因为没有 JIT,后者会创建一个临时数组视图,如果您评估多次表达。将 for 循环表达式中的行重写为
Y[k,jj,0] += S[ii,jj,0] * c[k-ii,jj,jj] * U[ii,jj,jj]
已经将计算速度提高了 4 倍 (!)。