为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

动漫人物 2021-06-04 18:50:57
我不确定这是否与其他 PyPy 内存问题重复,但在这里我将提供一个具体示例。from __future__ import divisiondef 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 x1M = 1000000009L = 10**8bin2 = [0] * Lbin2[0] = 1for 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 回答

?
呼如林

TA贡献1798条经验 获得超3个赞

糟糕,这是优化整数列表的一个糟糕案例。问题是它以整数列表开始:


bin2 = [0] * L

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


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


如果我们像这样初始化列表,我们可以检查问题是否不会发生,以便它确实包含 1 亿次相同的对象:


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

bin2[0] = 1

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


bin2 = [1]

并使用bin2.append(). 这让程序启动得更快,而且在开始时不会占用大量内存。


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


查看完整回答
反对 回复 2021-06-09
?
芜湖不芜

TA贡献1796条经验 获得超7个赞

AFAICT 这里的问题是您将 long 分配给数组,尽管您是模数,但 PyPy 似乎没有注意到该数字仍然适合机器字。

我可以想到两种方法来解决这个问题:

  1. 通过分配给该值bin2[n+1]通过int()

  2. 使用array.array().

前者只影响 PyPy2,并导致在我的 Mac 上似乎稳定的内存占用为 ~800MB,而后者似乎稳定在 ~1.4GB,无论我是在 PyPy2 还是 PyPy3 中运行它。

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


查看完整回答
反对 回复 2021-06-09
  • 2 回答
  • 0 关注
  • 208 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信