【问题标题】:Iterating the results of a multiprocessing list is consuming large amounts of memory迭代多处理列表的结果会消耗大量内存
【发布时间】:2019-05-05 08:30:53
【问题描述】:

我有一个很大的清单。我想处理每个项目。我想对列表进行分段并在不同的 CPU 上处理每个段。我正在使用 pathos 多处理库。我创建了以下函数:

def map_list_in_segments (l, f):
  cpus = max(1, int(cpu_count() / 2) - 1)
  seg_length = int(len(l) / cpus)
  segments = [l[x:x+seg_length] for x in range(0,len(l),seg_length)]
  pool = Pool(nodes=cpus)
  mapped_segments = pool.map(lambda seg: f(seg), segments)
  return (sg for seg in mapped_segments for sg in seg)

它返回正确的结果并使用所有(或几乎所有)CPU。但是,遍历返回的列表会导致意外消耗大量内存。

起初我返回的是一个列表理解。我将其切换为生成器,希望减少内存消耗,但这并没有改善任何东西。

基于 cmets 的更新:

我不知道 imap 和 uimap 并且它们会自动分块输入列表。我尝试了uimap,但发现 CPU 利用率非常低且运行时间非常长。但是,其中一个进程的 CPU 利用率非常高。我认为正在发生的是有很多酸洗正在进行。我传入的f 在闭包中有一个大对象。使用 ProcessingPool 方法(map、imap、uimap)时,需要为列表中的每个元素腌制此对象。我怀疑这是一个非常繁忙的进程正在做的事情。此酸洗会限制其他进程。

如果是这样,这就解释了为什么我的手动分段会显着提高 CPU 利用率:大型对象只需要在每个分段而不是每个项目上腌制一次。

然后我尝试在我的map_list_in_segments 中使用uimap,希望减少内存消耗,但这并没有发生。以下是调用该方法并迭代结果的代码的外观:

segments = multiprocessing.map_list_in_segments(l, lambda seg: process_segment(seg, large_object_needed_for_processing))
for seg in segments:
  for item in seg:
    # do something with item

我对生成器的(有限)理解是,第一个循环遍历段的for 循环应该在迭代时从内存中释放每个段。如果是这样,那么大内存使用似乎是process_segment 方法的返回值的酸洗。我没有返回大量数据(每个项目大约 1K 字节),我正在使用的 l 的大小是 6000 个项目。不知道为什么会消耗 5GB 内存。

【问题讨论】:

  • mapped_segments 已具体化为列表,因为您使用了Pool.map,请考虑使用Pool.imap 或Pool.imap_unordered
  • 另外,你确定这工作正常吗? map 已经在对你的数据进行分块了,为什么还要手动分块呢?
  • Pool.map() 有一个可选的chunksize 参数,它将为您切分可迭代。尝试使用它而不是自己做(并调整其值以更改一次使用的内存量)。
  • @martineau chunksize 不必指定,AFAIK,它会为你猜测
  • @juanpa:是的,我知道这一点——但我建议明确指定它。在这种情况下,为了控制内存使用。

标签: python for-loop multiprocessing iteration


【解决方案1】:

multiprocessing 的问题是进程之间的通信成本很高。如果您的结果在大小上与您的输入相同,那么您可能会花费大部分时间来酸洗和取消酸洗数据,而不是做任何有用的事情。这取决于f 的价格,但最好不要在这里使用multiprocessing。

【讨论】:

  • f 相当昂贵。通过使用multiprocessing,我大大减少了运行时间。我已经用额外的解释/见解更新了这篇文章。我相信问题在于事情被腌制/解开的方式。不确定是否有办法分批对结果进行解酸,这样我就不会消耗服务器上的所有可用内存。
【解决方案2】:

一些进一步的测试表明酸洗不是问题。我在for item in seg 中所做的处理是构建消耗大量内存的额外对象。

从这次练习中得出的见解和聪明的评论者:

  1. ProcessPool 方法(map、imap、uimap)会自动对列表进行分块。
  2. 如果您将大型对象传递给 f(通过闭包),您可能会发现手动分块列表(如上)可以节省大量酸洗并提高 CPU 利用率。
  3. 使用imap 和uimap 可以显着减少内存使用量。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-01-04
    • 2020-11-18
    • 2012-12-28
    • 1970-01-01
    • 2012-11-14
    • 2012-11-06
    • 1970-01-01
    • 2022-01-16
    相关资源
    最近更新 更多