2015-04-23 86 views
2

我將改進我的代碼片段的性能,該代碼片段將經常以遞歸方式獲取子數組。爲什麼Numpy.array比獲取子列表的內建列表慢

所以我用numpy.array代替了內建列表。因爲,據我所知,在獲取子數組時,numpy.array不會複製原始列表。

但是當我改成numpy.array時,性能變差了。所以我想知道原因。謝謝!

以下是我的代碼片段,通過使用不同的對象,我得到了執行時間:

import timeit 
stat = ''' 
import numpy 
def func(a): 
    a[len(a)-1] += 1 
    if len(a) == 1: 
     return a[0] 
    else: 
     return func(a[1:len(a)]) 
a1=[1,2,3,4,5,6,7,8,9,10] 
a2=numpy.array([1,2,3,4,5,6,7,8,9,10]) 
''' 
if __name__ == "__main__": 
    print "Execution time with build-in list: {0}".format(timeit.timeit('func(a1)', setup = stat, number = 1000)) 
    print "Execution time with Numpy array: {0}".format(timeit.timeit('func(a2)', setup = stat, number = 1000)) 

而且在我的64位MAC(Python的2.7.6 + NumPy的1.8.0rc1)輸出:

Execution time with build-in list: 0.00507998466492 
Execution time with Numpy array: 0.0195469856262 
+0

它可能不會複製原始列表,但您仍然需要創建一個新的numpy數組,其中包含原始引用,這顯然比創建新列表更加昂貴。你可以嘗試用一個*很大的列表/數組來測試它。 – chepner

+0

以chepner爲基礎,隨着數組大小的增加,時間如何變化?如果他的假設是正確的,那麼列表運行時不會發生顯着變化,但數組運行時會線性增加。 – WakkaDojo

+0

@chepner看來你是對的。我測試和numpy陣列比內置列表大陣列更快。但是你能解釋爲什麼創建一個新的numpy數組引用比創建一個新列表顯然更昂貴嗎? – AngelIW

回答

0

感謝所有人的回答和對這個問題的評論,你已經顯示出寶貴的信息給我做出這個答案。

答案是這樣的。導致numpy數組在我的問題中表現不佳的原因是,訪問單個項目並在numpy數組上分配內置類型比在內置列表上慢。實際上,獲取numpy數組的子數組的性能比內置列表的性能增益確實存在。但是短陣列中的增益太小,例如在我的例子中len = 10的數組,所以小增益被丟失的是這行:a[len(a)-1] += 1其中我們訪問了單個項目並在內置類型int之間轉換。


下面的代碼證明了原因:

import numpy 
from timeit import timeit 
stat = ''' 
import numpy 
a1 = range(4000) 
a2 = numpy.array(a1) 
i = 0 
''' 
if __name__ == "__main__": 
    test_times = 1000 
    print '1. {0:.8f}'.format(timeit('a1[i]', setup = stat, number = test_times)) 
    print '2. {0:.8f}'.format(timeit('a2[i]', setup = stat, number = test_times)) 
    print '3. {0:.8f}'.format(timeit('i += a1[i]; ++i', setup = stat, number = test_times)) 
    print '4. {0:.8f}'.format(timeit('i += a2[i]; ++i', setup = stat, number = test_times)) 
    print '5. {0:.8f}'.format(timeit('a = a1[i:len(a1)]; ++i', setup = stat, number = test_times)) 
    print '6. {0:.8f}'.format(timeit('a = a2[i:len(a2)]; ++i', setup = stat, number = test_times)) 

運行結果在我的Mac是以下幾點:

1. 0.00005913 
2. 0.00017881 
3. 0.00008607 
4. 0.00084305 
5. 0.01492000 
6. 0.00053406 

我們可以從以上結果得到這些:

  1. 1對2:訪問單個項目比內置列表時numpy的陣列較慢。
  2. 2比4:似乎需要數據轉換,這花費額外的時間,當在numpy的陣列內建類型(INT)項的分配數據。對於內置列表,花費的時間非常少。
  3. 5比6:numpy的取子陣列內置列表進行比較時,真正得救的時間。
1

,如果你修改的代碼的最後兩行,你會得到相同的執行時間如下:

print "Execution time with build-in list: {0}".format(timeit.timeit(
    'func(a1)', setup = stat, number = 1000), 'gc.enable()') 
print "Execution time with Numpy array: {0}".format(timeit.timeit(
    'func(a2)', setup = stat, number = 1000), 'gc.enable()') 

在這兩種情況下,我們都允許 timeit開啓所謂的垃圾回收,即當內存不再使用時釋放內存的過程。上述修改返回,例如:

Execution time with build-in list: 0.00580596923828 
Execution time with Numpy array: 0.00822710990906 

具有相同的數量級。根據文檔 timeit「默認情況下,它暫時關閉垃圾收集時間。這種方法的優點是它使獨立的時間更具可比性,這個缺點是垃圾收集可能是性能的重要組成部分被測量的功能「。

有一個很好的理解什麼方法,即有或沒有垃圾回收,應該使用什麼時候,什麼時候使用。另請注意,如果您應用時間模塊中的time.time()塊,您將獲得更長的時間。

+0

你能解釋這兩個關於你的答案的問題嗎? – AngelIW

+0

1.你能解釋一下爲什麼GC可能會影響到消費的時間和他們花同時應用GC後的原因是什麼? 2.爲什麼time.time()會獲得更長的時間? – AngelIW

相關問題