2015-01-27 6 views
2

시작하기 전에 내가 전에 물어 본 적이 있지만 나는 (PyPy를 통해 실행하는 것과 같이) 제안 된 방법을 구현하는 데 어려움을 겪고있다. 이것은 코드 속도를 높이는 마지막 시도입니다.For 루프 속도를위한 Python

기본적으로 약 600 줄의 코드 조각이 있습니다. 대량의 코드는 실행하는 데 약 30 초가 걸리지 만 하나의 작은 섹션 (4 줄 길이)은 실행하는 데 5-15 분이 소요됩니다. 이것에 대한 간단한 이유는 for-loop, for-loop, for-loop에서의 수학 방정식입니다. 따라서이 방정식은 5 천만 번 정도 계산됩니다. 나는 그것이 시간이 걸릴 것이라고 생각하지만, MATLAB 내에서 같은 일이 실행될 때 그것은 정상적으로 1 분 안에 완료됩니다. 나는 이것이 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 :

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

을 사용하고

여기에 요청 바와 같이 위의 MATLAB 버전입니다 또한, 슬프게도 코드 뒤에 소스 수학이 없습니다. 코드의 2/3 정도는 가지고 있지만 후자의 3 분의 1은 아닙니다. 나는 변환 할 MATLAB 코드를 가지고있다. (지금은 적어도 지금)

+4

계산 내용을 파이썬 코드 외부에서 설명 할 수 있습니까? MATLAB, numpy 등을 사용하면 for 루프가 수동으로 반복하는 기본 제공 매트릭스/배열 처리 함수를 사용하는 것이 더 효율적입니다.예를 들어'c [k-ii] [jj, jj] * U [ii] [jj, jj]'는'tmp = reverse (c) * U '와 같은 형태 일 수 있습니다. (필자는 표기법을 근사하고 있지만 잘하면 아이디어는 분명하다.) 사실, ii는 k + 1까지 올라 가기 때문에 조금 더 복잡해 지지만 누적 합계 또는 누적 된 값을 가진 것이 도움이 될 수있다. 해당 MATLAB 코드는 무엇입니까? –

+0

나는 당신이 루프가 필요하다고 생각하지 않는다 .... 그러나 나는 당신의 말을 충분히 말할 수 없다. 아마 'Y = S * c * U' .... 어쩌면 –

+0

@JoranBeasley 그것은 ii는 0에서 k + 1까지의 범위를 가지므로 그보다 조금 더 복잡합니다. 계속되는 누적 합계가 있습니다. –

답변

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 덧셈은 실제로 회선 (convolution)이라는 것을 주목하십시오. 그런 다음 numpy 함수에서 인덱스를 올바르게 만지면됩니다.

추가 속도를 원할 경우 convolvescipy.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 배 (!)로 평가합니다.

+0

에 자세히 살펴볼 것입니다. 총 실행 시간은 677 초에서 26 초로 줄었습니다! 그리고 끝의 팁에 의거해서 나는 되돌아 가게되고, 다른 장소로부터 떨어져서 약간의 시간을 정돈 할 수있다라고 생각한다. 내가 15 초 미만으로 모든 것을 얻을 수 없다면 나는 놀랄 것이다! 대단히 감사합니다! – Steve

+0

배열 연산이 여기와 같은 빠른 프리미티브로 쉽게 다시 작성되지 않는 경우 Cython을 사용하여 다시 작성할 수 있습니다 (파이썬에서 이동할 때 코드 재구성을 최소화해야한다는 이점이 있습니다). 이 경우, 추가 정의'def compute (double [:, :, :]] Y, double [:, :, :] S, double [:, :, :] c, double [:, :] :] U) : cdef int k, ii, jj'하지만 for 루프는 그대로 유지 될 수 있습니다. –