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