【问题标题】:What actually happens in asynchronous IO异步 IO 中实际发生的情况
【发布时间】:2021-01-04 21:52:07
【问题描述】:

我一直在阅读为什么异步 IO 比同步 IO 更好,这是因为在异步 IO 中,您的程序可以继续运行,而在同步 IO 中,您会被阻塞,直到操作完成。
我不明白这句话,因为使用同步 IO(例如write())内核将数据写入磁盘 - 它不会自行发生。内核确实需要 CPU 时间才能做到这一点。
所以在异步 IO 中,它也需要它,这可能会导致上下文从我的应用程序切换到内核。所以它并不是真正的阻塞,但确实需要 cpu 周期来运行这个操作。

  • 正确吗?
  • 这两者之间的区别是我们假设磁盘访问速度很慢,因此与等待数据写入磁盘的同步 IO 相比,在异步 IO 中等待数据写入磁盘的时间可以用来继续做应用处理,写入磁盘的内核部分很小?
  • 假设我有一个应用程序,它所做的只是获取信息并将其写入文件。使用异步 IO 代替同步 IO 有什么好处吗?

同步 IO 示例:

  • 写()

异步 ​​IO 示例:

  • io_uring(据我所知,副本也为零,所以这是一个好处)
  • spdk(应该是最好的,虽然我不明白怎么用)
  • aio

【问题讨论】:

  • "内核将数据写入磁盘-它不会自己发生" 实际上,内核向磁盘控制器制定消息,然后磁盘控制器自己进行实际写入没有处理器或操作系统的帮助。支持 DMA 的现代磁盘接口实际上会自己从系统 RAM 中获取数据——内核只会给磁盘控制器提供数据的地址(像指针,但物理地址不是虚拟地址)。
  • 是的,异步输出的系统完成的工作总量通常至少是同步的。然而,与 CPU 负载相比,用户更倾向于抱怨用户界面暂停几分之一秒。如果用户界面定期写入文件以响应一些较小的用户操作,同步输出将意味着用户界面在写入文件时会一直暂停或挂起。异步输出允许程序继续响应用户,即使输出不完整。
  • 磁盘是慢的部分,不是CPU。因此,如果您有其他事情要做,请使用异步 I/O。如果你仍然要等待,那么没有理由这样做。
  • 当你执行write 时,你只需要等待数据写入内核的缓冲区缓存,而不是直到它被写入磁盘。写入返回后,写入磁盘将异步进行。如果你想要一个完全同步的写入,你也需要使用 fsync。

标签: c++ c linux io


【解决方案1】:

您的理解部分是对的,但是您使用哪些工具取决于您喜欢哪种编程模型,并且不能确定您的程序是否会冻结等待 I/O 操作完成。对于某些专门的、负载非常高的应用程序,某些模型的效率略高到中等,但除非您处于这种情况,否则您应该选择易于编写和维护您的程序的模型并让它成为可移植到您和您的用户关心的系统,而不是有人宣传的高性能系统。

传统上,有两种方法可以在不阻塞的情况下进行 I/O:

  1. 将您的程序构建为一个事件循环,在一组文件描述符上执行select(现在pollselect 已过时并且存在严重缺陷),这些文件描述符可能已准备好读取输入或接受输出。这需要为您尚未准备好处理的部分输入和您尚未能够写出的待处理输出保持某种显式状态。

  2. 将 I/O 分离到单独的执行上下文中。从历史上看,这种统一的方法是单独的进程,当你有其他理由想要单独的进程(特权隔离等)时,这仍然是有意义的,但更现代的方法是使用线程。为每个 I/O 通道使用单独的执行上下文,您可以只使用普通阻塞 read/write(甚至是缓冲的 stdio 函数),并且为您保留任何部分输入或未完成的输出状态隐式 在其执行上下文的调用帧堆栈/局部变量中。

请注意,在上述两个选项中,只有后者有助于解决因磁盘访问缓慢而导致的停顿,因为根据select/poll,常规文件始终“准备好”进行输入和输出。

现在有一种趋势,可能主要是由于 JavaScript 之类的语言,采用第三种方法,即“异步模型”,甚至带有处理程序回调。我发现它比上述任何一种方法都更难使用,需要更多样板代码,也更难推理,但很多人喜欢它。如果你想使用它,最好使用一个库来抽象你提到的 Linux 主义(io_uring 等),这样你的程序就可以在其他系统上运行,而不依赖于最新的 Linux 时尚。

现在回答您的特定问题:

假设我有一个应用程序,它所做的只是获取信息并将其写入文件。使用异步 IO 代替同步 IO 有什么好处吗?

如果您的应用程序只有一个输入源(没有交互性)和一个输出,例如像大多数 unix 命令一样,无论哪种编程模型(事件循环、线程、异步回调等),任何类型的异步 I/O 都绝对没有任何好处。最简单、最有效的做法就是读写。

【讨论】:

  • 我不认为这种趋势是由 Javascript 引起的。 Win32 在 Javascript 之前有异步 I/O 方式,我假设它是从 VMS 获得的。
【解决方案2】:

内核确实需要 CPU 时间才能做到这一点。

对吗?

差不多,是的。

这两者之间的区别是我们假设磁盘访问很慢吗...在异步IO中,您等待它写入​​磁盘的时间可用于继续进行应用程序处理,而内核部分写入它到磁盘很小吗?

没错。

假设我有一个应用程序,它所做的只是获取信息并将其写入文件。使用异步 IO 代替同步 IO 有什么好处?

取决于许多因素。应用程序如何“获取信息”?它是 CPU 密集型的吗?它使用与写入相同的 IO 吗?它是同时处理多个请求的服务吗?有多少同时连接?首先,性能重要吗?在某些情况下:是的,使用异步 IO 可能有很大的好处。在其他一些情况下,您可以通过在单独的线程中使用同步 IO 获得大部分好处。而在其他情况下,单线程同步 IO 就足够了。

【讨论】:

    【解决方案3】:

    我不明白这句话,因为内核使用同步 IO(例如 write())将数据写入磁盘 - 它不会自行发生。内核确实需要 CPU 时间才能做到这一点。

    没有。大多数现代设备都能够自行将数据传输到 RAM 或从 RAM 传输数据(使用 DMA 或总线主控)。

    举个例子; CPU 可能会告诉磁盘控制器“将 4 个扇区读入地址 0x12345000 的 RAM”,然后 CPU 可以在磁盘控制器进行传输时执行它喜欢的任何其他操作(并且在磁盘控制器执行传输时会被来自磁盘控制器的 IRQ 中断已完成数据传输)。

    但是;对于现代系统(您可以有任意数量的进程都想同时使用同一设备),设备驱动程序必须维护一个待处理操作的列表。在这种情况下(负载下);当设备生成一个 IRQ 表示它完成了一个操作时,设备驱动程序通过告诉设备开始下一个“挂起的操作”来响应。这样,设备几乎没有时间空闲等待被要求开始下一个操作(设备利用率更高),而 CPU 几乎所有时间都在做其他事情(在 IRQ 之间)。

    当然,硬件通常更先进(例如,本身有一个内部操作队列,因此驱动程序可以告诉它做多件事,并且它可以在完成前一个操作后立即开始下一个操作);并且驱动程序通常更高级(例如,具有“IO 优先级”以确保首先完成更重要的事情,而不是仅仅拥有一个简单的等待操作的 FIFO 队列)。

    假设我有一个应用程序,它所做的只是获取信息并将其写入文件。使用异步 IO 代替同步 IO 有什么好处吗?

    假设您从设备 A 获取信息(而 CPU 和设备 B 空闲);然后稍微处理该信息(当设备A和设备B空闲时);然后将结果写入 deviceB(而 deviceA 和 CPU 空闲)。您可以看到大多数硬件大部分时间都无所事事(利用率低下)。

    带异步IO;当 deviceA 正在获取下一条信息时,CPU 可以处理当前信息,而 deviceB 正在写入前一条信息。在理想条件下(没有速度不匹配)可以达到 100% 的利用率(设备 A、CPU 和设备 B 永远不会空闲);即使存在速度不匹配(例如,设备 B 需要等待 CPU 完成当前部分的处理),任何空闲的时间都将最小化(并尽可能最大化利用率)。

    另一种选择是使用多个任务 - 例如一个任务从设备A同步获取数据,并在读取数据时通知另一个任务;第二个任务等待数据到达并处理它,并在处理数据时通知另一个任务;然后是第三个任务,等待直到数据被处理并将其同步写入设备B。供使用;这实际上与使用异步 IO 相同(实际上它可以被认为是“模拟异步 IO”)。问题是您添加了一堆额外的开销来管理和同步多个任务(更多的 RAM 用于状态和堆栈、任务切换、锁争用......);并使代码更复杂,更难维护。

    【讨论】:

    • 谢谢。问题是我在top:%Cpu10 : 0.0 us, 99.7 sy, 0.0 ni, 0.3 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st 上看到了这个。我有一个nvme 磁盘。我没有看到它wa(等等)。这是否意味着我写作效率低下?我记得perf'ing 我的应用程序,我看到大多数周期都通过 ~ 复制 64 之类的功能进入内核(我不记得确切的名称)
    • 根据你的说法,如果获取信息是 CPU 密集型的,使用异步 io 会有所帮助,因为在磁盘写入时我可以获取信息。如果获取 CPU 不是密集的,那么这个线程只是“浪费时间”写数据。那么改成a-sync io就没意思了。
    • @hudac:对于高级文件 IO,它甚至更加复杂,因为正在进行缓存(主要使用“否则为空闲 RAM”)。写入通常只是缓冲,因此它们看起来很快/即时(因为数据实际上并没有写入设备,直到内核稍后处理它),并且当正在读取的文件/数据是时读取也可以看起来快速/即时已经缓存在 RAM 中。但是...
    • @hudac:“做 A,然后做 B,然后做 C”(就性能、利用率而言)永远不会比“并行做 A、B 和 C”更好;因此同步 IO 永远不会比异步 IO 更好(就性能、利用率而言)。它们仅在极不可能的情况下“相等”(例如,非常简单的场景涉及不存在的设备和/或少量数据,具有单 CPU,而其他进程没有任何作用)。例如;如果提取是 CPU 密集型的并且处理提取的数据是 CPU 密集型的,那么(实际上)您可能有 4 个或更多 CPU,并且可以并行执行。
    • @hudac:同步 IO 的主要好处是对程序员来说更容易,因为它符合“过程思维”。具体来说; “A 发生,然后 B 发生,然后 C 发生”比“所有这些事情可以同时发生,我不知道任何事情会以哪个顺序完成”更容易处理。
    【解决方案4】:

    在任何情况下都需要上下文切换。内核总是在它自己的上下文中工作。因此,同步访问不会节省处理器时间。 通常,编写不需要大量的处理器工作。限制因素是磁盘响应。问题是我们是否会等待这个回复来完成我们的工作。

    假设我有一个应用程序,它所做的只是获取信息并写入 将其放入文件中。使用异步 IO 代替有什么好处吗? 同步 IO?

    如果您实现同步访问,您的顺序如下:

    1. 获取信息
    2. 写入信息
    3. 转到 1。

    因此,在 write() 完成之前,您无法获取信息。让信息提供者和你写入的磁盘一样慢。在这种情况下,程序将比异步程序慢两倍。 如果信息提供者在你写的时候等不及保存信息,你在写的时候会丢失部分信息。此类信息源的示例可以是用于快速处理的传感器。在这种情况下,您应该同步读取传感器并异步保存获取的值。

    【讨论】:

      【解决方案5】:

      异步 ​​IO 并不比同步 IO 好。反之亦然。

      问题是哪一个更适合您的用例。

      同步 IO 通常更易于编码,但异步 IO 可以带来更好的吞吐量和响应能力,但代价是更复杂的代码。

      我从来没有从仅用于文件访问的异步 IO 中受益,但某些应用程序可能从中受益。

      访问“慢”IO(如网络或终端)的应用程序具有最大的优势。使用异步 IO 可以让他们在等待 IO 完成的同时做有用的工作。这可能意味着能够为更多客户提供服务或保持应用程序对用户的响应。

      (而“慢”只是意味着 IO 操作完成的时间是无限的,它可能永远不会完成,例如在等待用户按 Enter 或网络客户端发送命令时)

      最后,异步 IO 并没有做更少的工作,它只是在时间上分配不同,以减少空闲等待。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-06-21
        • 1970-01-01
        • 2014-09-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多