【发布时间】: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