Java中NIO相对于传统IO的性能提升

27

1
原始的TSS帖子看起来已经有七年的历史了,截至今天似乎没有任何实用价值。根据评论,它显然缺乏任何客观批评。我不确定您是否应该在结果中考虑这样的帖子。 - Vineet Reynolds
10个回答

42

NIO和IO是一个非常有趣的讨论话题。

根据我的经验,这两种方法用于不同的工作。我听说过IO被称为“每个客户端一个线程”的方法,而NIO被称为“所有客户端都在一个线程上”的方法。虽然这些名称不是100%准确的,但我认为它们还是比较贴切的。

在我看来,NIO和IO的真正问题在于可扩展性。

NIO网络层应该使用单个线程来处理选择器并将读写/接受作业分派给其他线程。这使得处理选择器的线程(“选择器线程”)只需做这件事。这样可以在处理大量(请注意实际数字的缺乏)客户端时加快响应速度。现在,当服务器正在获取如此多的读写/接受请求以至于选择器线程一直在工作时,NIO开始崩溃。除此之外,由于所有读写/接受工作都由选择器线程处理,因此增加其他CPU将无法提高性能。

IO网络层可能采取每个套接字一个线程的方法,包括监听套接字。因此,线程数与客户端数量成正比。在中等数量的客户端下,这种方法效果很好。通过这种方法付出的代价是线程的成本。如果您有2000个客户端连接到服务器......,那么您至少需要2001个线程。即使在四芯、每芯6核心的机器上,您也只有24个处理节点(如果计算HyperThreading,则为48个)来处理这些2001个线程。实例化所有这些线程会消耗CPU时间和RAM,但即使使用线程池,您仍然必须支付CPU上下文在从一个线程转移到另一个线程时发生的切换的成本。在高服务器负载下,情况可能变得非常糟糕,如果编码不正确,可能会使整个机器停止运行。好处是,在这种情况下,添加CPU将提高性能。

现在这些都很好,但因为我的描述中没有数字来帮助决定使用IO还是NIO,所以这只是抽象的。这是因为需要考虑更多的变量:

  • 客户端的生命周期?短期还是长期?
  • 每个客户端预计要处理多少数据?大量小块还是少量巨块?
  • 预计同时连接多少客户端?
  • 您所在的操作系统和使用的JVM是什么?两者都影响线程和轮询成本。

这只是一些思考的食物。要回答哪个更快,NIO还是IO的问题:两者皆是又皆非 :)


你似乎忽略了大多数实现使用多个选择器(通常是核心数量的倍数),因此你关于NIO不能正确利用核心的论点是错误的。线程堆栈大小通常只有不到200KB...如果有20000个这样的线程,那将是一个问题。 - Johnny V

21

AB更快通常是一个过于简单化的观点,有时是错误的。

NIO并不一定比普通IO自动更快。

使用NIO可以潜在地更快地执行某些操作,并且您可以更容易地扩展到许多网络连接,因为您无需为每个连接使用一个线程。

但是NIO并不是一个神奇的“使一切更快”的开关,需要应用于所有场合。


11

NIO 不是因为它更快,而是因为它有更好的可伸缩性,特别是对于大量客户端的情况。

通常使用 IO (阻塞 IO/流 IO) 时,每个连接需要一个线程来更快地响应客户端。如果你使用单个线程来 (阻塞)监听/(阻塞)读取/处理/(阻塞)写入 所有客户端,就像星巴克只用一个窗口为所有顾客服务一样,那么星巴克顾客(即您的客户)会变得不耐烦(超时)。

请注意,您可能认为可以使用线程池来避免过多的线程拖慢服务器。虽然这就像星巴克将所有客户排成几个窗口一样,但客户仍然会因他人的 阻塞 而延迟。这就是为什么在传统的 Java IO 编程中,每个连接一个线程 是一个很好的选择。

NIO (非阻塞 IO/块 IO 哪一个使用) 使用反应器模式来处理 IO 事件。在这种情况下,您可以使用单个线程来 阻塞/监听|读取|处理|写入。然后客户端的 阻塞 (等待时间)不会相互影响。

请注意,IO 和 NIO 都可以使用多线程来利用更多的 CPU 资源,更多细节请参考Doug Lee 的介绍


5
这篇文章的问题在于它将阻塞IO与非阻塞NIO进行了比较。而在我自己的测试中,将阻塞IO与阻塞NIO进行比较(更类似),NIO的速度可快达30%。
然而,除非您的应用程序像代理服务器一样简单,否则这并不重要。应用程序的功能才是更重要的。无论是IO还是NIO都已经测试过最多支持10,000个连接。
如果您想要超快速的IO,可以使用异步IO(Java 7+)和Infiniband(不便宜,但延迟更低)。

4
您引用的文章已经有三年历史了,使用的是Java 1.4.2 (4)。自那时以来,Java 5、6和现在的7版本都已经发布。JVM内部和类库中有巨大的变化,这使得与1.4.2相关的任何基准测试都变得无关紧要。如果您开始深入研究,还会注意到java.io和java.nio之间的区别并不是很清晰。许多java.io调用现在解析为java.nio类。但是,当您想要提高性能时,解决方案不是只做一件事。唯一确定的方法是尝试不同的技术并进行度量,因为对我的应用程序来说快速的方法未必适合您的应用程序,反之亦然。NIO可能对某些应用程序而言速度较慢。或者它可能是性能问题的解决方案。最有可能的情况是二者兼而有之。

3

另外,据我所知,Java IO 已经被重写以使用 NIO(并且 NIO 具有更多功能)。微基准测试只是一个坏主意,特别是当它们很旧时,正如 lavinio 所指出的。


1
Java NIO和反应器模式并不是关于网络性能本身,而是关于单线程模型在性能和简洁性方面可以为系统带来的优势。而且,这种单线程方法可以带来巨大的改进。请看这里:低于2微秒延迟的套接字间通信

1

Java NIO被认为比常规IO更快,因为:

  1. Java NIO支持非阻塞模式。非阻塞IO比阻塞IO更快,因为它不需要为每个连接提供专用线程。当您需要处理大量同时连接时,这可以显着提高可扩展性,因为线程不太具有可扩展性。

  2. Java NIO通过支持直接内存缓冲区来减少数据复制。可以在没有任何数据副本的情况下读取和写入NIO套接字。使用传统的Java IO,数据在套接字缓冲区和字节数组之间多次复制。


0

没有固有的原因说明其中一个比另一个更快。

每个线程一个连接模型目前存在问题,因为Java线程具有很大的内存开销 - 线程堆栈预先分配到固定(且较大)的大小。这可以并且应该得到解决;然后我们就可以廉价地创建数十万个线程。


-1

Java IO 涵盖了多个构造和类。您不能在这么普遍的级别上进行比较。具体而言,NIO 使用内存映射文件进行读取 - 理论上这应该比简单的BufferedInputStream文件读取略快。但是,如果您比较诸如 RandomAccess 文件读取之类的东西,那么 NIO 内存映射文件将会快得很多


除非开发人员明确地这样做,否则NIO不会内存映射文件。 - RecursiveExceptionException

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