2017-12-15 17 views
-1

고성능 파이썬에 numpy를 소개하고 제 컴퓨터에서 코드를 사용하여 장을 읽었습니다. 실수로 for 루프를 사용하여 numpy 버전을 실행하고 결과가 원래 파이썬 루프에 비해 놀라 울 정도로 느리다는 것을 알았습니다.원시 파이썬 목록에서 for 루프가 numpy 배열의 루프보다 빠릅니다.

코드의 간략화 된 버전 I이 1과 0의 2 차원 어레이의 X 및 다른 2D 배열 Y를 정의하는 경우, 다음 다음 반복 개념적 X + = Y.

import time 
import numpy as np 

grid_shape = (1024, 1024) 

def simple_loop_comparison(): 
    xmax, ymax = grid_shape 

    py_grid = [[0]*ymax for x in range(xmax)] 
    py_ones = [[1]*ymax for x in range(xmax)] 

    np_grid = np.zeros(grid_shape) 
    np_ones = np.ones(grid_shape) 

    def add_with_loop(grid, add_grid, xmax, ymax): 
     for x in range(xmax): 
      for y in range(ymax): 
       grid[x][y] += add_grid[x][y] 

    repeat = 20 
    start = time.time() 
    for i in range(repeat): 
     # native python: loop over 2D array 
     add_with_loop(py_grid, py_ones, xmax, ymax) 
    print('for loop with native list=', time.time()-start) 

    start = time.time() 
    for i in range(repeat): 
     # numpy: loop over 2D array 
     add_with_loop(np_grid, np_ones, xmax, ymax) 
    print('for loop with numpy array=', time.time()-start) 

    start = time.time() 
    for i in range(repeat): 
     # vectorized numpy operation 
     np_grid += np_ones 
    print('numpy vectorization=', time.time()-start) 

if __name__ == "__main__": 
    simple_loop_comparison() 
X로 Y를 추가로 인 나는 완전히 NumPy와 벡터화 작업이 다른 두를 능가하지만 기본 파이썬 목록보다 훨씬 느린 NumPy와 배열 결과에 대한 루프를 위해 사용하는 것을보고 놀랐다 것으로 기대

# when repeat=10 
for loop with native list= 2.545672655105591 
for loop with numpy array= 11.622980833053589 
numpy vectorization= 0.020279645919799805 

# when repeat=20 
for loop with native list= 5.195128440856934 
for loop with numpy array= 23.241904258728027 
numpy vectorization= 0.04613637924194336 

: 같은

결과는 보인다. 나의 이해는 최소한 캐시가 numpy 배열로 상대적으로 채워 져야만한다는 것이다. for 루프를 사용하더라도 벡터화 없이는 성능이 뛰어나다.

numpy 또는 CPU/캐시/메모리가 저급 수준에서 작동하지 않는 이유가 있습니까? 고맙습니다.

편집 : 변경 제목

+1

이 아니다 'numpy' 루프 있지만 'numpy' 배열을 가진 보통의 파이썬 루프. – Sraw

+0

제목을 편집했습니다. 그것이 무엇이든 그것을 부르기 원한다면, 질문은 왜 성능 격차가 존재 하는가입니다. – dhu

+0

세 가지 : 1.) 이중 색인 : 기존 목록 및 요소를 참조하는 목록의 목록에 대해 2 차원 배열의 경우 행 및 요소에 대한 새 개체를 만듭니다. 2.) numpy'__getitem __/__ setitem__'은 목록 카운터 부분보다 더 많은 인자를 처리합니다. 3.) (나는 그 차이점을 완전히 이해하지는 못했지만 목록에 있습니다.) list .'__getitem__'는 내장 된 배열입니다. '__getitem__'은 메소드 랩퍼입니다 –

답변

4

더 간단한 경우 - 배열 대 목록 지능형리스트 :

In [119]: x = list(range(1000000)) 
In [120]: timeit [i for i in x] 
47.4 ms ± 634 µs per loop (mean ± std. dev. of 7 runs, 10 loops each) 

In [121]: arr = np.array(x) 
In [122]: timeit [i for i in arr] 
131 ms ± 3.69 ms per loop (mean ± std. dev. of 7 runs, 10 loops each) 

목록이 메모리의 다른 객체에 대한 포인터를 포함하는 데이터 버퍼를 갖는다.

In [123]: type(x[1000]) 
Out[123]: int 

배열은 바이트로 DataBuffer 내의 요소를 저장 : 그래서 반복하거나 목록 색인 그냥 포인터를 찾고 및 개체를 가져 오는이 필요합니다. 요소를 가져 오려면 해당 바이트를 빨리 찾은 다음 numpy 객체에 래핑해야합니다 (dtype에 따라). 그러한 객체는 0d 단일 요소 배열 (많은 동일한 속성이있는)과 유사합니다.

In [124]: type(arr[1000]) 
Out[124]: numpy.int32 

이 색인 생성은 번호를 가져 오지 않고 다시 작성합니다.

종종 개체 dtype 배열을 향상된 목록 또는 성능 저하 목록으로 설명합니다. 목록과 마찬가지로 메모리의 다른 위치에있는 객체에 대한 포인터가 있지만 append까지는 커질 수 없습니다. 숫자 배열의 많은 이점을 잃어 버리는 경우가 종종 있습니다. 그러나 그것의 반복 속도가 다른 두 사이에 해당 될

In [125]: arrO = np.array(x, dtype=object) 
In [127]: type(arrO[1000]) 
Out[127]: int 
In [128]: timeit [i for i in arrO] 
74.5 ms ± 1.42 ms per loop (mean ± std. dev. of 7 runs, 10 loops each) 

을 어쨌든, 나는 당신이 반복해야하는 경우, 목록을 고수하는 것이, 다른 SO 답을 찾았습니다. 목록으로 시작하는 경우 목록을 사용하는 것이 더 빠릅니다. numpy vector 속도가 빠르지 만 어레이를 만드는 데는 시간이 걸리므로 시간을 절약 할 수 있습니다.시간은 처음부터 (컴파일 NumPy와 코드)와 같은 배열을 만들 필요로

는 그것이이리스트로부터 배열을 만드는 데 걸리는 시간을 비교해

In [129]: timeit np.array(x) 
109 ms ± 1.97 ms per loop (mean ± std. dev. of 7 runs, 10 loops each) 
In [130]: timeit np.arange(len(x)) 
1.77 ms ± 31.6 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each) 
0

이후 그들이있어, 데이터 포인터에 대한 NumPy와 청하는 사람들 포인터 위치에서 값을 검색 한 다음 반복을 사용하여 참여 전환이다. 파이썬 목록에는 이러한 단계가 적습니다. 탁월한 속도 향상은 내부적으로 벡터, 행렬 수학을 반복하고 수행 한 다음 반환하고 대답하거나 배열에 응답 할 수있는 경우에만 나타납니다.