【问题标题】:Python Multiprocessing Runaway MemoryPython 多处理失控内存
【发布时间】:2019-12-21 12:40:59
【问题描述】:

我有一个昂贵的函数可以在许多并行的独立对象上执行,所以我正在尝试使用多处理模块。但是,内存消耗似乎处于失控的上升轨道上。请参阅下面的附图。

基本上我有一个大型二进制对象的路径列表。我有一个用这个列表实例化的类。在这个类的__iter__ 方法中,我从磁盘读取文件并生成它。这个想法是我遍历这个对象列表(它将文件读入内存)并执行一些昂贵的操作。下面是一些示例代码来模拟这一点。我正在使用np.random.rand(100,100) 来模拟将大文件读入内存,如果模拟昂贵函数中的矩阵,我只是索引 [0,0] 元素。

import numpy as np
from pathos.multiprocessing import ProcessingPool as Pool
from memory_profiler import profile

class MyClass:
    def __init__(self, my_list):
        self.name = 'foo'
        self.my_list = my_list

    def __iter__(self):
        for item in self.my_list:
            yield np.random.rand(100,100)

def expensive_function(foo):
    foo[0,0]

my_list = range(100000)
myclass = MyClass(my_list)

iter(myclass) # should not return anything

p = Pool(processes=4, maxtasksperchild=50)
p.map(expensive_function, iter(myclass), chunksize=100)

问题可以在情节中看到。内存消耗似乎在不断攀升。我预计总内存消耗约为每个子进程消耗的 4 倍,但情况似乎并非如此。

是什么导致了这种失控的内存使用,我该如何解决?

【问题讨论】:

  • 我认为最简单的修复方法就是传入一个np.random.rand(100, 100)。因为所有子进程都会有一个副本,所以你不需要在主进程中也复制它。跨度>
  • 顺便说一句如何绘制这个情节?看起来不错。
  • @Sraw 这样做的含义是,当意图可能是使用不同的实现时,所有进程都使用相同的 100x100 元素的随机实现。但是我认为解决方案是在每个子进程而不是在父进程中生成这些元素,可选地使用共享内存数组(每个子进程 1 个)来最小化内存分配
  • @Michael 是的,你说得非常对 :) 如果可以从子进程构造数组,那将是最好的。
  • 我是pathos 作者。请注意,使用closejoinclear 将关闭池、释放进程并从池单例字典中删除池对象。如果您多次调用 map 函数,这有助于内存管理。

标签: python multiprocessing python-multiprocessing pathos


【解决方案1】:

每次孩子开始调用expensive_function,它都会从MyClass.__iter__ 接收一个新的np.random.rand(100,100) 数组。这些数组在主进程中持续存在,因此内存使用量当然会继续增长;子进程无法清理这些,它们存在于父进程中。请注意峰值略低于 8 GiB,或者您应该期望生成多少数据(100000 个数组,具有 100x100 个条目,每个条目 8 个字节)

【讨论】:

  • 感谢您的回复。我有几个想法。首先,不清楚这是“关于你应该期待多少数据”。为什么每个孩子都会收到一个新数组并将其保存在内存中?该数组未分配给任何变量。此外,我对maxtasksperchild 参数的理解(可能是错误的)是它强制池关闭一个孩子并在 X 任务之后启动一个新的。在这种情况下,我希望一次最多将 50 x 4 数组保存在内存中。但这又回到了第一个问题——为什么这些数组首先会保存在内存中?
  • 本意是将一个数组读入内存,对其进行操作,丢弃,然后读取下一个。这似乎是一个微不足道的并行问题,而多进程的默认行为似乎不适合这种情况。
  • 数组被隐式赋值给一个变量,只是你没有自己命名。重要的是每次调用 MyClass.__iter__ 时都会创建一个 80 kiB 的对象,然后将其复制给每个孩子。子进程不是持久内存,父进程是;您的代码要求 100k 数组,这是 numpy 尽职尽责地提供的。这只是一个实现问题,看看multiprocessing's shared memory tools;分配一些共享内存数组,然后填充 + 生成它们而不是创建新数组
猜你喜欢
  • 2014-11-06
  • 2013-01-22
  • 1970-01-01
  • 1970-01-01
  • 2015-03-15
  • 2021-12-03
  • 2014-10-15
  • 2016-08-10
  • 2019-08-11
相关资源
最近更新 更多