本文介绍了作者为解决特定需求而编写的batch-channel,包括其设计目标、实现过程中的困难及相关优化等方面:
- 设计目标:实现多生产者多消费者、同步异步支持、有界或无界、一次发送接收多个值且在稳态使用下无分配等。
实现难点:
- 通道实现中的
Waker问题:waiting_for_elements是Vec<Waker>,每次阻塞任务时都需分配内存,导致稳态下频繁分配和释放内存,影响性能。 - 使用侵入式列表优化:尝试将
Waker存储在阻塞的未来自身中以避免频繁分配,但在 Rust 中表达侵入式链表困难,如intrusive-collections中列表节点不能比列表存活更久等问题。 Pin相关问题:WakerList和WakerSlot构成自引用数据结构,需要使用unsafe代码,且Pin的特性导致语言假设值可移动,与自引用结构冲突,使公共 API awkward,如在函数参数的生命周期约束方面存在问题。- 处理未定义行为:
batch-channel的两个优化需要unsafe代码,为简化审计将其置于安全 API 和分离的 crate 中,Rust 中安全 Rust 无未定义行为,而unsafeRust 可能导致未定义行为,可通过希望最好、仔细思考或自动化 sanitizers 处理,Rust 支持多种 sanitizers,其中 MIRI 对捕获 Rust 别名模型违规很重要。 - Rust 别名模型问题:深入
unsafeRust 前应先了解相关指南,Rust 的别名规则与 C 不同,如在Box::leak和Box::from_raw等操作中易出现 MIRI 失败的情况,在处理链接列表的自引用指针时,需注意避免形成冲突的&mut引用,可通过UnsafeCell引入“mutability barrier”来解决,还可避免创建引用而完全在指针读写和偏移的域中操作。
- 通道实现中的
- 相关工作及研究:介绍了多个涉及侵入式列表的 crate,如
futures-intrusive等,以及pinned-mutexcrate 用于获取Pin<&mut T>,还提到 Rust-for-Linux 项目对Pin和自引用数据结构的关注及相关研究,如pinned initialization problem等。 - 结论:尽管编写过程痛苦,但最终得到了安全高效的 API
wakerset,从中学会要谨慎使用unsafe,明白 Rust 中引用比 C 中的指针更危险,Pin语法不佳但有望解决,UnsafeCell对于侵入式结构很重要,不清楚如何静态约束侵入式结构的生命周期关系,MIRI 在多线程压力测试中很关键,将经历写成文字和写代码一样困难。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用。你还可以使用@来通知其他用户。