簡體   English   中英

重用列表切片以獲取長度是否會花費額外的內存?

[英]Does reusing a list slice to get length cost additional memory?

我在這個答案的評論中提出了一些建議。 馬丁·彼得斯(Martijn Pieters)說,我的建議會占用大量內存,他通常是對的,但是我喜歡自己看看事情,所以我嘗試介紹一下。 這是我得到的:

#!/usr/bin/env python
""" interpolate.py """

from memory_profiler import profile

@profile
def interpolate1(alist):
    length = (1 + len(alist)) // 2
    alist[::2] = [0] * length

@profile
def interpolate2(alist):
    length = len(alist[::2])
    alist[::2] = [0] * length

a = []
b = []
for i in range(5, 9):
    print i
    exp = 10**i
    a[:] = range(exp)
    b[:] = range(exp)
    interpolate1(a)
    interpolate2(b)

我看不到切片解決方案的內存成本有任何增量差異,但是有時我看到的是算術解決方案。 exp = 7的結果為例:

7
Filename: interpolate.py

Line #    Mem usage    Increment   Line Contents
================================================
     5    750.1 MiB      0.0 MiB   @profile
     6                             def interpolate1(alist):
     7    750.1 MiB      0.0 MiB       length = (1 + len(alist)) // 2
     8    826.4 MiB     76.3 MiB       alist[::2] = [0] * length


Filename: interpolate.py

Line #    Mem usage    Increment   Line Contents
================================================
    10    826.4 MiB      0.0 MiB   @profile
    11                             def interpolate2(alist):
    12    826.4 MiB      0.0 MiB       length = len(alist[::2])
    13    826.4 MiB      0.0 MiB       alist[::2] = [0] * length

我嘗試了一些其他的方法來分析,包括運行interpolate2 interpolate1 ,隨機運行秩序,以及更小的名單,但結果相當一致。

我可以假設結果是因為無論哪種方式都為列表切片分配了內存,無論是在賦值的右側還是左側,但是以任何方式對其進行切片,看起來切片的解決方案即使使用算術運算也會中斷解。 我是否正確解釋了這些結果?

是的,將為僅為slice創建的新列表對象保留額外的內存。

但是,查詢長度后,列表對象再次丟棄 您剛剛創建了一個列表對象,只是為了計算一個列表的一半長度。

內存分配相對昂貴,即使您隨后再次丟棄該對象也是如此。 當您正在尋找永久性的內存占用增加時,這就是我所指的成本 無論列表對象可能是瞬態的,您仍然需要為此對象分配內存。

使用timeit比較這兩種方法時,成本立即顯而易見:

>>> import timeit
>>> def calculate(alist):
...     (1 + len(alist)) // 2
... 
>>> def allocate(alist):
...     len(alist[::2])
... 
>>> testlist = range(10**5)
>>> timeit.timeit('f(testlist)', 'from __main__ import testlist, calculate as f', number=10000)
0.003368854522705078
>>> timeit.timeit('f(testlist)', 'from __main__ import testlist, allocate as f', number=10000)
2.7687110900878906

切片僅需創建一個列表對象並跨引用的一半進行復制,但是該操作所花的時間是簡單地從現有列表中計算長度的800倍

注意,我實際上不得不減少timeit重復計數; 默認的100萬次重復將需要額外的4.5分鍾。 我不會等那么久,而直接計算只花了0.18秒。

暫無
暫無

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

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