处理多个文件时如何最大程度地提高吞吐量

时间:2019-03-07 03:23:52

标签: multithreading optimization operating-system filesystems disk

假设您要尽快处理许多文件,其中处理时间>文件读取时间。

  • 使用线程池读取多个文件会增加吞吐量吗?还是只会引起更多磁盘争用?
  • 如果线程池确实有帮助,那么确定达到最大数量需要多少个线程呢?可以根据目标系统进行计算吗?
  • 对于单个内核,通过线程异步读取和处理的循环是否比同步处理更快?我认为由于磁盘延迟很高,所以确实如此。但是,也许如果读取的文件比处理时间小得多,最好让处理步骤不中断而无需上下文切换即可完成。

还有,您还有其他一些技巧可以最大化磁盘吞吐量吗?

1 个答案:

答案 0 :(得分:1)

我做了一些基准测试以提出一些一般性准则。我测试了约500k个小文件(约14kb)。我认为对于中等大小的文件,结果应该相似;但是对于较大的文件,我怀疑磁盘争用会变得更加重要。如果对操作系统/硬件内部知识更深入的人可以用更具体的解释来补充为什么这个问题,为什么某些事情比其他事情更快,将不胜感激。

我在具有双通道RAM和Linux内核4.18的16虚拟核心(8物理)计算机上进行了测试。

多个线程会增加读取吞吐量吗?

答案是肯定的。我认为这可能是由于1)单线程应用程序的硬件带宽限制或2)当有多个线程发出请求时,操作系统的磁盘请求队列得到了更好的利用。最好的性能是使用virtual_cores*2线程。吞吐量可能会缓慢下降,这可能是由于磁盘争用增加所致。如果这些页面恰好被缓存在RAM中,那么最好使用大小为virtual_cores的线程池。但是,如果缓存了<50%的页面(我认为是更常见的情况),那么virtual_cores*2就可以了。

我认为virtual_cores*2virtual_cores更好的原因是,文件读取还包括一些与磁盘无关的延迟,例如系统调用,解码等。因此,处理器可以交错更有效地线程化:当一个在磁盘上等待时,另一个可以执行与磁盘无关的文件读取操作。 (是否也可能是因为RAM是双通道?

我测试了读取随机文件还是按顺序读取(通过查找文件在存储中的物理块位置,并以此顺序排序请求)。可以预期,顺序访问将对HDD带来相当大的改善。如果您的应用程序中的限制因素是文件读取时间,而不是处理所述文件,则建议您对顺序访问的请求重新排序,以提高效率。

read throughput vs thread count

可以使用异步磁盘IO代替线程池。但是,从我的阅读来看,似乎还没有一种可移植的方法(see this reddit thread)。另外,libuv可以为NodeJS uses a thread pool提供动力来处理其文件IO。

平衡读取吞吐量与处理吞吐量

理想情况下,我们可以在单独的线程中进行读取和处理。在处理第一个文件时,我们可以在另一个线程中排队下一个文件。但是,我们分配用于读取文件的线程越多,处理线程的CPU争用就越多。解决方案是使更快的操作(读取与处理)的线程数最少,同时仍使文件之间的处理延迟为零。这个公式在我的测试中似乎给出了很好的结果:

prop = read_time/process_time
if prop > 1:
    # double virtual core count gives fastest reads, as per tests above
    read_threads = virtual_cores*2
    process_threads = ceil(read_threads/(2*prop))
else:
    process_threads = virtual_cores
    # double read thread pool so CPU can interleave better, as mentioned above
    read_threads = 2*ceil(process_threads*prop)

例如:读取= 2s,过程= 10s;因此每5个处理线程就有2个读取线程

在我的测试中,拥有额外的读取线程只会降低1-1.5%的性能。在我的测试中,对于prop接近于零的情况,1个读取+ 16个处理线程的吞吐量几乎与32个读取+ 16个处理线程的吞吐量相同。现代线程应该是非常轻量级的,并且如果文件消耗得不够快的话,读线程无论如何都应该处于睡眠状态。 (当prop很大时,进程线程也应如此)

另一方面,阅读线程太少会产生更大的影响(我的第三个原始问题)。例如,对于非常大的prop,1个读取+ 16个处理线程比1个读取+ 15个处理线程慢36%。由于进程线程占用了所有基准计算机的内核,因此读取线程具有过多的CPU争用,并且有36%的时间无法使下一个要处理的文件排队。因此,我的建议是改用太多的读取线程。如我上面的公式中所述,将读取线程池的大小加倍即可完成此操作。

旁注:您可以通过将virtual_cores设置为可用内核的较小百分比来限制应用程序占用的CPU资源。您还可以选择不加倍,因为当有备用内核或更多内核没有执行更密集的处理线程时,CPU争用可能不是问题。

摘要

根据我的测试结果,将线程池与virtual_cores*2文件读取线程+ virtual_cores文件处理线程一起使用,可以在各种不同的时序情况下为您提供良好的性能。此配置应为您提供最大吞吐量的约2%,而不必花费大量时间进行基准测试。