我能否找到导致Python MemoryError的分配请求?

17

背景

我的小型Python脚本使用一个库处理一些相对较大的数据。这个任务的标准算法是动态规划算法,因此推测该库“在幕后”分配了一个大数组来跟踪DP的部分结果。当我尝试提供相当大的输入时,它立即报告一个MemoryError

最好不要深入挖掘库的底层,我想弄清楚是否值得在具有更多内存的不同机器上尝试此算法,或者尝试在输入大小上稍微减少一些,或者如果我正在尝试使用的数据大小已经无望了。

问题

当我的Python代码抛出MemoryError时,是否有一种“自上而下”的方式可以调查我的代码尝试分配的内存大小,从而导致错误,例如通过检查错误对象?


5
这是一个关于MemoryError的很好的概述:https://airbrake.io/blog/python-exception-handling/memoryerror。您使用的DP库是什么?非常大的输入大小是多少?类似于博客文章中强制超出边界的情况,您可以尝试循环遍历并基于N分配内存,直到失败为止。这将告诉您N何时失效。至于您的直接问题“如何调查代码尝试分配的内存大小导致了错误”,我没有立即看到任何明显的东西。有趣的问题! - Scott Skiles
@ScottSkiles,目前我的实际问题已经得到了近似/概率解决方案,对于Python中的错误对象仅是我的好奇心。背景只是为了明确为什么有人会关心这个问题,而且大部分与实际问题并不相关。该算法用于计算适用于近似子字符串匹配的Levenshtein距离变体,而我的数据(如果我记得正确)约为一百万个字符。 - Mees de Vries
1
从@ScottSkiles引用的文章中可以看出,你可以使用psutil.virtual_memory()来获取你所需要的内存使用数据。话虽如此,根据你的问题,我不知道有没有一种方法可以从错误本身获取这些信息。 - benvc
@ScottSkiles @benvc 如果你们中的任何一个人能够将关于psutil的事实转化为答案,我会很高兴接受并授予奖励。 - Mees de Vries
@benvc 请继续。我正在旅行中。 - Scott Skiles
它可能在事后没有用处,并且不使用错误对象,但我经常使用objgraph来确定某些东西是否创建了大量对象以及它们消耗了多少内存。 - sheridp
3个回答

4
您可以使用 Pyampler 查看内存分配情况,但是您需要在使用的库中本地添加调试语句。假设是标准的 PyPi 包,以下是步骤:
  1. 克隆包到本地。

2 使用 Pyampler 的 summary 模块。将下面的代码放入主递归方法中:

   from pympler import summary
   def data_intensive_method(data_xyz)
       sum1 = summary.summarize(all_objects)
       summary.print_(sum1)
       ...
  1. 运行 pip install -e . 将编辑后的软件包安装在本地。
  2. 运行主程序并检查控制台,以获取每个迭代的内存使用情况。

4
你无法从MemoryError异常中看出情况,该异常会在任何内存分配失败的情况下抛出,包括与创建新Python数据结构的代码没有直接连接的Python内部;一些模块创建锁或其他支持对象,这些操作可能由于内存耗尽而失败。
你也无法确切知道需要多少内存才能使整个操作成功。如果库在操作过程中创建了多个数据结构,试图为用作字典键的字符串分配内存可能是最后一根稻草,或者它可能正在复制整个现有数据结构进行突变,或者介于两者之间,但这并不意味着还需要多少内存来完成其余的进程。
话虽如此,Python可以提供有关正在进行的内存分配以及何时何地进行的详细信息,使用tracemalloc模块。利用该模块和实验方法,您可以估计完成您的数据集需要多少内存。
关键是找到可以完成过程的数据集。您需要找到不同大小的数据集,然后可以测量这些数据结构所需的内存量。您可以使用tracemalloc.take_snapshot()创建快照,然后比较这些数据集的快照之间的差异和统计信息,也许您可以从这些信息中推断出更大的数据集需要多少内存。当然,这取决于操作和数据集的性质,但如果有任何模式,tracemalloc是发现它的最佳选择。

2

看起来MemoryError没有与任何相关数据一起创建:

def crash():
    x = 32 * 10 ** 9
    return 'a' * x

try:
    crash()
except MemoryError as e:
    print(vars(e))  # prints: {}

这很有道理 - 如果没有剩余内存,怎么可能呢?
我认为没有简单的解决方法。您可以从MemoryError引起的回溯开始,并使用调试器进行调查,或者使用类似pympler(或评论中建议的psutil)的内存分析器。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接