迭代多处理列表的结果会消耗大量内存

时间:2018-12-04 00:39:01

标签: python for-loop multiprocessing iteration

我的名单很大。我要处理每个项目。我想细分列表并在不同的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。但是,对返回的列表进行迭代会导致意外地消耗大量内存。

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

基于评论的更新:

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

如果是这样,这说明了为什么我的手动分段会导致CPU使用率显着提高:大型对象每个分段只需要被酸洗一次,而不是每个项目都被酸洗一次。

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

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的内存。

2 个答案:

答案 0 :(得分:0)

multiprocessing的问题在于,进程之间的通信非常昂贵。如果结果的大小等于输入的大小,则可能会花费大部分时间来腌制和取消腌制数据,而不是做任何有用的事情。这取决于f的价格,但是最好不要在这里使用multiprocessing

答案 1 :(得分:0)

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

从此练习中获得的见解和聪明的评论者:

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