[英]What is the reason for slowness of list(numpy.array)?
眾所周知,如果a
是一個numpy數組,則a.tolist()
比list(a)
更快,例如:
>>> import numpy as np
>>> big_np=np.random.randint(1,10**7,(10**7,))
>>> %timeit list(big_np)
1 loop, best of 3: 869 ms per loop
>>> %timeit big_np.tolist()
1 loop, best of 3: 306 ms per loop
這意味着,朴素的list(a)
版本比特殊功能tolist()
慢約3
tolist()
。
但是,將其與內置array
-module的性能進行比較:
>>> import array
>>> big_arr=array.array('i', big_np)
>>> %timeit list(big_arr)
1 loop, best of 3: 312 ms per loop
我們可以看到,也許應該說list(a)
慢而不是tolist()
快,因為array.array
和特殊函數一樣快。
另一個觀察結果: array.array
-module和tolist
受益於small-integer-pool (即,當值在[-5, 256]
tolist
[-5, 256]
范圍內時),但是list(a)
並非如此:
##only small integers:
>>> small_np=np.random.randint(1,250, (10**7,))
>>> small_arr=array.array('i', small_np)
>>> %timeit list(small_np)
1 loop, best of 3: 873 ms per loop
>>> %timeit small_np.tolist()
10 loops, best of 3: 188 ms per loop
>>> %timeit list(small_arr)
10 loops, best of 3: 150 ms per loop
我們可以看到,較快的版本比以前快2倍左右,但較慢的版本和以前一樣慢。
我的問題:與list(numpy.array)
相比, list(array.array)
速度降低了多少?
編輯:
再觀察一下,對於Python2.7,如果整數更大(即不能由int32
保持),則需要更長的時間:
>>> very_big=np.random.randint(1,10**7,(10**7,))+10**17
>>> not_so_big=np.random.randint(1,10**7,(10**7,))+10**9
>>> %timeit very_big.tolist()
1 loop, best of 3: 627 ms per loop
>>> %timeit not_so_big.tolist()
1 loop, best of 3: 302 ms per loop
但仍比緩慢的列表版本更快。
這是部分答案,解釋您對小型整數池的觀察:
>>> a = np.arange(10)
>>> type(list(a)[0])
<class 'numpy.int64'>
>>> type(a.tolist()[0])
<class 'int'>
正如我們所看到的, tolist
似乎嘗試創建本機python類型的元素,而數組迭代器(列表構造函數使用的數組)不會打擾。
確實, tolist
的C實現( 在此處提供源代碼)使用PyArray_GETITEM
,它等效於Python arr[index].item()
,而不是-可能有人假設arr[index].item()
arr[index]
基本上,Paul Panzer的答案解釋了會發生的情況:在慢list(...)
版本中,列表的結果元素不是python-integers,而是numpy- numpy.int64
,例如numpy.int64
。 這個答案只說明一點點,並連接點。
我沒有進行系統的分析,但是當在調試器中停止運行時,每次兩個版本都在創建整數對象的例程中,因此很可能這是執行時間的主要部分,並且開銷並不重要。
list(..)
version迭代器調用array_item
,它對一維數組有特殊處理,並調用PyArray_Scalar
,這是一個非常通用的函數,並且不使用Pythons-integer-creation的機制。 它恰好比Python版本慢,對於小值也沒有整數池。
.tolist()
版本調用recursive_tolist
,該版本最終使用Python的PyLong_FromLong(long)
,它顯示了所有觀察到的行為,並且碰巧比numpy功能更快(可能是因為這不是使用numpy的正常方式,沒有很多優化措施)完成)。
與Python3相比,Python2的差別很小:Python2具有兩種不同的整數類別:一類效率更高,適用於32位以下的數字,另一類適用於任意大數-因此,對於較大的數字,最通用的(因此更多昂貴)的路徑必須走-這也是可以觀察到的。
使用list(something)
構造一個列表會迭代某些內容,並將迭代結果收集到一個新列表中。
如果list(small_np)
比list(small_arr)
慢,則可以假定對small_np
迭代要比對small_arr
迭代慢。 讓我們驗證一下:
%timeit for i in small_np: pass # 1 loop, best of 3: 916 ms per loop
%timeit for i in small_arr: pass # 1 loop, best of 3: 261 ms per loop
是的,迭代一個numpy數組似乎比較慢。 這是我必須開始推測的地方。 numpy數組很靈活。 它們可以具有任意數量的尺寸,並具有不同的跨度。 數組數組總是平坦的。 這種靈活性可能需要付出一定的代價,這體現在更復雜的迭代中。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.