在Java中执行IO操作的最佳线程数量是多少?

21
在Goetz的《Java Concurrency in Practice》一书中,他在第101页的注脚中写道:“对于像这样不涉及I/O和访问任何共享数据的计算问题,Ncpu或Ncpu+1个线程产生最佳吞吐量;更多的线程没有帮助,实际上可能会降低性能...”
我的问题是,在执行文件写入、文件读取、文件删除等I/O操作时,是否有指导方针来确定使用的线程数以实现最大性能? 我明白这只是一个指导数字,因为磁盘速度和众多其他因素都会影响性能。
尽管如此,我还是想知道:在4个CPU的机器上,20个线程能否比4个线程更快地将1000个单独的文件写入磁盘?
7个回答

14

实际上,即使是I/O绑定的应用程序也可以从多线程中受益,因为并行读写几个文件比按顺序读写要快得多。特别是在网络延迟影响总吞吐量的情况下更是如此。但是,在一个线程正在处理最后读取的内容的同时,另一个线程可以忙于读取,从而允许更高的CPU利用率。

我们可以一整天都谈论理论,但正确的答案是使线程数可配置。我认为,将其增加到1以上将提高速度,但也会出现收益递减的情况。


听起来OP说的是让所有线程执行相同的操作,而不是一个读取一个处理。 - Bill the Lizard
好的,如果每个线程都在读取和处理不同的文件,您仍然可以获得并行性。 - Steven Sudit
2
非常抱歉造成困惑。我已经编辑了帖子,以明确我不是在谈论使用X个不同的线程编写相同文件的情况。我只是指每个线程写入不同的文件(但包含相同的字符串,这样我们就可以比较苹果和苹果)。 - marc

5
是的,在一个拥有4个CPU的机器上,20个线程肯定比4个线程更快地将数据写入磁盘。许多真实的程序受到I/O限制而非CPU限制。然而,这完全取决于您的磁盘以及其他线程在等待这些磁盘之前所做的CPU工作量。
如果您所有的线程都只是写入磁盘而没有其他操作,那么在一个4个CPU的机器上使用1个线程可能是写入磁盘最快的方式。这完全取决于您拥有多少个磁盘、要写入多少数据以及您的操作系统在I/O调度方面的表现。您的具体问题表明您想要4个线程都写入同一个文件。这没有太多意义,在任何实际场景中我都无法想象这样做会更快。 (您必须提前分配文件,然后每个线程都会seek()到不同的位置,结果每个线程都试图写入一些块,从而使写头反复移动。)
多线程的优势在于网络限制。例如:等待数据库服务器、Web浏览器或类似的外部资源。在这种情况下,您正在等待多个外部资源。

3
如果您正在使用同步I/O,则应为您的机器可以处理的每个并发I/O请求拥有一个线程。对于单个主轴单个硬盘,这是1(您可以同时读取或写入但不能同时进行)。对于可以同时处理多个I/O请求的磁盘,可以处理多少个请求就是多少个。
换句话说,这不受CPU计数的限制,因为I/O除了提交请求和等待外,实际上并没有影响CPU。有关更好的解释,请参见此处
在任何给定时间内,应该有多少I/O请求正在处理也是一个完全不同的问题。

如果我没记错的话,即使你只有一个主轴,磁盘控制器也会尝试批量处理IO请求,以便在单个磁盘旋转中完成尽可能多的操作。因此,如果您有写入磁盘完全不同角度位置的IO请求,我想您可以从同时编写线程中获得一些性能优势。否则,您将冒着在较短的操作之前同步安排较长的操作的风险。 - Navneeth

3

2
谢谢你指引我去那里。不幸的是,对于那个问题的被接受答案是错误的,除了添加我们的评论之外,我们几乎无能为力。 - Steven Sudit

2

像所有与性能有关的事情一样,这取决于情况。

如果您受到I/O限制,则添加线程将完全无助于您。(好吧,正如Steven Sudit所指出的那样,您可能会获得一些性能提升,但它会很小) 如果您没有受到I/O限制,则添加线程可能有所帮助。

不是要聪明,但找出最好的方法是对其进行分析,并查看哪种方法适用于您特定的情况。

编辑:根据评论更新


我不会给你点踩,但是正如我在回答中所解释的,我的经验与这个不同。 - Steven Sudit
不,我不是在谈论小的改进。我指的是在双核处理器上快三到四倍的速度提升。 - Steven Sudit
我们明确认同的一件事是,在这些问题上,实践胜过理论。编写代码并使其双向运行,亲自体验一下。当我看到改进的幅度时,感到非常惊讶。 - Steven Sudit
关于性能分析,我一直在进行,目前的发现是,在一个四核处理器上,使用4个线程和20个线程之间的差别并不是很明显,但是当使用100个线程时,性能下降非常明显。 - marc
3
增加I/O操作的线程可以更高效地排队处理任务,并且更好地隐藏延迟。 - Eric
显示剩余3条评论

1

Ncpu + 预期的并发IO活动数量是我的常规数字。

关键不在于20个线程可以比4个线程更快地将单个文件写入磁盘。如果每个CPU只有1个线程,那么当您正在写入磁盘时,您的进程将无法使用托管执行文件IO的线程的CPU。该CPU实际上正在等待文件被写入,而如果您有一个以上的线程,则它可以使用CPU在此期间进行实际处理。


确实。在操作系统层面,I/O是异步的,因此进行同步调用只意味着您的线程将被阻塞。如果没有其他线程可供调度,则CPU利用率将下降,您可能会认为自己受到I/O限制,即使您还没有达到管道的极限。 - Steven Sudit

0

如果你使用线程的唯一目的是写入磁盘,那么你的性能提升将会微不足道,甚至有害,因为通常驱动程序都针对硬盘的顺序读取进行了优化,所以你正在将文件中的顺序写入转换为多个“随机”写入。

如果 I/O 是针对不同的磁盘、不同的网络卡或不同的数据库服务器进行的,则多线程只能帮助你解决 I/O 绑定问题,从性能角度来看。然而,在观察到的性能方面,差异可能会更大。

例如,假设你要通过网络向许多不同的接收器发送多个文件。你仍然受到网络限制,因此你的最大速度不会超过100Mb/S,但如果你使用20个线程,那么这个过程将会变得更加公平。


由于延迟的原因,单个线程无法使网络卡达到饱和状态,但是多个线程可以。换句话说,存在软限制和硬限制。 - Steven Sudit
我发现一个线程在饱和1 Gb/s网络适配器方面没有问题。事实上,对于相对较小的消息大小,单个线程可以在环回中泵送约3-4 Gb/s。我还没有尝试过10 Gb/s网络适配器,但我希望在大约一个月内能够得到一些。 - Peter Lawrey
@Peter:延迟和带宽不是同一回事。 - Steven Sudit

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接