簡體   English   中英

內存使用:創建一個大集合與合並許多小集合

[英]Memory usage: creating one big set vs merging many small sets

我使用%memit magic函數來測量內存使用情況:

In [1]: %memit n = pow(10, 7); range(n)
peak memory: 568 MiB, increment: 272 MiB

In [2]: %memit n = pow(10, 7); set(xrange(n))
peak memory: 824 MiB, increment: 447 MiB

好的,所以似乎有一個中間步驟,其中xrange(n)被實例化為完整列表。 但是,如果我將列表分成10個子列表,並將它們逐個聯合起來呢? 這會更有效,對吧?

In [3]: %memit n = pow(10, 7); reduce(set.union, (set(xrange(p, n, 10)) for p in range(10)))
peak memory: 1260 MiB, increment: 897 MiB

嗯,這沒有按預期進行。 為什么reduce方法比set(xrange(n))消耗更多內存?

根據文檔reduce大致相當於:

def reduce(function, iterable, initializer=None):
    it = iter(iterable)
    if initializer is None:
        try:
            initializer = next(it)
        except StopIteration:
            raise TypeError('reduce() of empty sequence with no initial value')
    accum_value = initializer
    for x in it:
        accum_value = function(accum_value, x)
    return accum_value

迭代iterable(set(xrange(p, n, 10)) for p in range(10)) ,需要大約447 MiB。 您可能會認為,由於此iterable是生成器表達式,因此您將節省內存,但整數將保存在內部空閑列表中

“為了速度”,Python維護整數對象的內部空閑列表。 不幸的是,這個免費清單既是不朽的,也是無限的。

因此,一旦每個集都被實例化,它消耗的大部分內存永遠不會被釋放。

返回值accum_value也需要大約447 MiB。 因此, reduce呼叫大約需要447 + 447 = 894 MiB。

有許多誤解需要澄清。 首先, xrange在被setset(xrange(n))之前不會變成列表!

reduce方法的峰值內存來自set.union返回一個值的事實,該值是2個結果集的並集。 並且reduce內部存儲額外的值,即accum_value 所以在for循環的最后一次迭代中:

accum_value = function(accum_value, x)

進入函數的accum_value包含90%的accum_value元素, x包含10%的10⁷元素,但返回值將需要所有10⁷元素的空間。 所有這些空間需要同時保留。 只有在function返回后accum_value釋放舊的accum_valuex的內存。


然而,這還不是結束。 峰值內存並告訴了多少內存所需的過程 ,但不能是多少在使用所完成的操作,如果集顯仍然存在。

因此需要一個不同的基准:

In [1]: %memit n = pow(10, 7); result = set(xrange(n));
peak memory: 522.22 MiB, increment: 498.93 MiB
In [2]: %memit 42
peak memory: 513.83 MiB, increment: 0.12 MiB
In [3]: import sys
In [4]: sys.getsizeof(result)
Out[4]: 268435688

In [1]: %memit n = pow(10, 7); result = reduce(set.union, (set(xrange(p, n, 10)) for p in range(10)));
peak memory: 1063.20 MiB, increment: 1039.71 MiB
In [2]: %memit 42
peak memory: 779.77 MiB, increment: 0.00 MiB
In [3]: import sys    
In [4]: sys.getsizeof(result)
Out[4]: 536871144
In [5]: 536871144.0 / 268435688
Out[5]: 1.9999991357333977

因此,執行后, reduce的內存使用量降至 778 MiB; 然而,它仍然不僅僅是更簡單的集合構造案例。 正如您所看到的, set(xrange(n))不需要太多的內部存儲器,因為在構造集合之后內存使用量僅下降了9 MiB。

最值得注意的是, reduce方法不僅更慢; 結果集也消耗了兩倍的內存 這是因為一個集合由一個哈希表支持,並且只要認為它有太多的沖突就會變得更大。 你遇到了病態行為,其中一組相同的值導致一個內存占另一個內存的兩倍。

暫無
暫無

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

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