如果我有一个在关键执行路径中调用协程的常规方法,使用co_spawn可能会引入延迟。当我使用co_spawn时,它将协程调度为与其余代码并发运行,这意味着它不会阻塞调用方法的执行。但是,在调度和管理协程时仍然存在一些开销,这可能会影响应用程序的整体延迟。有没有更有效的方法从常规函数调用协程?
9bfwbjaz1#
当我使用co_spawn时,它将协程调度为与其余代码并发运行,这意味着它不会阻塞调用方法的执行。但是,在调度和管理协程时仍然会涉及一些开销这不一定是真的。你把并发和异步混为一谈了。在ASIO的上下文中,不需要调度异步操作。相反,它们可以将工作委托给内核,或者实际上是自然异步的硬件。这里唯一的“调度”元素是完成的调用。实际上,如果IO操作花费的时间无限小,那么回调调用将支配观察到的挂钟时间。然而,通常,IO操作相对于例如面向CPU的负载。您可以在一个TCP往返过程中启动多个任务(甚至在环回网络上)。这就是异步IO框架流行的原因。这也是Windows实现IO完成端口等的原因。随着时间的推移,所有的创新都在那里,因为它们“有意义”。有没有更有效的方法从常规函数调用协程?是的。原则上,直接在可用的操作系统原语上编写自己的状态机在理论上是最快的。然而,与其他代码集成将是乏味的、依赖于平台的和VeryHard(TM)。这是Asio巧妙地塞住的洞。要将开销降至最低,请执行以下操作:
co_await_t<io_context::executor_type>
any_io_executor
该库进行了巧妙的优化,涉及(但不限于)调度在本地线程上排队的完成,管理分配顺序以最大化重用并最小化碎片。请关注最新版本中的 * 立即完成 * 优化功能。例如:When You Do
Live On Compiler Explorer
#include <boost/asio.hpp> namespace asio = boost::asio; using Ex = asio::io_context::executor_type; asio::awaitable<int, Ex> static inline answer(std::string_view prompt) { // co_return prompt.length() + 9; } int main() { asio::io_context ioc(1); co_spawn(ioc.get_executor(), answer("Life, the Universe and Everything"), [](std::exception_ptr, int i) { ::exit(i); }); ioc.run(); }
您会看到程序返回42,而没有任何调度开销。我看到的最明显的开销是分配科罗帧,如果你需要一个coro,你通常会需要它(你可以只是提交处理程序到一个队列-或io上下文-否则)。有没有更有效的方法从常规函数调用协程?协程可以是非常轻量级的。它们的轻量程度取决于编译器优化它们的能力。而这又主要取决于awaitable类型(promise/handle)的复杂性。原则上,以切割功能为代价来降低成本将是非常可能的。Asio的awaitable是为异步IO场景设计的,原因显而易见。如果你不想/不需要,那么可以考虑更低级或通用的库,比如cppcoro。当然,如果您有它们,您将自己将它们集成到应用程序的异步IO需求中。
42
awaitable
1条答案
按热度按时间9bfwbjaz1#
当我使用co_spawn时,它将协程调度为与其余代码并发运行,这意味着它不会阻塞调用方法的执行。但是,在调度和管理协程时仍然会涉及一些开销
这不一定是真的。你把并发和异步混为一谈了。
在ASIO的上下文中,不需要调度异步操作。相反,它们可以将工作委托给内核,或者实际上是自然异步的硬件。这里唯一的“调度”元素是完成的调用。实际上,如果IO操作花费的时间无限小,那么回调调用将支配观察到的挂钟时间。
然而,通常,IO操作相对于例如面向CPU的负载。您可以在一个TCP往返过程中启动多个任务(甚至在环回网络上)。这就是异步IO框架流行的原因。这也是Windows实现IO完成端口等的原因。随着时间的推移,所有的创新都在那里,因为它们“有意义”。
有没有更有效的方法从常规函数调用协程?
是的。原则上,直接在可用的操作系统原语上编写自己的状态机在理论上是最快的。然而,与其他代码集成将是乏味的、依赖于平台的和VeryHard(TM)。这是Asio巧妙地塞住的洞。
要将开销降至最低,请执行以下操作:
co_await_t<io_context::executor_type>
,而不是由默认值any_io_executor
参数化)该库进行了巧妙的优化,涉及(但不限于)调度在本地线程上排队的完成,管理分配顺序以最大化重用并最小化碎片。请关注最新版本中的 * 立即完成 * 优化功能。
例如:When You Do
Live On Compiler Explorer
您会看到程序返回
42
,而没有任何调度开销。我看到的最明显的开销是分配科罗帧,如果你需要一个coro,你通常会需要它(你可以只是提交处理程序到一个队列-或io上下文-否则)。有没有更有效的方法从常规函数调用协程?
协程可以是非常轻量级的。它们的轻量程度取决于编译器优化它们的能力。而这又主要取决于awaitable类型(promise/handle)的复杂性。原则上,以切割功能为代价来降低成本将是非常可能的。Asio的
awaitable
是为异步IO场景设计的,原因显而易见。如果你不想/不需要,那么可以考虑更低级或通用的库,比如cppcoro。当然,如果您有它们,您将自己将它们集成到应用程序的异步IO需求中。