为什么 cpu 绑定在阻塞 I/O 时更好,而 I/O 绑定在非阻塞 I/O 时更好

Why cpu bound is better with blocking I/O and I/O bound is better with non blocking I/O

有人告诉我,对于 I/O 绑定应用程序,非阻塞 I/O 会更好。对于 CPU 绑定的应用程序,阻止 I/O 会好得多。我找不到这样的声明的原因。尝试过 google,但很少有文章只是触及主题而没有太多细节。有人可以提供它的深层原因吗?

有了这个,我也想弄清楚非阻塞的缺点是什么I/O。

在通过另一个线程 here 之后,我可以联系到的一个原因是如果 I/O 进程足够重,那么只有我们可以看到使用非阻塞 I/O 的显着性能改进].它还指出,如果 I/O 操作的数量很大(典型的 Web 应用程序场景),其中有许多请求寻找 I/O 请求,那么我们也会看到使用非阻塞 I/O.

因此我的问题归结为以下列表:

  1. 在 CPU 密集型应用程序的情况下,是否最好启动一个 threadpool(或 scala 的 executionContext)并在两者之间划分工作 线程池的线程。(我想它肯定有一个 优于产生自己的线程和划分工作 手动。还使用未来的异步概念,甚至 CPU 密集 可以使用回调返回工作,从而避免问题 与多线程阻塞有关?)。另外如果有 I/O 速度足够快,然后使用阻塞原则执行 I/O 线程池本身的线程。我说的对吗?

  2. 使用非阻塞的实际缺点或开销是什么 I/O 技术上?为什么我们看不到使用的性能提升 非阻塞 I/O 如果 I/O 足够快或者如果有非常少 I/O 需要操作吗?最终是 OS 正在处理 I/O的。不管 I/O 的数量是大还是 小,让 OS 处理这种痛苦。有什么不同。

从程序员的角度来看,阻塞 I/O 比非阻塞 I/O 更易于使用。您只需调用 read/write 函数,当它 return 时您就完成了。对于非阻塞 I/O,您需要检查是否可以 read/write,然后 read/write,然后检查 return 值。如果不是所有内容都已读取或写入,您需要一种机制来立即或稍后在可以完成写入时再次读取或写入。

关于性能:一个线程中的非阻塞 I/O 并不比一个线程中的阻塞 I/O 快。 I/O 操作的速度由读取或写入的设备(例如硬盘)决定。速度不是由等待(阻塞)或不等待(非阻塞)的人决定的。此外,如果您调用阻塞 I/O 函数,那么 OS 可以非常有效地进行阻塞。如果您需要在应用程序中执行 blocking/waiting,您可能会做得与 OS 几乎一样好,但也可能做得更糟。

那么,为什么程序员要让自己的生活更艰难并实现非阻塞 I/O?因为,这是关键点,他们的程序要做的不仅仅是那个单一的 I/O 操作。使用阻塞 I/O 时,您需要等到阻塞 I/O 完成。使用非阻塞 I/O 时,您可以进行一些计算,直到阻塞 I/O 完成。当然在非阻塞期间I/O你也可以触发其他I/O(阻塞或非阻塞)。

非阻塞 I/O 的另一种方法是在阻塞 I/O 中投入更多线程,但正如 SO post that you linked 中所述,线程是有代价的。该成本高于(OS 支持)非阻塞 I/O.

的成本

如果您的应用程序具有大量 I/O 但使用率较低 CPU,例如具有大量并行客户端的 Web 服务器,则使用一些非阻塞线程 I/O。使用阻塞 I/O 你最终会得到很多线程 -> 高成本,所以只使用几个线程 -> 需要非阻塞 I/O.

如果您有一个 CPU 密集的应用程序,例如读取文件、对完整数据进行密集计算并将结果写入文件的程序,那么 99% 的时间将花费在CPU 密集部分。因此,创建几个线程(例如每个处理器一个)并并行执行尽可能多的计算。关于 I/O 你可能会坚持使用阻塞 I/O 的一个主线程,因为它更容易实现,而且因为主线程本身与并行无关(假设计算是在其他线程)。

如果您有一个 CPU 密集型和 I/O 密集型应用程序,那么您还可以使用一些线程和非阻塞 I/O。您可以想象一个具有大量客户端和网页请求的 Web 服务器,您在其中使用 CGI 脚本进行密集计算。在连接上等待 I/O 时,程序可以计算另一个连接的结果。或者考虑一个读取大文件并可以对文件块进行密集计算的程序(例如计算平均值或将所有值加 1)。在那种情况下,您可以使用非阻塞读取,并且在等待下一次读取完成时,您已经可以计算可用的数据。如果结果文件只是一个小的压缩值(如平均值),您可以对结果使用阻塞写入。如果结果文件与输入文件一样大并且类似于“所有值 +1”,那么您可以非阻塞地写回结果,并且在写入完成时您可以自由地对下一个块进行计算。