Jupyter 笔记本内存管理

2024-01-20

我目前正在 kaggle 中开发 jupyter 笔记本。在我的 numpy 数组上执行所需的转换后,我将其腌制以便可以将其存储在磁盘上。我这样做的原因是为了释放大数组消耗的内存。

pickle 阵列后消耗的内存约为 8.7 GB。

我决定运行 @jan-glx 提供的代码片段here https://stackoverflow.com/a/51046503/12403182,找出哪些变量消耗了我的内存:

import sys

def sizeof_fmt(num, suffix='B'):
    ''' by Fred Cirera,  https://stackoverflow.com/a/1094933/1870254, modified'''
    for unit in ['','Ki','Mi','Gi','Ti','Pi','Ei','Zi']:
        if abs(num) < 1024.0:
            return "%3.1f %s%s" % (num, unit, suffix)
        num /= 1024.0
    return "%.1f %s%s" % (num, 'Yi', suffix)

for name, size in sorted(((name, sys.getsizeof(value)) for name, value in locals().items()),
                         key= lambda x: -x[1])[:10]:
    print("{:>30}: {:>8}".format(name, sizeof_fmt(size)))

执行此步骤后,我注意到数组的大小为 3.3 GB,所有其他变量的大小总和约为 0.1 GB。

我决定删除该数组,并通过执行以下操作来查看是否可以解决问题:

del my_array
gc.collect()

执行此操作后,内存消耗从 8.7 GB 减少到 5.4 GB。这在理论上是有道理的,但仍然没有解释其余的内存被消耗了什么。

我决定继续并重置所有变量,看看这是否会释放内存:

%reset

正如预期的那样,它释放了上面函数中打印出的变量的内存,并且我仍然剩下 5.3 GB 的内存在使用中。

需要注意的一件事是,我在腌制文件本身时注意到内存峰值,因此该过程的摘要如下所示:

  1. 对数组执行操作 -> 内存消耗从大约 1.9 GB 增加到 5.6 GB
  2. pickled 文件 -> 内存消耗从 5.6 GB 增加到约 8.7 GB
  3. 当文件被 pickle 到 15.2 GB 时,内存突然激增,然后又回落到 8.7 GB。
  4. 删除的数组 -> 内存消耗从 8.7 GB 减少到 5.4 GB
  5. 执行重置 -> 内存消耗从 5.4 GB 减少到 5.3 GB

请注意,上述内容大致基于监控 Kaggle 上的内存,可能不准确。 我也检查过这个question https://stackoverflow.com/questions/56126062/how-to-destroy-python-objects-and-free-up-memory但这对我的案子没有帮助。

这会被视为内存泄漏吗?如果是这样,在这种情况下我该怎么办?

EDIT 1:

经过进一步挖掘,我注意到有others https://stackoverflow.com/questions/13871152/why-pickle-eat-memory面临这个问题。这个问题源于酸洗过程,酸洗会在内存中创建一个副本,但由于某种原因,不会释放它。有没有办法在酸洗过程完成后释放内存。

EDIT 2:

从磁盘删除 pickled 文件时,使用:

!rm my_array 

它最终释放了磁盘空间并释放了内存空间。我不知道上面的花絮是否有用,但我决定无论如何都将其包括在内,因为每一点信息都可能有所帮助。


您应该注意一个基本缺点:Python 解释器实际上实际上只能勉强释放内存并将其返回给操作系统 https://realpython.com/python-memory-management/。对于大多数工作负载,您可以假设在解释器进程的生命周期内不会释放内存。然而,解释器可以在内部重用内存。所以从操作系统的角度来看CPython进程的内存消耗确实没有任何帮助。一个相当常见的解决方法是在子进程/工作进程中运行内存密集型作业(通过多重处理 https://docs.python.org/3/library/multiprocessing.html例如)和“仅”将结果返回到主进程。一旦工人死亡,内存实际上就被释放了。

二、使用sys.getsizeof on ndarray可能会产生令人印象深刻的误导。使用ndarray.nbytes相反,请注意,这在处理时也可能会产生误导views https://scipy-cookbook.readthedocs.io/items/ViewsVsCopies.html.

此外,我不完全确定为什么你“pickle”numpy 数组。有更好的工具可以完成这项工作。仅举两个例子:h5py https://www.h5py.org/(经典,基于HDF5 https://en.wikipedia.org/wiki/Hierarchical_Data_Format) and zarr https://zarr.readthedocs.io/en/stable/。这两个库都允许您使用ndarray- 就像直接在磁盘上的对象一样(和压缩) - 基本上消除了酸洗步骤。此外,zarr 还允许您create 压缩的 ndarray- 内存中兼容的数据结构 https://zarr.readthedocs.io/en/stable/tutorial.html#compressors. Must ufunc来自 numpy、scipy 和朋友的 s 会很乐意接受它们作为输入参数。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Jupyter 笔记本内存管理 的相关文章

随机推荐