如何为多个文件处理选择最佳 I/O 策略?
Posted
技术标签:
【中文标题】如何为多个文件处理选择最佳 I/O 策略?【英文标题】:How to choose best I/O strategy for multiple files processing? 【发布时间】:2017-06-26 09:46:08 【问题描述】:假设我们有下一个任务(非常抽象):
我们有一个文件夹,其中包含要处理的不同数量的文件(文件数可能是 1、2 或几千个)。每个文件只能按顺序处理(这意味着不可能在内存中读取整个文件并在多个线程中处理它)。文件处理的结果应该是生成新文件,也是顺序写入的。如何使用所有可用的 CPU 内核?
我只看到两种方法:
使用由多个线程处理的任务队列。每个任务都在处理单个文件,例如从文件中读取块,处理块,将块写入结果文件。
使用类似管道模式的东西。我们有一个输入线程,它以异步方式读取文件并将块发布到多个处理队列。每个线程读取自己的队列并进行块处理。然后将结果发布到输出队列。输出线程写入结果文件。所以我们有1个输入读取线程,1个输出写入线程和几个进程线程。
块处理不是很快的操作,比读取慢。
操作系统:Mac/Linux,也许是 Windows。
哪种方法更好?我们还有其他解决方案吗?
【问题讨论】:
仅供参考 MS Windows:I/O 完成端口:msdn.microsoft.com/en-us/library/windows/desktop/… 在其他操作系统上寻找类似的功能 【参考方案1】:最好的方法是编写一个简单的 Task 类,它独立完成整个操作(读取、处理、写入),因此与外部的线程不安全操作没有任何联系。然后使用任务队列,其中固定数量的线程可以获取这些任务并处理它们。很多线程通常是核心 * 2。
可以从数学上证明,选项 2 将始终等于或慢于基于任务的解决方案,并且在所有情况下都会更加复杂。选项 2 更可行的唯一情况是线程切换成为实际瓶颈时。 IE。如果您的服务器具有类似 1000 个并发但有状态的连接,但只有一个网卡,那么让 1 个网络线程为 1000 个处理线程提供服务会更有效,而不是在通过线路发送的每个字节上唤醒 1000 个线程。
基于任务的解决方案还可以更轻松地测量吞吐量并比较其他线程对其的影响,因为您可以简单地以每秒任务数为单位进行测量。
【讨论】:
【参考方案2】:可能最简单有效的解决方案是使用低于默认优先级的单个读取器线程。如果有空闲的 CPU 内核,它就会运行。这将创建一个工作线程(处理一个输入文件并将其写回)。由于这些线程以默认优先级运行,这将自我平衡。当所有 CPU 都忙于处理文件时,读取线程不会获得太多 CPU 时间,因此不会生成很多新的工作线程。
分离文件的处理并将它们写回磁盘没有任何意义;这只会产生大量不成文的工作在内存中排队的可能性。
【讨论】:
【参考方案3】:这两种方法各有优缺点。
单读
-
pro:从处理中提供良好的阅读器学术抽象层,并可能最终形成更好的编程模型。
pro:此外,如果您可以为所有其他线程提供作业(如果进程明显慢于您的读取操作)并且文件系统没有碎片,那么您可以更好地利用系统。李>
缺点:#2 很难实现,因此很可能不会存在这种优势。
缺点:编程比较困难,您需要一个先进先出或其他队列并在顶部进行同步。
处理线程中的读取:
-
专业版:易于实施,无需队列或同步
pro:在碎片文件系统中工作得更好:多个 io 请求可以通过操作系统或硬件进行优化以减少延迟。此外,延迟较低的请求会更快地开始处理,无论是在单个阅读器中,无论如何您都必须等待。
pro:读取操作本身有一个非系统部分,它也可以在线程中运行并成为并行处理的一部分。
con:在非碎片化系统中可能会松动,因为它增加了一些类似碎片化的行为。
顺便说一句,还有更多可能的处理方案。您忘记提及的是有一个编写器线程,您的处理转储结果在队列中并让后台进程编写它。这可能会给你额外的提升。不需要每个线程都等待写入。
您也可以使用在一个队列中写入的并行读取器,而不是从这个队列中进行处理(甚至更复杂的编程:-),但在某些情况下可以工作。
好吧,并行编写器也可以工作。
您还可以在不同的本地磁盘(不是目录,而是物理磁盘)之间分配文件。如果并行执行,这肯定会提高您的读/写性能。
【讨论】:
以上是关于如何为多个文件处理选择最佳 I/O 策略?的主要内容,如果未能解决你的问题,请参考以下文章
在 Express 请求中处理 Long I/O 操作的最佳实践