2014-10-28 2 views
4

3D 배열에 대한 NumPy 중첩 루프가 Cython과 비교할 때 너무 느리다는 것이 혼란 스럽습니다. 나는 간단한 예를 썼다.NumPy 대 Cython - 너무 느린 중첩 루프?

파이썬/NumPy와 버전 :

import numpy as np 

def my_func(a,b,c): 
    s=0 
     for z in xrange(401): 
     for y in xrange(401): 
      for x in xrange(401): 
       if a[z,y,x] == 0 and b[x,y,z] >= 0: 
        c[z,y,x] = 1 
        b[z,y,x] = z*y*x 
        s+=1 
    return s 

a = np.zeros((401,401,401), dtype=np.float32) 
b = np.zeros((401,401,401), dtype=np.uint32) 
c = np.zeros((401,401,401), dtype=np.uint8) 

s = my_func(a,b,c) 

Cythonized 버전 : 약 my_func() 실행의

cimport numpy as np 
cimport cython 

@cython.boundscheck(False) 
@cython.wraparound(False) 
def my_func(np.float32_t[:,:,::1] a, np.uint32_t[:,:,::1] b, np.uint8_t[:,:,::1] c): 
    cdef np.uint16_t z,y,x 
    cdef np.uint32_t s = 0 

    for z in range(401): 
     for y in range(401): 
      for x in range(401): 
       if a[z,y,x] == 0 and b[x,y,z] >= 0: 
        c[z,y,x] = 1 
        b[z,y,x] = z*y*x 
        s = s+1 
    return s 

Cythonized 버전. 6500 배 빠릅니다. if 문과 배열 액세스만으로도 더 간단한 함수는 10000 배 더 빠릅니다. my_func()의 Python 버전은 500.651 초 걸립니다. 끝내기 위해. 상대적으로 작은 3D 배열을 너무 느리게 반복하거나 코드에서 실수를 저 지르지 않았습니까?

Cython 버전 0.21.1, Python 2.7.5, GCC 4.8.1, Xubuntu 13.10.

+1

타이밍에 대해 실제로 언급 할 수는 없습니다.그러나 나는 파이썬이 아닌 "작은"6500 만회 반복으로 루프를 호출하지 않을 것입니다. 이것이 numpy가 존재하는 이유입니다. 모든 배열 요소에 대해 파이썬 루프를 작성하는 대신 numpy가 내부적으로 요소를 반복하도록해야합니다. – sebastian

+1

'@cython.boundscheck (False)'는 python for 루프에서 수행되는 최대 15 억 개의 경계 검사를 제거합니다. 그것은 401^3 반복입니다 * 4 배열 액세스 * 액세스 당 3 인덱스 * 인덱스 당 2 검사. 음수 색인 생성을 허용하지 않았으므로이 숫자에 2를 곱하면됩니다. – Dunes

+0

이것은 정확하게 Numba가 코딩을하지 않아도 당신에게 아주 빠른 스피드 업을 제공 할 Python 종류입니다. 그냥'''numb.jit'''로 함수를 꾸미십시오. –

답변

5

파이썬은 해석 된 언어입니다. 기계 코드를 컴파일 할 때 얻을 수있는 이점 중 하나는 중첩 된 루프와 같은 것들로 얻을 수있는 엄청난 속도 향상입니다.

나는 당신의 기대가 무엇인지 모르지만, 당신이하려는 것을 해석 할 때 모든 언어가 매우 느릴 것입니다 (JIT compiling는 어느 정도 도움이 될 수 있습니다).

Numpy (또는 MATLAB 또는 다른 비슷한 것)에서 좋은 성능을 얻는 비법은 루프를 피하는 대신 코드를 대형 행렬에 대한 몇 가지 작업으로 리팩터링하는 것입니다. 이렇게하면 루핑은 Python 코드 대신 (많이 최적화 된) 기계 코드 라이브러리에서 수행됩니다.

+0

Numba은 참으로 JIT이 파이썬 코드를 (LLVM을 사용하여) 컴파일하고 C에서 O2에 비해 실행 속도를 그래, 우리 사이 썬에서 더 나은 성능을 이길 수 있지만, 때'''@의 numba.jit''' 데코레이터는 것입니다 수 있습니다 너무 극적인 효과를 발휘하십시오 (시도해보십시오!) 거의 가치가 없습니다. –

4

Krumelur에서 언급했듯이 파이썬 루프는 확실히 느립니다. 그러나 귀하는 귀하의 이익을 위해 numpy를 사용할 수 있습니다. 때로는 약간의 독창성이 필요하지만 전체 어레이에 대한 작업은 매우 빠릅니다.

예를 들어 코드에서 수정 한 루프가 b의 값을 읽지 못하기 때문에 (내 생각에 머리가 약간 흐릿 해져서 확실히 이해할 수 있습니다.) 다음은 동일해야 :

# Precalculate a matrix of x*y*z 
tmp = np.indices(a.shape) 
prod = (tmp[:,:,:,0] * tmp[:,:,:,1] * tmp[:,:,:,2]).T 

# Use array-wide logical operations to compute c using a and the transpose of b 
condition = np.logical_and(a == 0, b.T >= 0) 

# Use condition to alter b and c only where condition is true 
b[condition] = prod[condition] 
c[condition] = 1 

s = condition.sum() 

그래서 이것도 조건이 거짓 인 경우에 x*y*z을 계산 않습니다. 많은 시간을 사용하고있는 것으로 밝혀지면 피할 수 있지만 중요한 요소는 아닙니다.

+0

파이썬 루프는 실제로 빠릅니다. MATLAB과 R 루프는 느립니다. 그러나 NumPy 인덱싱은 느려지므로 긴밀한 루프에서 많은 작업을 수행하는 것은 좋은 생각이 아닙니다. 그러나 많은 과학자와 엔지니어가 MATLAB이나 R에 유효한 선입견을 가지고 Python에 도달하고 있습니다. 종종 Python에는 적용되지 않습니다. 파이썬 루프가 '느린'것은 그 중 하나입니다. C의 루프보다 느리지 만, R이나 MATLAB의 루프와 같은 것은 아닙니다. –

+0

+1, 당신이 실제로 * 한 * 리팩토링 파이썬 (I ​​년에 MATLAB을 사용하지 않은 내가 모르는 것 아마 훨씬 빨리 MATLAB보다 동안 나는 너무 게으른 : – Krumelur

+0

@SturlaMolden하지 C.에 비해 동안), CPython은 여전히 ​​해석되며 매우 동적 인 언어와 관련된 오버 헤드가 있습니다. – Krumelur

2

파이썬에서 numpy 배열을 사용하는 루프가 느린 경우 가능한 벡터 계산을 사용해야합니다. 알고리즘에 배열의 모든 요소에 대한 루프가 필요한 경우 여기에 몇 가지 빠른 힌트가 있습니다.

a[z,y,x]

는 NumPy와 스칼라 값, NumPy와 스칼라 값 계산은 매우 느립니다 :

x = 3.0 
%timeit x > 0 

x = np.float64(3.0) 
%timeit x > 0 

NumPy와 1.8.2 내 PC에서 출력, 윈도우 7 :

10000000 loops, best of 3: 64.3 ns per loop 
1000000 loops, best of 3: 657 ns per loop 

당신이 할 수있는 item() 메서드를 사용하여 직접 파이썬 값을 가져 오십시오.

if a.item(z, y, x) == 0 and b.item(x, y, z) >= 0: 
    ... 

for 루프를 약 8 배속으로 향상시킬 수 있습니다.

+0

NumPy를 버전 1.9.0으로 업그레이드하고 a.item (z, y, x)가 0이면 a [z, y, x] == 0 및 b [x, y, == 0 및 b.item (x, y, z)> = 0 :'눈에 띄는 차이를 만드십시오. 이제 파이썬/NumPy 버전은 aprox입니다. cythonized 버전에 비해 600 배 느립니다. – MarcinBurz