【问题标题】:CPU utilization while waiting for I/O to be ready in asynchronous programs在异步程序中等待 I/O 就绪时的 CPU 利用率
【发布时间】:2017-04-03 19:24:03
【问题描述】:

在异步程序中(例如 asyncio、twisted 等),所有系统调用都必须是非阻塞的。这意味着在主循环的每次迭代中都需要执行非阻塞select(或等效的东西)。这似乎比多线程方法更浪费,在多线程方法中,每个线程都可以使用阻塞调用和睡眠(不浪费 CPU 资源),直到套接字准备好。

这是否有时会导致异步程序比它们的多线程替代方案慢(尽管线程切换成本),或者是否有某种机制使这不是一个有效的问题?

【问题讨论】:

    标签: python python-3.x asynchronous io


    【解决方案1】:

    在单线程程序中使用select 时,您不必不断检查结果。使用它的正确方法是让它阻塞直到相关的 I/O 到达,就像在多线程的情况下一样。 但是,select 调用不是等待单个套接字(或其他 I/O),而是获取相关套接字的列表,并阻塞直到其中任何一个被中断。

    一旦发生这种情况,select 就会唤醒并返回准备就绪的套接字(或 I/O)列表。由编码人员以所需的方式处理这些准备好的套接字,然后,如果代码无事可做,它可能会开始 select 的另一次迭代。

    如您所见,不需要轮询循环;在一个或多个所需的套接字准备好之前,该程序不需要 CPU 资源。此外,如果几个套接字几乎一起准备好了,那么代码会唤醒一次,处理所有这些,然后才再次启动select。再加上程序不需要几个线程的资源开销这一事实,您可以看到为什么这在操作系统资源方面更有效。

    【讨论】:

    • 但是 AFAIK any 在异步程序中阻塞调用 is unacceptable,因为它会占用整个线程。如果您在等待时需要完成另一项任务怎么办?它可能是设置在某个时间开始的任务,或者是对与您在 select 调用中监视的套接字无关的其他中断做出反应的任务。
    • @max 当然,等待select 只有在程序无事可做时才会发生。如果还有其他任务要完成,那么程序会使用 unblocking select,并在完成其他必须做的事情后循环调用它(当然,它应该小部分做,以便对在合理的时间准备好插座)。但是,在这种情况下,不会浪费 CPU 周期——程序会继续做它必须做的事情。如果没有其他内容 - 它将再次使用阻止表单。
    • However, in this case there is no waste of CPU cycles - the program continues doing what it had to do. 我想如果the other stuff it has to doselect 所花费的时间相比足够大,那么它运行良好。尽管非阻塞select 调用所花费的时间是否可能与主循环的每次迭代中完成的实际工作一样多(或更多)?还是有什么机制让它不可能?
    • 非阻塞选择应立即返回提供列表中每个套接字的当前状态。所以你必须确保的是,它是在对你来说足够满意的时期被阅读的。例如,如果您的应用程序可以在读取套接字时延迟 0.01 秒,请确保每次连续调用 select 之间的“其他工作”时间少于 0.01 秒。
    【解决方案2】:

    在我的问题中,我将 I/O 处理分为两类:由非阻塞 select 表示的轮询和由阻塞 select 表示的“回调”。 (阻塞select 使线程休眠,所以严格来说它不是回调;但从概念上讲,它类似于回调,因为它在 I/O 准备好之前不使用 CPU 周期。因为我不知道准确的术语,我将只使用“回调”)。

    我假设异步模型不能使用“回调”I/O。现在在我看来,这个假设是不正确的。虽然异步程序不应该使用非阻塞select,并且它也不能严格地从操作系统请求传统的回调,但它当然可以为操作系统提供其主事件循环并说一个协程,并要求操作系统创建一个当 I/O 套接字准备好时,使用该协程在该事件循环中执行任务。在 I/O 准备好之前,这不会使用任何程序的 CPU 周期。 (如果它使用轮询而不是 I/O 中断,它可能会使用操作系统内核的 CPU 周期,但即使是多线程程序也是如此。)

    当然,这需要操作系统支持程序使用的异步框架。它可能没有。但即便如此,添加一个使用单个单独线程并阻止select 与操作系统对话的中间层似乎也很简单,并且每当 I/O 准备好时,都会为程序的主事件循环创建一个任务。如果该层包含在解释器中,则程序看起来完全是异步的。如果将此层作为库添加,则程序将在很大程度上是异步的,除了一个将同步 I/O 转换为异步 I/O 的简单附加线程。

    我不知道这是否在 python 中完成,但在概念上似乎是合理的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-16
      • 1970-01-01
      • 1970-01-01
      • 2017-03-14
      相关资源
      最近更新 更多