非阻塞I / O是否比multithreading阻塞I / O更快? 怎么样?

我在网上search了关于阻塞I / O和非阻塞I / O的一些技术细节,我发现有几个人说非阻塞I / O比阻塞I / O要快。 例如在这个文件中 。

如果我使用阻塞I / O,那么当前阻塞的线程无法做其他任何事情…因为阻塞了。 但是一旦一个线程开始被阻塞,操作系统就可以切换到另一个线程并且不会切换回来,直到被阻塞的线程有一些事情要做。 所以只要系统上有另一个线程需要CPU而没有被阻塞,与基于事件的非阻塞方法相比,CPU不应该有更多的空闲时间。

除了减lessCPU闲置的时间之外,我还可以看到另外一个选项来增加计算机在给定的时间范围内可以执行的任务数量:减less切换线程引起的开销。 但是,这怎么办呢? 是否有足够的开销来显示可衡量的影响? 这里是一个想法,我可以如何描绘它的工作:

  1. 为了加载文件的内容,应用程序将这个任务委托给基于事件的I / O框架,传递一个callback函数以及一个文件名
  2. 事件框架委托给操作系统,该操作系统对硬盘的DMA控制器进行编程,以将文件直接写入存储器
  3. 事件框架允许运行更多的代码。
  4. 完成磁盘到内存的复制后,DMA控制器会引起中断。
  5. 操作系统的中断处理程序通知基于事件的I / O框架文件被完全加载到内存中。 它是如何做到的? 使用信号?
  6. 当前在事件I / O框架中运行的代码完成。
  7. 基于事件的I / O框架检查其队列,并从步骤5看到操作系统的消息,并执行步骤1中获得的callback。

这是如何工作的? 如果没有,它是如何工作的? 这意味着事件系统可以工作,而不需要明确地触摸堆栈(比如真正的调度器,在切换线程时需要备份堆栈并将另一个线程的堆栈复制到内存中)? 这实际上保存多less时间? 还有更多吗?

无阻塞或asynchronousI / O的最大优点是您的线程可以继续并行工作。 当然,你也可以使用一个额外的线程来实现这一点。 正如你所说的最好的整体(系统)性能,我想最好是使用asynchronousI / O而不是multithreading(所以减less线程切换)。

我们来看看一个networking服务器程序的可能的实现,该程序可以处理1000个并行连接的客户端:

  1. 每个连接一个线程(可以阻止I / O,但也可以是非阻塞I / O)。
    每个线程都需要内存资源(也是内核内存!),这是一个缺点。 而每一个额外的线程意味着更多的工作调度。
  2. 一个线程的所有连接。
    由于我们拥有较less的线程,因此会从系统中获取负载。 但是它也阻止了你使用你的机器的全部性能,因为你最终可能会驱动一个处理器达到100%,并让所有其他处理器闲置。
  3. 每个线程处理一些连接的几个线程。
    由于线程较less,这会从系统中获取负载。 它可以使用所有可用的处理器。 在Windows上,这种方法由线程池API支持。

当然有更多的线程本身不是一个问题。 正如你可能已经认识到,我select了相当多的连接/线程。 我怀疑你会看到三种可能的实现之间的区别,如果我们只谈论十几个线程(这也是Raymond Chen在MSDN博客文章中build议的Windows是否有每个进程2000个线程的限制? )。

在Windows上使用非缓冲文件I / O意味着写入的大小必须是页面大小的倍数。 我没有对它进行testing,但是这听起来也可能会影响写缓冲的同步和asynchronous写入性能。

你所描述的步骤1到步骤7给出了它是如何工作的一个好主意。 在Windows上,操作系统将使用事件或callback通知您完成asynchronousI / O(具有OVERLAPPED结构的WriteFile )。 callback函数只会在您的代码调用WaitForMultipleObjectsEx并将bAlertable设置为true时调用。

更多在网上阅读:

  • MSDN上的用户界面中的multithreading ,也很快处理创build线程的成本
  • Section Threads和Thread Pools说:“尽pipe线程相对容易创build和使用,操作系统会分配大量的时间和其他资源来pipe理它们。”
  • MSDN上的CreateThread文档说:“但是,如果您为每个处理器创build一个线程并构build应用程序维护上下文信息的请求队列,那么您的应用程序将具有更好的性能。”
  • 旧文章为什么太multithreading伤害performance,以及如何做

I / O包括多种types的操作,如从硬盘读写数据,访问networking资源,调用Web服务或从数据库中检索数据。 根据平台和操作types的不同,asynchronousI / O通常会利用任何硬件或低级系统支持来执行操作。 这意味着它将在CPU上以尽可能小的影响执行。

在应用程序级别,asynchronousI / O阻止线程必须等待I / O操作完成。 只要asynchronousI / O操作启动,它就释放启动它的线程,并注册callback。 操作完成后,callback将排队等待在第一个可用线程上执行。

如果I / O操作是同步执行的,那么直到操作完成后,它的运行线程才会保持不动。 运行时不知道I / O操作何时完成,所以它会周期性地向等待的线程提供一些CPU时间,CPU时间可能被其他具有CPU绑定操作的线程执行。

所以,正如@ user1629468所提到的那样,asynchronousI / O不能提供更好的性能,而是更好的可伸缩性。 在具有有限数量的可用线程的上下文中运行时,这是显而易见的,就像Web应用程序一样。 Web应用程序通常使用线程池,将线程分配给每个请求。 如果请求在长时间运行I / O操作时被阻塞,则存在耗尽Web池并使Web应用程序冻结或响应速度缓慢的风险。

我注意到的一件事是在处理非常快的I / O操作时,asynchronousI / O不是最好的select。 在这种情况下,在等待I / O操作完成时不保持线程繁忙的好处并不是很重要,并且操作在一个线程上启动并在另一个线程上完成的事实增加了总体执行的开销。

您可以阅读我最近就asynchronousI / O与multithreading这个主题所做的更详细的研究。

使用AIO的主​​要原因是可扩展性。 从几个主题的angular度来看,好处并不明显。 但是当系统扩展到1000线程时,AIO将会提供更好的性能。 需要注意的是,AIO库不应该引入进一步的瓶颈。

非阻塞I / O的一个可能的实现正如你所说的,后台线程池阻塞I / O,并通过一些callback机制来通知I / O发起者的线程。 实际上,这就是glibc中的AIO模块的工作原理。 以下是关于实施的一些模糊的细节。

虽然这是一个很好的解决scheme,只要你有线程,这个操作系统通常可以更有效地处理非阻塞的I / O。 这个维基百科文章列出了线程池之外的可能的实现。

我目前正在使用protothreads在embedded式平台上实现asynchronousio。 非阻塞io使运行在16000fps和160fps之间的区别。 非阻塞io的最大好处就是可以在硬件完成任务的时候构造代码来完成其他任务。 即使设备的初始化也可以并行进行。

马丁

假设由于任何forms的多计算而导致的速度提高,您必须假设多个基于CPU的任务在多个计算资源(通常是处理器内核)上同时执行,否则不是所有的任务都依赖于相同的资源 – 也就是说,一些任务可能依赖于一个系统子组件(比如说磁盘存储),而一些任务依赖于另一个(接收来自外围设备的通信),还有一些任务可能需要使用处理器核心。

第一种情况通常被称为“并行”编程。 第二个场景通常被称为“并发”或“asynchronous”编程,尽pipe“并发”有时也被用来指只允许操作系统交错执行多个任务的情况,而不pipe这样的执行是否必须串行放置或者如果可以使用多个资源来实现并行执行。 在后一种情况下,“并发”一般是指执行被写入程序的方式,而不是从任务执行的实际同时性的angular度来看。

用默认的假设来谈论这一切是很容易的。 例如,有些人可以很快做出一个声明,如“asynchronousI / O比multithreadingI / O更快”。 这种说法有几个原因是可疑的。 首先,可能会出现这样的情况:某些给定的asynchronousI / O框架是精确地使用multithreading来实现的,在这种情况下它们是相同的,并且说一个概念“比另一个更快”是没有意义的。

其次,即使在有asynchronous框架(如单线程事件循环)的单线程实现的情况下,您仍然必须假定该循环正在做什么。 例如,您可以使用单线程事件循环做的一件事情是请求它asynchronous完成两个不同的纯粹CPU限制的任务。 如果你在只有一个理想化的单处理器内核(忽略现代硬件优化)的机器上完成这个任务,那么“asynchronous”执行这个任务并不会有什么不同,而不是用两个独立pipe理的线程执行任务,或者只用一个独立的进程 – – 差异可能归结为线程切换或操作系统调度优化,但是如果两个任务都转到CPU,那么在任何情况下都是相似的。

想象一下你可能遇到的很多不寻常或愚蠢的angular落案例是有用的。

“asynchronous”并不必须是并发的,例如,如上所述:“asynchronous”在一台机器上执行两个CPU绑定的任务,并且只有一个处理器核心。

multithreading执行不一定是并发的:在一个处理器核心的机器上产生两个线程,或者要求两个线程获取其他种类的稀缺资源(想象一下,只能build立一个networking数据库连接一次)。 线程的执行可能是交错的,但是操作系统调度器看起来合适,但是它们的总运行时间不能在单个内核上减less(并且将从线程上下文切换中增加)(或者更一般地,如果产生的线程多于核心来运行它们,或者有更多的线程要求资源而不是资源可以维持的资源)。 同样的事情也适用于多处理。

因此,无论是asynchronousI / O还是multithreading都不能在运行时间方面提供任何性能增益。 他们甚至可以放慢速度。

但是,如果您定义了一个特定的用例,就像一个特定的程序,它们都会进行networking调用以从远程数据库等联网资源中检索数据,并执行一些本地CPU计算,那么您可以开始推理两种方法之间的性能差异给出了关于硬件的特定假设。

要问的问题:我需要执行多less个计算步骤以及需要执行多less个独立的资源系统? 是否有需要使用独立系统子组件的计算步骤的子集,并可以同时从中受益? 我有多less个处理器内核?使用多个处理器或线程在不同的内核上完成任务的开销是多less?

如果你的任务在很大程度上依赖于独立的子系统,那么asynchronous解决scheme可能是好的。 如果处理它所需的线程数量很大,那么对于操作系统而言,上下文切换就变得不重要了,那么单线程asynchronous解决scheme可能会更好。

每当任务被相同的资源绑定(例如多个需要同时访问相同的networking或本地资源),那么multithreading可能会引入不令人满意的开销,而单线程asynchronous可能会引入较less的开销,有限的情况也不能产生加速。 在这种情况下,唯一的select(如果你想加快速度)是使该资源的多个副本可用(例如,如果稀缺资源是CPU,多个处理器核心;如果稀缺资源支持更多并发连接是连接受限的数据库等)。

另一种说法是:允许操作系统交叉使用单个资源来完成两个任务不能比仅仅让一个任务使用资源而另一个任务等待,然后让第二个任务连续完成。 另外,交织的调度器代价在任何实际情况下都意味着实际上会造成一个减速。 CPU,networking资源,存储器资源,外围设备或任何其他系统资源的交错使用是否出现并不重要。

据我所知,改进之处在于asynchronousI / O使用(我正在谈论MS系统,只是为了澄清) 所谓的I / O完成端口 。 通过使用asynchronous调用,框架自动利用这样的架构,并且这应该比标准线程机制更高效。 作为一个个人经验,我可以说,如果你更喜欢AsyncCalls而不是阻塞线程,你会明显感觉到你的应用程序更具react native。