最近用 C++20 写了一个模仿 Python asyncio 的协程库https://github.com/netcan/asyncio,使用方式基本和 Python 一模一样,简单看一下使用方式,例如一个 Hello World:
Task<> hello_world() {
fmt::print("hello\n");
co_await asyncio::sleep(1s);
fmt::print("world\n");
}
int main() {
asyncio::run(hello_world());
}
编写这个库的目的是为了我 即将出版的 C++20 书籍有关,目前还差协程和 Modules 特性没写,这篇文章不会涉及太多技术细节。很多人关注 C++ 协程的性能表现,据我测试恢复、挂起一个协程和函数开销差不多,大概 10 纳秒的量级,如果使用协程做异步编程,那么这部分开销几乎可以忽略不计,因为 IO 非常耗时,量级通常是毫秒的。
实践出真知,数据才能说明问题,我简单使用这个库和 Python asyncio、C++ 的 asio、C 的 epoll/libevent/libuv、Rust 的 tokio 进行性能测试,通过它们编写一个 单线程 的 Echo 服务端,客户端连接服务端后,发送一条消息立即收到一条发送过的消息。
测试系统为 Debian Linux 5.15.0-1-amd64,CPU 为 AMD 2600X,测试工具为 ApacheBench,消息条数为 10’000’000,每条消息的大小为 106 字节,并发连接数量为 1000,保持长连接测试。由于我没有多余的机器,因此使用本地回环测试,并且使用 C 语言版本作为基线对比。得到如下测试结果:
framework | RPS [#/sec] (mean) | Language | Pattern |
---|---|---|---|
python asyncio | 47393.59 | Python | coroutine |
this project | 164457.63 | C++20 | coroutine |
asio | 159322.66 | C++20 | coroutine |
tokio-rs | 156852.70 | Rust1.59.0-nightly | coroutine |
epoll | 153147.79 | C | eventloop |
libevent | 136996.46 | C | callback |
libuv | 159937.73 | C | callback |
基本可以看到,C++ 的协程性能是 Python 协程的 3 倍多,当然测试也有波动性,RPS 误差在 5000 左右,所以其他性能基本差不多,就 C 语言版本而言,协程的可读性要比回调模式要好很多,而又不会带来性能上的损耗。
详细的测试代码、数据请参见:https://github.com/netcan/asyncio/blob/master/docs/benchmark.md。
相关的技术讨论,整理至FAQ,也可以参考我在 Reddit 发的帖子: