Monoio: 字节跳动开源 Rust Runtime

2021-12-09 09:30:06 +08:00
 ihciah

Monoio 是字节跳动服务框架组开源的基于 io-uring 的 thread-per-core 模型高性能 Rust Runtime ,旨在为高性能网络中间件等场景提供必要的运行时。

项目仓库: https://github.com/bytedance/monoio

背景

过去,高性能网络中间件或服务器往往使用 C/C++ 编写,比如我们常见的 Envoy 和 Nginx 。它们往往以非常直接的方式和操作系统交互,并且得益于没有垃圾收集机制,相比有 GC 的语言(如 Golang 和 Java ),额外开销十分低,延迟稳定。

但是开发这类组件对开发者的专业水平有较高的要求,编程范式上对开发者心智负担巨大,稍有不慎就会造成非预期的后果。举例来说,在 C++ 中要完成一次异步的网络请求,需要将整个流程按照异步点拆分成独立的纯同步函数,并以 callback 的形式将其串联——这一来大大降低了其可读性,二来状态转换和管理容易出错;并且对变量生命周期需要精细管理,否则就会出现悬垂指针等内存问题。

为什么不试试神奇的 Rust 呢? Rust 语言通过引入所有权模型,在不引入垃圾回收的情况下保证了内存安全;并且通过语言内置的异步抽象,支持了 async + await 的异步编程模式。使用一个优秀的 Runtime ,即可像写 Golang 一样流畅地在 Rust 中平铺直叙地写异步代码——而性能并不输于 C++。

Rust Runtime 与 thread-per-core 模型

与 Golang 不同,Rust 语言中标准库并没有提供异步运行时(Runtime),只提供了必要的结构抽象。Runtime 负责与操作系统打交道,并对齐标准库的 Future 和 Waker 等定义,用户可以自主选择 Runtime 。

当前被广泛使用的 Runtime 是 Tokio ,它提供了类似 Golang 调度器的实现,用户的 Task 可以在多个线程之间被调度,较为有效地利用了多核心的性能。

但问题也随之而来:在部分强依赖高性能低延迟的场景下,调度带来的开销反而是用户不希望看到的。在核心数较多的情况下,调度开销反而会抵消调度带来的好处。

Nginx 和 Envoy 这类组件往往使用 thread-per-core 模型,即多少核心就运行多少线程,一个任务一旦被一个线程所接收,它后续的处理则都在该线程上。这种做法下几乎没有跨线程的开销,提升了 CPU 利用率,可以较好地保持系统的线性扩展性。此外,由于没有跨线程,处理逻辑也可以尽可能地利用 thread local 的便利,多数时候无需加锁便可操作共享数据。

面向这类场景,Monoio 基于 io-uring 致力于提供最佳的性能;另外,我们还定义了一个更适合 io-uring 的 IO trait 。

性能

我们对比了 Monoio 、Tokio 和 Glommio (另一个类似的 Runtime ,但在性能目标上不如 Monoio 激进)。

在绝大多数测试中,Monoio 都具有更低的延迟和更高的吞吐。对比 Tokio ,在多核场景下 Monoio 可以提供 2 到 3 倍的性能提升(原因主要在于模型上,没有了跨线程同步开销);而对比 Glommio ,我们可以在降低延迟的同时,节省约 1/4 到 1/3 的 CPU 占用(性能提升在于更优的调度实现,io-uring 批量 submit )。

更进一步的测试报告和设计上的权衡在 Github Repo 中有详细的文档。

另外,我们还对比了生产中使用的(epoll based) Nginx ,在 Proxy 场景下基于 Monoio 写的 TCP 代理可以获得差不多的性能(连接数较多时 Monoio 性能优于 Nginx ,较少时差于 Nginx ,数据整体上差不多)。与 Envoy 的 TCP Proxy 对比也表明 Monoio 有非常明显的性能优势。

结语

Monoio 提供了 thread-per-core 场景下最高性能的 Runtime 实现。我们的目标是能够让 Rust 在高性能场景下成为替换 C/C++ 的更好选择。目前字节已经开始基于 Rust 和 Monoio 构建下一代 Service Mesh 。

当然,没有什么 Runtime 是绝对最佳的选择,Runtime 的选型还是要根据具体的业务场景来。希望我们的 Monoio 可以给某些场景用户多一种选择。

在 Monoio 的设计和实现中我们大量参考了 Tokio 等同类产品,感谢这些项目的贡献者;也希望 Monoio 能够在大家的共同努力下变得更加完善更加易用。

参考资料

另外,在开发过程中我也总结了一些东西,写成了几篇博客,感兴趣可以看这里:Rust Runtime 设计与实现

9673 次点击
所在节点    分享创造
49 条回复
Mithril
2021-12-09 09:33:12 +08:00
BS:你们为了不学 C++也真是费尽心机了
fyooo
2021-12-09 09:40:47 +08:00
谢谢分享,字节很多开源项目都充满干货啊
ihciah
2021-12-09 09:48:09 +08:00
@Mithril 用 Rust 上线更安心~

@fyooo 谢谢支持,也欢迎大家参与!
PureWhiteWu
2021-12-09 09:48:17 +08:00
Monoio 之父 YYDS
Kilerd
2021-12-09 09:55:01 +08:00
thread-per-core 的一个问题在于没有 work-steal ,那么就很有可能存在一核干活,N 核围观的情况。这十分考验任务进哪个 thread 的调度。

tokio 作为一个通用的 async runtime 必然是要设计成 work-steal 的。

但是一个问题是,你们有没有试过你们的产品和 自己启动 thread ( thread 里面各自启动一个 tokio 的 single thread 的 rt ) 的性能比较呢? 这样的测试可能才是对等的。

另外,Rust 现阶段跟 io-uring 并不是很搭,不知道你们是怎么解决 buffer 的安全性问题的。
kernelerror
2021-12-09 09:55:48 +08:00
支持👍
RtfscRtfm
2021-12-09 09:57:44 +08:00
Monoio 之父 YYDS
ihciah
2021-12-09 10:00:52 +08:00
@Kilerd 是的,我们的场景上和 Tokio 是不同的。
可以看和 tokio 单线程的对比数据(这时其实主要差别就在于 epoll 和 io-uring 了)。
buffer 我们采用的 Tokio-uring 的做法,直接拿所有权,用完还回去。
CatCode
2021-12-09 10:32:33 +08:00
感谢开源
Croxx
2021-12-09 11:06:34 +08:00
在 github 看到之后就在找作者的 twitter ,没想到在 v 站看到了😂
coeru
2021-12-09 11:39:17 +08:00
成功
abcbuzhiming
2021-12-09 11:52:02 +08:00
@Mithril 我看了 rust 之后觉得 rust 的学习曲线没有比 c++低到哪里去,可能唯一的优势就是没有 C++那么多的特性
libook
2021-12-09 11:57:59 +08:00
最近刚看完 Rust 的教程 ,不知道以后能不能用 Rust 写微服务。
PureWhiteWu
2021-12-09 11:59:46 +08:00
@libook 当然能用,可以期待一下我们即将开源的微服务框架~
tulongtou
2021-12-09 12:53:25 +08:00
支持。话说这是孵化的产品,还是已经再生产环境使用了?
mywaiting
2021-12-09 12:58:27 +08:00
虽然看不懂,但是看到 Rust 写的我就想点赞
ihciah
2021-12-09 13:17:12 +08:00
@tulongtou 目前公司内部计划基于这套 Runtime 做下一代高性能 MeshProxy ,但尚无生产实际使用。开源出来也是希望能够和大家一起建设生态:)
CSM
2021-12-09 13:25:43 +08:00
提个建议,叫 Rust async runtime ,只看 Rust runtime 容易摸不着头脑
araaaa
2021-12-09 13:29:07 +08:00
啥时候支持多平台
tulongtou
2021-12-09 14:01:20 +08:00
@ihciah 挺好的,希望项目能长期发现下去。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/821027

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX