用 io_uring 替换 mmap,Rust 引擎反而变慢了
We Replaced MMAP with Io_uring in Our Rust Query Engine. It Got Slower

在 Conviva 的 Rust 查询引擎中,我们原本依赖 mmap 实现零拷贝读取 Arrow IPC 文件。但在高并发生产环境下,共享的页面缓存导致严重的锁竞争和缺页中断,性能不升反降。为了突破瓶颈,我们尝试用 io_uring 配合 O_DIRECT 绕过内核页面缓存。结果令人意外:虽然主要缺页中断减少了 35 倍,但次要缺页中断激增,整体查询时间反而从 13.6 秒延长到了 21.8 秒。这次实验揭示了一个关键教训:盲目替换底层 I/O 机制而不重构调度策略,往往适得其反。
io_uring 本身并不能解决问题,如果没有更好的调度和并发控制,它也无济于事。
HN 评论区
21- jandrewrogers
这里的问题在于,在性能场景下,mmap 和 io_uring 需要根本不同的软件架构。你不应该直接替换它们。
像 io_uring 这样的 API,配合 O_DIRECT,允许你从零开始设计专用于特定工作负载的用户态调度器。如果你把调度工作委托给运行时环境,你就放弃了这些 API 旨在提供的大部分性能优势。在很多情况下,性能反而会更差。相比之下,mmap 隐式地将所有调度决策委托出去,如果你的策略是委托,那么相比于运行时环境,它有一些优势。
如果不承诺设计自己的调度器,io_uring 的优势就很有限。好在,熟练的调度器设计者使用这些 API 相比 mmap 可以将性能提升数个整数倍。设计应用专用的调度器并不容易,这是一项高技能的工作。但回报是真实的。
要善用 io_uring,就必须全力投入它所要求的软件架构,这样才能展现它的真正能力。
- laserbeam
真正的问题在于,文章末尾链接了第二部分,在那部分里他们让 io_uring 的实现比 mmap 快了两倍。所以第一部分就是个标题党,问题在第二部分得到了解决。
所以他们凑出了两篇文章,其中一篇纯粹是引战。而且这两篇看起来都是 LLM 写的。也许里面有些不错的建议,也许没有……但这不是我喜欢的阅读格式。
- londons_explore
软件工程里有个常见的套路:
* 项目存在
* 新员工提出效率改进的想法
* 花几个月去实现。旧代码路径变成遗留代码。
* 新东西在构建过程中被强行附加了额外功能。
* 新东西的效率结果比原来的还差,但现在驱动因素变成了“新功能”和“技术债务更少”。
* 新东西上线,旧东西弃用,但几个月的工作并没有带来什么实质性的好处。