2014-09-12 88 views
8

我最近遇到了一個great SO post,其中一個用戶建議numpy.sum在處理NumPy數組時比Python的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快於sumsum被實現爲「天真」迭代的迭代器(在這種情況下,numpy的陣列),調用元素的__add__運營商(其中施加了顯著的開銷),而numpy的的實現的sum進行了優化,例如利用它知道元素類型(dtype)的事實,並且它們在內存中是連續的。

這不是np.subtract(arr1, arr2)arr1-arr2的情況。後者大致意味着前者。

不同之處在於可以覆蓋python中的減法運算符,因此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函數實際上似乎慢了一點。我不確定這是否意義重大,但我懷疑這可能是因爲在同一個實現之上的一些額外的函數調用開銷。

編輯:由於@unutbu指出,這可能是因爲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 2014-09-12 21:51:34

+0

好點,@unutbu。這兩個'np.subtract'的附加功能都是函數入口/出口處的一次性問題。如果你不使用它們,它們就是'O(1)',所以它們在更大和更大的陣列中會變得越來越微不足道。 – 2014-09-12 21:58:47

3

很好的答案@ shx2。

我只是sumnp.sum略有擴大:

  • 內置sum將經歷一個數組,採取的元素一個接一個和他們每個人之前轉換爲Python對象將它們作爲Python對象添加在一起。
  • np.sum將總結使用本機代碼優化的循環陣列,無需單獨的數值的任何轉化(如shx2所指出的,這個關鍵的要求的陣列內容的均勻性和連續性)

的每轉換數組元素到Python對象是開銷的主要來源。

順便說一下,這也解釋了爲什麼它是愚蠢使用Python的standard-library C array type數學。 sum(list)很快sum(array.array)

1

a-b翻譯成函數調用a.__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])的文檔表明,它是一個ufuncufunc通常使用編譯的操作,如__rsub__,但可能會增加一些開銷以適應ufunc協議。

在其他許多情況下np.foo(x, args)轉換爲x.foo(args)。通常,如果函數和運算符最終調用編譯後的numpy代碼來執行實際計算,則時序將非常相似,特別是對於大型數組。