Dmi*_*riy 2 python memory-leaks pytorch
我有一个 python 脚本,它使用开源 pytorch 模型,并且此代码存在内存泄漏。我用 memory_profiler 运行它mprof run --include-children python my_sctipt.py并得到以下图像:
我正在尝试通过系统python模块tracemalloc搜索泄漏的原因:
tracemalloc.start(25)
while True:
...
snap = tracemalloc.take_snapshot()
domain_filter = tracemalloc.DomainFilter(True, 0)
snap = snap.filter_traces([domain_filter])
stats = snap.statistics('lineno', True)
for stat in stats[:10]:
print(stat)
Run Code Online (Sandbox Code Playgroud)
如果仅查看tracemalloc 输出,我将无法识别问题。我认为问题出在 C 扩展中,但是我想确保这是真的。我尝试通过 DomainFilter 更改域,但我仅在 0 域中输出。
另外,我不明白所获得的参数的含义tracemalloc.start(frameno),frameno是最近帧的数量,但是当我更改它时没有任何反应。
接下来我该怎么做才能找到代码中导致内存泄漏的有问题的地方?
期待您的答复。
鉴于您的猜测是问题出在 C 扩展中,但您想确保这是真的,我建议您使用不太特定于 python 的工具来执行此操作,例如 https://github.com/ vmware/chap或至少如果您能够在 Linux 上运行您的程序。
您需要做的是运行脚本(未检测)并在某个时候收集实时核心(例如,使用“gcore pid-of-your-running-program”)。
拥有该核心后,在 chap 中打开该核心(“chap your-core-file-path”)并在 chap 提示符下尝试以下命令:
总结可写
输出将是这样的,但你的数字可能会有很大差异:
chap> summarize writable
5 ranges take 0x2021000 bytes for use: stack
6 ranges take 0x180000 bytes for use: python arena
1 ranges take 0xe1000 bytes for use: libc malloc main arena pages
4 ranges take 0x84000 bytes for use: libc malloc heap
8 ranges take 0x80000 bytes for use: used by module
1 ranges take 0x31000 bytes for use: libc malloc mmapped allocation
4 ranges take 0x30000 bytes for use: unknown
29 writable ranges use 0x23e7000 (37,646,336) bytes.
Run Code Online (Sandbox Code Playgroud)
摘要中的各行按字节使用量的降序排列,因此您可以遵循该顺序。因此,首先看最上面的一个,我们看到使用的是“stack”:
5 ranges take 0x2021000 bytes for use: stack
Run Code Online (Sandbox Code Playgroud)
这个特定的核心适用于一个非常简单的 python 程序,该程序启动 4 个额外的线程并让所有 5 个线程处于休眠状态。多线程 python 程序很容易发生大型堆栈分配的原因是,python 使用 pthreads 来创建附加线程,并且 pthreads 使用 ulimit 值作为堆栈大小的默认值。如果您的程序具有类似的大值,则可以通过多种方式之一更改堆栈大小,包括在父进程中运行“ulimit -s”来更改默认堆栈大小。要查看哪些值真正有意义,您可以在 chap 提示符下使用以下命令:
chap> describe stacks
Thread 1 uses stack block [0x7fffe22bc000, 7fffe22dd000)
current sp: 0x7fffe22daa00
Peak stack usage was 0x7798 bytes out of 0x21000 total.
Thread 2 uses stack block [0x7f51ec07c000, 7f51ec87c000)
current sp: 0x7f51ec87a750
Peak stack usage was 0x2178 bytes out of 0x800000 total.
Thread 3 uses stack block [0x7f51e7800000, 7f51e8000000)
current sp: 0x7f51e7ffe750
Peak stack usage was 0x2178 bytes out of 0x800000 total.
Thread 4 uses stack block [0x7f51e6fff000, 7f51e77ff000)
current sp: 0x7f51e77fd750
Peak stack usage was 0x2178 bytes out of 0x800000 total.
Thread 5 uses stack block [0x7f51e67fe000, 7f51e6ffe000)
current sp: 0x7f51e6ffc750
Peak stack usage was 0x2178 bytes out of 0x800000 total.
5 stacks use 0x2021000 (33,689,600) bytes.
Run Code Online (Sandbox Code Playgroud)
因此,您在上面看到的是,其中 4 个堆栈的大小为 8MiB,但很可能远低于 64KiB。
您的程序可能没有任何堆栈大小问题,但如果是这样,您可以按照上面的描述修复它们。
继续检查增长的原因,查看摘要中的下一行:
6 ranges take 0x180000 bytes for use: python arena
Run Code Online (Sandbox Code Playgroud)
所以 python arenas 使用的内存次之。这些严格用于特定于 python 的分配。因此,如果这个值在你的情况下很大,它就反驳了你关于 C 分配是罪魁祸首的理论,但稍后你可以做更多的事情来弄清楚这些 python 分配是如何使用的。
查看摘要的其余行,我们看到一些“libc”作为“use”描述的一部分:
1 ranges take 0xe1000 bytes for use: libc malloc main arena pages
4 ranges take 0x84000 bytes for use: libc malloc heap
1 ranges take 0x31000 bytes for use: libc malloc mmapped allocation
Run Code Online (Sandbox Code Playgroud)
请注意,libc 负责所有内存,但您无法知道内存是否用于非 python 代码,因为对于超过特定大小阈值(远低于 4K)的分配,python 通过 malloc 获取内存,而不是从一个内存中获取内存蟒蛇竞技场。
因此,假设您已经解决了堆栈使用方面可能遇到的任何问题,并且主要有“python arenas”或“libc malloc”相关的用法。您要了解的下一件事是该内存是否大部分是“已使用”(意味着已分配但从未释放)或“空闲”(意味着“已释放但未返还给操作系统”)。您可以按如下所示执行此操作:
chap> count used
15731 allocations use 0x239388 (2,331,528) bytes.
chap> count free
1563 allocations use 0xb84c8 (754,888) bytes.
Run Code Online (Sandbox Code Playgroud)
因此,在上述情况下,已用分配占主导地位,人们应该做的是尝试了解这些已用分配。自由分配占主导地位的情况要复杂得多,用户指南中对此进行了一些讨论,但在这里讨论会花费太多时间。
因此,现在让我们假设已用分配是您的情况增长的主要原因。我们可以找出为什么我们有这么多已用的分配。
我们可能想知道的第一件事是是否有任何分配实际上是“泄漏”的,即它们不再可访问。这排除了由于基于容器的增长而导致增长的情况。
这样做的方法如下:
chap> summarize leaked
0 allocations use 0x0 (0) bytes.
Run Code Online (Sandbox Code Playgroud)
因此,对于这个特定的核心,就像 python 核心很常见的那样,没有任何泄漏。您的号码可能不为零。如果它不为零,但仍然远低于上面报告的与“python”或“libc”使用的内存相关的总数,您可能只是记下泄漏,但继续寻找增长的真正原因。用户指南有一些有关调查泄漏的信息,但有点稀疏。如果泄漏计数实际上足以解释您的增长问题,您应该下一步进行调查,但如果不是,请继续阅读。
现在您假设基于容器的增长,以下命令很有用:
chap> redirect on
chap> summarize used
Wrote results to scratch/core.python_5_threads.summarize_used
chap> summarize used /sortby bytes
Wrote results to scratch/core.python_5_threads.summarize_used::sortby:bytes
Run Code Online (Sandbox Code Playgroud)
上面创建了两个文本文件,一个具有按对象计数排序的摘要,另一个具有按这些对象直接使用的总字节数排序的摘要。
目前,chap 对 python 的支持非常有限(除了由 libc malloc 分配的任何 python 对象之外,它还找到这些 python 对象,但对于 python 对象,摘要仅根据模式划分了 python 对象的有限类别(例如,%SimplePythonObject 匹配)像“int”,“str”,...这样的东西不保存其他Python对象,而%ContainerPythonObject匹配诸如tuple,list,dict,...之类的东西,它们确实保存对其他Python对象的引用)。话虽如此,从摘要中应该很容易看出已用分配的增长是否主要是由于 python 分配的对象或本机代码分配的对象。
因此,在这种情况下,考虑到您特别想查明增长是否是由本机代码引起的,请在摘要中查找如下所示的计数,所有这些都与 python 相关:
Pattern %SimplePythonObject has 7798 instances taking 0x9e9e8(649,704) bytes.
Pattern %ContainerPythonObject has 7244 instances taking 0xc51a8(807,336) bytes.
Pattern %PyDictKeysObject has 213 instances taking 0xb6730(747,312) bytes.
Run Code Online (Sandbox Code Playgroud)
因此,在我一直使用的核心示例中,Python 分配绝对占主导地位。
您还将看到以下一行,该行用于 chap 尚未识别的分配。您无法假设这些是否与 python 相关。
Unrecognized allocations have 474 instances taking 0x1e9b8(125,368) bytes.
Run Code Online (Sandbox Code Playgroud)
这有望回答您下一步可以做什么的基本问题。至少到那时,您将了解增长是否可能是由于 C 代码或 Python 代码造成的,并且根据您发现的内容,chap 用户指南应该可以帮助您进一步前进。
| 归档时间: |
|
| 查看次数: |
4350 次 |
| 最近记录: |