2014-09-12 1 views
8

최근에 great SO post을 보았습니다. 사용자가 NumPy 배열을 처리 할 때 이 파이썬의 sum보다 빠르다고 제안했습니다.NumPy 함수를 사용하면 요소 별 연산이 연산자보다 빠릅니까?

NumPy 함수를 사용하면 NumPy 배열의 요소 별 연산이 연산자보다 빠릅니다. 그렇다면 왜이 경우입니까?

다음 예제를 고려하십시오.

import numpy as np 
a = np.random.random(1e10) 
b = np.random.random(1e10) 

np.subtract(a, b) 빠르고 안정적으로 a - b 이상이어야합니까?

답변

12

아니요, 중요한 의미는 아닙니다.

이유 np.sum가 NumPy와 구현하면서, sumsum "는 소박"(상당한 오버 헤드를 부과) 요소의 __add__ 오퍼레이터 호출 (이 경우는 NumPy와 배열)를 반복 가능한 반복하도록 구현되는 것보다 빠르다 sum은 최적화되어 있습니다. 요소의 유형 (dtype)을 알고 있으며 메모리에서 연속적이라는 사실을 이용합니다.

np.subtract(arr1, arr2)arr1-arr2의 경우는 그렇지 않습니다. 후자는 대략 전자로 번역됩니다.

차이점은 파이썬에서 빼기 연산자를 무시할 수 있기 때문에 numpy 배열은 최적화 된 버전을 사용하기 위해 그것을 재정의합니다. 그러나 sum 작업은 무시할 수 없으므로 numpy는 대안으로 최적화 된 버전을 제공합니다.

7

아니요. 타이밍을 꽤 쉽게 확인할 수 있습니다.

a = np.random.normal(size=1000) 
b = np.random.normal(size=1000) 

%timeit np.subtract(a, b) 
# 1000000 loops, best of 3: 1.57 µs per loop 

%timeit a - b 
# 1000000 loops, best of 3: 1.47 µs per loop 

%timeit np.divide(a, b) 
# 100000 loops, best of 3: 3.51 µs per loop 

%timeit a/b 
# 100000 loops, best of 3: 3.38 µs per loop 

numpy 함수는 실제로 느린 것 같습니다. 그게 중요한지는 확실치 않지만, 같은 구현 위에서 몇 가지 추가 함수 호출 오버 헤드로 인한 것 같아요.

EDIT : np.add과 친구가 필요에 따라 배열을 좋아하는 것을 배열로 변환하기 위해 추가 유형 검사 오버 헤드가 있으므로 아마도 np.add([1, 2], [3, 4])과 같은 것들이 가능합니다.

+6

'np.subtract'는 인수를 배열로 변환하는 추가 코드가 있습니다. 따라서'np.subtract ([1,2,3], [4,5,6])'가 작동합니다. 'a-b'는이 여분의 코드가 필요 없기 때문에 좀 더 빠릅니다. 'np.subtract'는 또한'out' 키워드 매개 변수를 처리합니다 ... – unutbu

+0

좋은 지적, @unutbu. 'np.subtract'의 추가 기능은 모두 함수의 시작/종료시 일회성 문제입니다. 여러분이 사용하지 않는다면'O (1) '이므로, 더 크고 더 큰 배열에서는 오버 헤드가 점점 무시 될 것입니다. –

3

@ shx2의 답변입니다.

난 그냥 sumnp.sum에 약간 확장됩니다 :

  • 내장 sum 요소 하나 하나에 의해 전에 파이썬 객체로 각각 변환 가지고 배열을 통해 이동합니다 그것들을 파이썬 객체로 함께 추가합니다.
  • np.sum 개별 값 중 변환없이 네이티브 코드의 최적화 루프를 사용하여 어레이들을 합산한다

마다의 변환 (shx2 지적한 바와 같이, 이것은 결정적 배열 콘텐츠의 균질성과 연속성을 요구) 배열 요소를 파이썬 객체에 추가하는 것은 오버 헤드의 주요 원인이다.

덧붙여서, 이것은 수학을 위해 파이썬의 standard-library C array type을 사용하는 것이 바보 인 이유를 설명합니다. sum(list)이 더 빠릅니다.sum(array.array)입니다.

1

a-ba.__rsub__(b)이라는 함수 호출로 변환됩니다. 따라서 변수에 속한 메소드를 사용합니다 (예 : a이 배열 인 경우 컴파일 된 numpy 코드).

In [20]: a.__rsub__?? 
Type:  method-wrapper 
String Form:<method-wrapper '__rsub__' of numpy.ndarray object at 0xad27a88> 
Docstring: x.__rsub__(y) <==> y-x 

np.subtract(x1, x2[, out]) 대한 의사 그것이 ufunc 것을 보여준다. ufunc은 종종 __rsub__과 같은 컴파일 된 작업을 사용하지만 ufunc 프로토콜에 맞게 약간의 오버 헤드가 추가 될 수 있습니다.

기타 많은 경우 np.foo(x, args)x.foo(args)으로 변환됩니다.

일반적으로 함수와 연산자가 실제 계산을 수행하기 위해 컴파일 된 numpy 코드를 호출하는 경우 타이밍은 매우 유사합니다. 특히 대규모 배열의 경우와 비슷합니다.