挣扎着Python For-Loop速度

时间:2015-01-27 16:42:13

标签: python numpy

在开始之前,我会说我之前已经问过这个问题,但是我一直在努力实现已建议的方法(例如通过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代码。 (至少现在)

1 个答案:

答案 0 :(得分:2)

可以使用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

这提供了一个很好的~50000x加速。

另请注意,您应始终编写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倍(!)。