【问题标题】:Why Does My Package Take up so Much Memory为什么我的包占用这么多内存
【发布时间】:2021-07-13 09:18:48
【问题描述】:

我正在研究Ptera Software,这是一个开源空气动力学求解器。这是我分发的第一个包,我遇到了一些与内存管理相关的问题。

具体来说,导入我的包会占用大量内存。我上次检查时,它占用了大约 136 MB 的 RAM。 PyPI 将包大小列为 118 MB,这似乎也很高。作为参考,NumPy 只有 87 MB。

起初,我以为我可能不小心在包中包含了一些巨大的文件。所以我从 PyPI 下载了每个版本的 tar.gz 文件并解压出来。解压后没有超过 1 MB。

这让我相信我导入需求的方式有问题。我的 REQUIREMENTS.txt 文件如下所示:

matplotlib >= 3.2.2, < 4.0.0
numpy >= 1.18.5, < 2.0.0
pyvista >= 0.29.0, < 1.0.0
scipy >= 1.5, < 2.0
numba >= 0.53, <1.0

也可能是我弄乱了我的 __init__.py 文件。它看起来像这样:

from pterasoftware import aerodynamics
from pterasoftware import airfoils
from pterasoftware import geometry
from pterasoftware import meshing
from pterasoftware import movement
from pterasoftware import operating_point
from pterasoftware import output
from pterasoftware import problems
from pterasoftware import steady_horseshoe_vortex_lattice_method
from pterasoftware import steady_ring_vortex_lattice_method
from pterasoftware import unsteady_ring_vortex_lattice_method

目录结构如下:

├───pterasoftware
│   ├───airfoils
│   │   └───naca0012.dat
│   ├───__init__.py
│   ├───aerodynamics.py
│   ├───geometry.py
│   ├───meshing.py
│   ├───movement.py
│   ├───operating_point.py
│   ├───output.py
│   ├───problems.py
│   ├───steady_horsehoe_vortex_lattice_method.py
│   ├───steady_ring_vortex_lattice_method.py
│   └───unsteady_ring_vortex_lattice_method.py

我知道导入 numpy、matplotlib 和 scipy 等大型包可能会占用大量内存。但是,我知道有很多使用这些资源的包,它们的导入不需要接近 136 MB。我在这里错过了什么?

这是我用来测试导入包时分配的内存的代码:

from memory_profiler import profile


@profile
def find_import_memory_usage():
    import pterasoftware as ps


if __name__ == "__main__":
    find_import_memory_usage()

【问题讨论】:

  • 我真的看不出这里有什么问题。不要忘记 numpy、numba 和许多其他 Python 包的开销很大,因为它们不是原生 Python,而是用 C 编写的。如果您可以分享一些比较数据。
  • PyPI 显示你的包小于 70 KB,这是正常的。安装时,依赖项(包括所有瞬态依赖项)可以在 venv 中轻松累积到 100-200 MB,这也很好。例如,我必须维护一个 300 KB 的项目,该项目拉取 torch 依赖项,该依赖项将近 1 GB。
  • 查看代码,开销可能来自 numba 将很多东西编译到内存中(不过我可能错了)。

标签: python numpy import pip package


【解决方案1】:

Importing a python module takes too much memory。导入模块需要内存来存储字节码(即.pyc 文件)以及存储引用对象的编译形式。

那么,这些内存到底是为了什么而分配的?

我们可以通过运行内存分析器来检查是否为您的包或依赖项分配了内存。我们将首先导入您的包的依赖项,看看它们占用了多少内存。

由于下次您导入这些库时不会分配内存(您可以自己尝试一下),所以当我们导入您的包时,我们只会看到该包的内存使用情况,而不是它的依赖项。

from memory_profiler import profile

@profile
def find_import_memory_usage():
    import matplotlib
    import numpy
    import pyvista
    import scipy
    import numba
    import copy
    import pterasoftware

这给了我(在 Windows 10 上使用 Python 3.7.6):

Line #    Mem usage    Increment  Occurences   Line Contents
============================================================
    10     18.3 MiB     18.3 MiB           1   @profile
    11                                         def find_import_memory_usage():
    12     34.3 MiB     16.0 MiB           1       import matplotlib
    13     34.3 MiB      0.0 MiB           1       import numpy
    14     96.6 MiB     62.2 MiB           1       import pyvista
    15    101.1 MiB      4.6 MiB           1       import scipy
    16    137.3 MiB     36.2 MiB           1       import numba
    17    137.3 MiB      0.0 MiB           1       import copy
    18    174.6 MiB     37.3 MiB           1       import pterasoftware

你的包只使用了 37.3 MiB,这更合理。 您的依赖项使用 119 MiB,其中 Pyvista 的导入成本特别高。并且当使用 numpy 时,它将需要even more memory

您自己的软件包有waysreducememory requirements(有些是以可读性为代价的,有些只是很好的做法,有些在使用即时包时可能根本没有帮助像 Numba 这样的编译器),但是如果你想减少依赖项占用的内存,那么你可能只需要选择不同的,或者,在最坏的情况下,修改它们的代码,将它们拆分成你项目的组件需要,如果它们的其他组件包含大量未使用的开销。

【讨论】:

    最近更新 更多