Python 内存使用:创建一个大集合与合并多个小集合

Python 内存使用:创建一个大集合与合并多个小集合,python,python-2.7,memory,set,Python,Python 2.7,Memory,Set,我使用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个子列表,然后一个接

我使用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
(为范围(10)中的p设置(xrange(p,n,10))
, 需要大约447个MiB。 您可能认为,由于此iterable是一个生成器表达式,因此可以节省内存,但整数是:

“为了速度”,Python为整数对象维护一个内部自由列表。不幸的是,这个自由列表是不朽的,而且大小是无限的

因此,一旦每个集合被实例化,它所消耗的大部分内存就永远不会被释放


返回值
accum_value
也需要大约447个MiB。因此,调用
reduce
大约需要447+447=894 MiB。

有许多误解需要澄清。首先,
xrange
set(xrange(n))
中被设置为
set
之前,不会被转换为列表

reduce
方法的峰值内存来自以下事实:
set.union
返回一个新值,该值是两个结果集的并集。并在内部存储一个额外值,即
累计值
。因此,在该
for
循环的最后一次迭代中:

accum_value = function(accum_value, x)
进入函数的
累计值包含10的90%⁷ 元素,
x
包含10%的元素⁷ 元素,但返回值将需要所有10个元素的空间⁷ 元素。所有这些都需要同时保留空间。只有在
功能
返回后,旧的
累计值
x
的内存才会释放


然而,这还不是结束。峰值内存确实会告诉进程中需要多少内存,但如果集合仍然存在,则不会告诉在操作完成后使用了多少内存

因此,需要一个不同的基准:

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

因此
reduce
的内存使用在执行后下降到778mib;然而,它仍然比更简单的集合构造情况更重要。正如您所看到的,
集合(xrange(n))
不需要太多的内部内存,因为在构建集合后,内存使用量仅减少了9 MiB

最值得注意的是,
reduce
方法不仅速度较慢;结果集也会消耗两倍的内存。这是因为一个集合是由一个哈希表支持的,每当它被认为有太多冲突时,它就会变得更大。您遇到过病理行为,其中一组相同的值导致其中一个占用的内存是另一个的两倍

相关问题:请注意,
set.union
如果参数是一个集合,将使用更多内存,因为它假设公共元素很少,因此将分配两倍所需的内存。
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