如何在Python中并行化生成器/迭代器管道?

时间:2011-04-16 06:49:26

标签: python iterator parallel-processing pipeline

假设我有一些Python代码,如下所示:

input = open("input.txt")
x = (process_line(line) for line in input)
y = (process_item(item) for item in x)
z = (generate_output_line(item) + "\n" for item in y)
output = open("output.txt", "w")
output.writelines(z)

此代码从输入文件中读取每一行,通过多个函数运行它,并将输出写入输出文件。现在知道函数process_lineprocess_itemgenerate_output_line永远不会相互干扰,让我们假设输入和输出文件是分开的磁盘,这样读写就不会互相干扰。

但Python可能不知道这些。我的理解是Python将读取一行,依次应用每个函数,并将结果写入输出,然后在将第一行发送到输出后仅读取的第二行,所以在第一行退出之前,第二行不会进入管道。我是否正确理解该程序将如何流动?如果这是它的工作方式,是否有任何简单的方法可以使多个行同时在管道中,以便程序可以并行读取,写入和处理每个步骤?

2 个答案:

答案 0 :(得分:5)

您无法真正并行化读取或写入文件;这些将是你的瓶颈,最终。您是否确定这里的瓶颈是CPU,而不是I / O?

由于您的处理不包含任何依赖项(根据您),因此使用Python's multiprocessing.Pool class非常简单。

有几种方法可以写这个,但更容易w.r.t.调试是为了找到独立的关键路径(代码的最慢部分),我们将使它们并行运行。我们假设它是process_item。

......实际上就是这样。代码:

import multiprocessing.Pool

p = multiprocessing.Pool() # use all available CPUs

input = open("input.txt")
x = (process_line(line) for line in input)
y = p.imap(process_item, x)
z = (generate_output_line(item) + "\n" for item in y)
output = open("output.txt", "w")
output.writelines(z)

我没有测试过,但这是基本的想法。 Pool的imap方法确保以正确的顺序返回结果。

答案 1 :(得分:1)

<块引用>

有没有什么简单的方法可以让多行同时在管道中

我编写了一个库来做到这一点:https://github.com/michalc/threaded-buffered-pipeline,它在单独的线程中迭代每个可迭代对象。

那是什么

input = open("input.txt")

x = (process_line(line) for line in input)
y = (process_item(item) for item in x)
z = (generate_output_line(item) + "\n" for item in y)

output = open("output.txt", "w")
output.writelines(z)

变成

from threaded_buffered_pipeline import buffered_pipeline

input = open("input.txt")

buffer_iterable = buffered_pipeline()
x = buffer_iterable((process_line(line) for line in input))
y = buffer_iterable((process_item(item) for item in x))
z = buffer_iterable((generate_output_line(item) + "\n" for item in y))

output = open("output.txt", "w")
output.writelines(z)

这增加了多少实际并行度取决于每个迭代中实际发生的事情,以及您拥有多少 CPU 内核/它们有多忙。

经典的例子是 Python GIL:如果每个步骤都占用大量 CPU,并且只使用 Python,那么不会增加太多并行性,这可能不会比串行版本快。另一方面,如果每个都是网络 IO 繁重的,那么我认为它可能会更快。