猿问

当没有分配任何新内容时,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都被垃圾收集的情况下,没有其他东西会增加内存使用量。


动漫人物
浏览 209回答 2
2回答

呼如林

糟糕,这是优化整数列表的一个糟糕案例。问题是它以整数列表开始:bin2 = [0] * L这在内部存储为整数数组。它通常更紧凑,即使在这种情况下它不会改变任何东西——因为在 CPython 上它是一个包含L相同 object 副本的列表0。但问题是很快,我们将 a 存储long在列表中。此时,我们需要将整个列表变成可以存储任何内容的泛型类型。但!问题是我们看到了 1 亿个零,因此我们创建了 1 亿个0对象。除了列表本身的 800MB 之外,这会立即产生 3 GB 的内存压力。如果我们像这样初始化列表,我们可以检查问题是否不会发生,以便它确实包含 1 亿次相同的对象:bin2 = [0L] * L&nbsp; &nbsp; &nbsp;# Python 2.xbin2[0] = 1也就是说,在您的示例中,您不需要列表首先包含 1 亿个元素。您可以将其初始化为:bin2 = [1]并使用bin2.append(). 这让程序启动得更快,而且在开始时不会占用大量内存。请注意,PyPy3 仍然比 CPython3 使用更多的内存。

芜湖不芜

AFAICT 这里的问题是您将 long 分配给数组,尽管您是模数,但 PyPy 似乎没有注意到该数字仍然适合机器字。我可以想到两种方法来解决这个问题:通过分配给该值bin2[n+1]通过int()。使用array.array().前者只影响 PyPy2,并导致在我的 Mac 上似乎稳定的内存占用为 ~800MB,而后者似乎稳定在 ~1.4GB,无论我是在 PyPy2 还是 PyPy3 中运行它。不过,我还没有完全运行该程序,所以 YMMV ......
随时随地看视频慕课网APP

相关分类

Python
我要回答