我遇到了许多“令人尴尬的并行”项目,我想与multiprocessing
模块并行化。但是,它们通常涉及读取大文件(大于2GB),逐行处理,运行基本计算,然后写入结果。使用Python的多处理模块拆分文件并处理文件的最佳方法是什么?是否应使用Queue
中的JoinableQueue
或multiprocessing
?还是Queue
模块本身?或者,我应该使用multiprocessing
将文件映射到一个进程池上吗?我已经尝试了这些方法,但是在逐行分配数据方面的开销是巨大的。我已经使用cat file | process1 --out-file out1 --num-processes 2 | process2 --out-file out2
确定了轻量级管道过滤器设计,它将第一个过程输入的一定百分比直接传递给第二个输入(参见this post),但我想要完全包含在Python中的解决方案。
令人惊讶的是,Python文档没有提出这样做的规范方法(尽管在multiprocessing
文档中有关于编程指南的冗长部分)。
谢谢, 文斯
其他信息:每行的处理时间各不相同。有些问题很快,几乎没有I / O限制,有些是受CPU限制的。 CPU绑定的非依赖任务将从并行化中获得发布,因此即使是低效的方式将数据分配给处理函数仍然有利于挂钟时间。
一个主要的例子是一个脚本,它从行中提取字段,检查各种按位标志,并将具有某些标志的行以全新格式写入新文件。这似乎是一个I / O限制问题,但是当我使用带有管道的廉价并发版本运行它时,速度提高了大约20%。当我使用池和地图运行它,或者在multiprocessing
中排队时,它总是慢100%。
答案 0 :(得分:8)
最好的架构之一已经是Linux操作系统的一部分。不需要特殊的库。
你想要一个“扇出”的设计。
“主”程序创建了许多通过管道连接的子流程。
主程序读取文件,将行写入管道,执行将行处理到适当子进程所需的最小过滤。
每个子进程可能应该是从stdin读取和写入的不同进程的管道。
您不需要队列数据结构,这正是内存中的管道 - 两个并发进程之间的字节队列。
答案 1 :(得分:6)
一种策略是为每个工作人员分配一个偏移,所以如果你有八个工人进程,你就分配数字0到7.工人数字0读取第一个记录处理然后跳过7然后继续处理第8个记录等,第1号工人读取第二条记录然后跳过7并处理第9条记录.........
这个方案有很多优点。无论文件有多大,工作总是均匀分配,同一台机器上的进程将以大致相同的速率处理,并使用相同的缓冲区,因此不会产生任何过多的I / O开销。只要文件没有更新,您就可以重新运行各个线程以从故障中恢复。
答案 2 :(得分:4)
你没有提到你是如何处理这些线条的;可能是最重要的信息。
每条线是否独立?计算是否依赖于下一行之前的一条线?它们必须在块中处理吗?每条线的处理时间有多长?是否存在必须在末尾包含“全部”数据的处理步骤?或者可以抛弃中间结果并保持运行总量?可以通过将文件大小除以线程数来最初拆分文件吗?或者它在处理过程中会增长吗?
如果这些行是独立的并且文件没有增长,那么您需要的唯一协调就是为每个工作人员提供“起始地址”和“长度”;他们可以独立地打开并寻找文件,然后你必须简单地协调他们的结果;也许是等待N个结果回到队列中。
如果这些行不是独立的,答案将在很大程度上取决于文件的结构。
答案 3 :(得分:1)
这在很大程度上取决于您的文件格式。
将它拆分到任何地方是否有意义?或者你需要将它拆分为新线?或者您是否需要确保在对象定义的末尾将其拆分?
您应该在同一个文件上使用多个阅读器,而不是拆分文件,使用os.lseek
跳转到文件的相应部分。
更新:海报补充说他想拆分新线路。然后我提出以下建议:
假设您有4个进程。然后简单的解决方案是os.lseek到文件的0%,25%,50%和75%,并读取字节,直到你点击第一个新行。这是每个流程的起点。您不需要拆分文件来执行此操作,只需在每个进程中的大文件中寻找正确的位置,然后从那里开始阅读。
答案 4 :(得分:1)
我知道你特别询问过Python,但我会鼓励你看一下Hadoop(http://hadoop.apache.org/):它实现了专门设计用来解决这类问题的Map和Reduce算法。
祝你好运答案 5 :(得分:1)
Fredrik Lundh的Some Notes on Tim Bray's Wide Finder Benchmark是一个有趣的读物,关于一个非常相似的用例,有很多好的建议。其他各种作者也实现了相同的东西,有些是从文章中链接的,但你可能想尝试使用谷歌搜索“python wide finder”或其他东西来寻找更多。 (还有一个基于multiprocessing
模块的解决方案,但现在似乎不再可用了)
答案 6 :(得分:0)
如果运行时间很长,则不要让每个进程通过Queue
读取下一行,而是让进程读取多行。这样,开销在几行(例如数千或更多)上摊销。