没有分配新内容时,Pypy内存使用量会增加

时间:2018-09-04 06:35:12

标签: python memory out-of-memory pypy

我不确定这是否与其他PyPy记忆问题重复,但是在这里我将提供一个具体示例。

from __future__ import division

def mul_inv(a, m):
    """Modular multiplicative inverse, a^-1 mod m. Credit: rosettacode.org"""
    m0 = m
    x0, x1 = 0, 1
    if m == 1: return 1
    while a > 1:
        assert m != 0, "a and m must be coprime"
        q = a // m
        a, m = m, a%m
        x0, x1 = x1 - q * x0, x0
    if x1 < 0: x1 += m0
    return x1


M = 1000000009
L = 10**8

bin2 = [0] * L
bin2[0] = 1
for n in range(L-1):
    bin2[n+1] = (bin2[n] * (4*n + 2) * mul_inv(n+1, M)) % M
    if n % 10**5 == 0: print(n, bin2[n])

print(bin2[:20])

使用python 3.6时,该程序最多使用3-4 GB并运行到完成为止(Armin Rigo的列表更改并未对此做出重大更改)。使用运行PyPy 5.10.0的python 2.7.13,程序会迅速达到8 GB(我有多少RAM)并冻结。即使gc.collect()被调用,当n约为3.5 * 10 ^ 7时,程序也会用尽内存。

此内存使用量来自哪里?唯一大的内存使用情况应该是将bin2初始化为10 ^ 8 int列表。假设mul_inv中的所有局部变量都是垃圾回收的,那么没有什么可以增加内存使用了。

2 个答案:

答案 0 :(得分:1)

AFAICT这里的问题是,您正在为数组分配long数,尽管有了模数,PyPy似乎并未注意到该数字仍然适合机器字。

我可以想到两种解决方法:

  1. 通过bin2[n+1]传递分配给int()的值。
  2. 使用array.array()

前者仅影响PyPy2,在我的Mac上似乎可以稳定地占用约800MB的内存,而后者无论在PyPy2还是PyPy3中运行,都可以稳定在约1.4GB。

>

不过,我还没有完全运行程序,所以YMMV…

答案 1 :(得分:0)

糟糕,这是整数列表优化的糟糕情况。问题是,这始于int列表:

bin2 = [0] * L

这在内部存储为一个整数数组。它通常要紧凑得多,即使在这种情况下它也不会改变任何东西-因为在CPython上,它是一个包含L个相同对象0的副本的列表。

但是问题是很快,我们在列表中存储了long。此时,我们需要将整个列表转换为可以存储任何内容的通用类型。但!问题是我们看到1亿个零,因此我们创建了1亿个0对象。除了列表本身有800MB的存储空间外,这会立即造成3GB的存储压力。

如果我们像这样初始化列表,我们可以检查是否不会出现问题,以便它实际上包含同一对象的1亿倍:

bin2 = [0L] * L     # Python 2.x
bin2[0] = 1

也就是说,在您的示例中,您不需要列表首先包含1亿个元素。您可以将其初始化为:

bin2 = [1]

,然后使用bin2.append()。这样一来,程序的启动速度将大大提高,并且开始时不会占用大量内存。

请注意,PyPy3仍比CPython3使用更多的内存。

相关问题