簡體   English   中英

使用Python列表作為隊列的效率

[英]Efficiency of using a Python list as a queue

一位同事最近編寫了一個程序,其中他使用Python列表作為隊列。 換句話說,他在需要插入項目時使用.append(x) ,在需要刪除項目時使用.pop(0)

我知道Python有collections.deque ,我試圖弄清楚是否花費我(有限)的時間來重寫這段代碼來使用它。 假設我們執行了數以百萬計的追加和流行但從未有超過幾千個條目,他的列表使用是否會成為一個問題?

特別是,Python列表實現使用的底層數組是否會無限增長,有數百萬個點,即使列表只有一千個內容,或者Python最終會重新realloc並釋放一些內存?

一些答案聲稱,當兩者都有1000個條目時,deque vs list-used-as-FIFO的速度優勢為“10x”,但這有點過分了:

$ python -mtimeit -s'q=range(1000)' 'q.append(23); q.pop(0)'
1000000 loops, best of 3: 1.24 usec per loop
$ python -mtimeit -s'import collections; q=collections.deque(range(1000))' 'q.append(23); q.popleft()'
1000000 loops, best of 3: 0.573 usec per loop

python -mtimeit是你的朋友 - 一個非常有用和簡單的微基准測試方法! 有了它,你當然也可以在更小的情況下輕松探索性能:

$ python -mtimeit -s'q=range(100)' 'q.append(23); q.pop(0)'
1000000 loops, best of 3: 0.972 usec per loop
$ python -mtimeit -s'import collections; q=collections.deque(range(100))' 'q.append(23); q.popleft()'
1000000 loops, best of 3: 0.576 usec per loop

(對於12而不是100項btw來說差別不大),而且在更大的那些:

$ python -mtimeit -s'q=range(10000)' 'q.append(23); q.pop(0)'
100000 loops, best of 3: 5.81 usec per loop
$ python -mtimeit -s'import collections; q=collections.deque(range(10000))' 'q.append(23); q.popleft()'
1000000 loops, best of 3: 0.574 usec per loop

你可以看出,deque的O(1)性能聲明是有根據的,而列表的速度是1,000個項目的兩倍,大約10,000個數量級。 您還可以看到,即使在這種情況下,您每次附加/彈出對只會浪費5微秒左右,並決定浪費的重要程度(盡管如果您正在使用該容器執行此操作,則deque沒有任何缺點,因此您即使5或多或少使用不會產生重大影響,也可以切換。

使用list實現不會耗盡內存,但性能會很差。 來自文檔

盡管list對象支持類似的操作,但它們針對快速固定長度操作進行了優化,並導致pop(0)insert(0, v)操作的O(n)內存移動成本,這些操作改變了底層數據表示的大小和位置。

所以使用deque會快得多。

來自Beazley的Python Essential Reference,第四版 ,p。 194:

某些庫模塊提供的新類型在某些任務中優於內置函數。 例如,collections.deque類型提供與列表類似的功能,但已針對兩端的項目插入進行了高度優化。 相反,列表僅在最后附加項目時才有效。 如果您在前面插入項目,則需要移動所有其他元素以騰出空間。 隨着列表變得越來越大,執行此操作所需的時間也會增加。 只是為了讓您了解其中的差異,這里是一個在列表前面插入一百萬個項目和一個雙端隊列的時間測量:

以下是此代碼示例:

>>> from timeit import timeit
>>> timeit('s.appendleft(37)', 'import collections; s = collections.deque()', number=1000000)
0.13162776274638258
>>> timeit('s.insert(0,37)', 's = []', number=1000000)
932.07849908298408

時間來自我的機器。


2012-07-01更新

>>> from timeit import timeit
>>> n = 1024 * 1024
>>> while n > 1:
...     print '-' * 30, n
...     timeit('s.appendleft(37)', 'import collections; s = collections.deque()', number=n)
...     timeit('s.insert(0,37)', 's = []', number=n)
...     n >>= 1
... 
------------------------------ 1048576
0.1239769458770752
799.2552740573883
------------------------------ 524288
0.06924104690551758
148.9747350215912
------------------------------ 262144
0.029170989990234375
35.077512979507446
------------------------------ 131072
0.013737916946411133
9.134140014648438
------------------------------ 65536
0.006711006164550781
1.8818109035491943
------------------------------ 32768
0.00327301025390625
0.48307204246520996
------------------------------ 16384
0.0016388893127441406
0.11021995544433594
------------------------------ 8192
0.0008249282836914062
0.028419017791748047
------------------------------ 4096
0.00044918060302734375
0.00740504264831543
------------------------------ 2048
0.00021195411682128906
0.0021741390228271484
------------------------------ 1024
0.00011205673217773438
0.0006101131439208984
------------------------------ 512
6.198883056640625e-05
0.00021386146545410156
------------------------------ 256
2.9087066650390625e-05
8.797645568847656e-05
------------------------------ 128
1.5974044799804688e-05
3.600120544433594e-05
------------------------------ 64
8.821487426757812e-06
1.9073486328125e-05
------------------------------ 32
5.0067901611328125e-06
1.0013580322265625e-05
------------------------------ 16
3.0994415283203125e-06
5.9604644775390625e-06
------------------------------ 8
3.0994415283203125e-06
5.0067901611328125e-06
------------------------------ 4
3.0994415283203125e-06
4.0531158447265625e-06
------------------------------ 2
2.1457672119140625e-06
2.86102294921875e-06

每個.pop(0)需要N步,因為必須重新組織列表。 所需的內存不會無限增長,只會與所持有的項目一樣大。

我建議使用deque來獲得O(1)追加並從前面彈出。

這聽起來像是一些經驗測試可能是最好的事情 - 二階問題可能會使一種方法在實踐中更好,即使它在理論上並不是更好。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM