feat: reduce StreamExt write state size (#11)
Summary
- make idle read and write states zero-sized instead of storing cloned
Streamhandles- clone
StreamExt::inneronly while a read, flush, or shutdown future is in flight- invalidate pending read/EOF state when
inner_mut()is requested, so replacing the inner stream switches reads safely- add layout and inner-replacement regression coverage
Why
Both state machines permanently embedded additional
Streamhandles inStreamExt. SinceStreamis larger than the boxed-future variants, this increased the adapter’s inline size and made downstream enum usage more likely to hitclippy::large_enum_variant.The read side also kept a second tracked handle alongside its in-flight future solely to detect
inner_mut()replacement. Moving invalidation intoinner_mut()removes that handle while preserving the important behavior: a wait or cached EOF from the old stream cannot affect reads from the replacement.Behavior
Writes use
StreamExt::innerdirectly. Flush and shutdown futures continue to own a cloned handle, so they remain'static, preserveUnpin, and finish against the stream where the operation started.Reads clone the current inner stream only when creating the wait future. Calling
inner_mut()cancels any pending read and clears cached EOF before exposing the inner stream mutably.Validation
cargo fmt --checkcargo clippy --all-targets --all-features -- -D warningscargo test --lib(56 passed, 1 ignored)cargo test --test test stream_ext_ -- --nocapture(2 passed)
版权所有:中国计算机学会技术支持:开源发展技术委员会
京ICP备13000930号-9
京公网安备 11010802047560号
Shmipc
English | 中文
简介
Shmipc 是一个由字节跳动开发的高性能进程间通讯库。它基于 Linux 的共享内存构建,使用 unix/tcp 连接进行进程同步,实现进程间通讯零拷贝。在 IO 密集型场景或大包场景能够获得显著的性能收益。
特性
零拷贝
在工业生产环境中,Unix domain socket 和 Tcp loopback 常用于进程间通讯,读写均涉及通讯数据在用户态 buffer 与内核态 buffer 的来回拷贝。而 Shmipc 使用共享内存存放通讯数据,相对于前者没有数据拷贝。
批量收割IO
Shmipc 在共享内存中引入了一个 IO 队列来描述通讯数据的元信息,一个进程可以并发地将多个请求的元信息放入 IO 队列,另外一个进程只要需要一次同步就能批量收割 IO.这在 IO 密集的场景下能够有效减少进程同步带来的 system call。
性能测试
源码中 bench.rs 进行了 Shmipc 与 Unix domain socket 在 ping-pong 场景下不同数据包大小的性能对比,结果如下所示: 从小包到大包均有性能提升。
快速开始
HelloWorld
0.2 读取 API 迁移
0.2 将流式读取与指定长度的连续读取拆分为两个接口:
AsyncRead时,将Stream::read()替换为Stream::read_chunk();每次 返回一个非空的连续 buffer slice。size字节时,将Stream::read_bytes(size)替换为Stream::read_exact_bytes(size);跨 slice 时允许拼接为 owned buffer。旧接口名不保留兼容别名。
协议配置
Config::default()保持 legacy 行为:file-path 共享内存使用 V2 协议,memfd 共享内存使用 V3 协议。V4 需要通过嵌套的 protocol 配置显式启用:模式选择:
Config::default()Config::default().with_v4()Config::default().with_v4_eventfd()Config::default().with_v4_event_queue_polling(interval)服务端接入说明:
Config::default()即可同时接受 legacy V2/V3 客户端和 V4 客户端。V4 可选 wakeup feature 会根据客户端请求协商选择。eventfd_wakeup要求 memfd 共享内存。MemMapType默认就是 memfd;如果客户端需要 V4 eventfd,不要把服务端切到MemMapTypeDevShmFile。fallback = true。客户端仅在 V4 初始化遇到网络/协议失败时回退到 legacy V3;如果服务端显式拒绝 V4 negotiation,会直接返回错误,不会静默降级。如果不希望 自动降级,可设置config.protocol.mode = ProtocolMode::V4 { fallback: false }。现有示例服务端可直接配合以下 V4 客户端使用:
从
examplespackage 运行:如果使用 struct literal 构造
Config,需要显式填写protocol,或者使用..Config::default()保留默认字段,避免升级后遗漏新增配置项。关闭语义
Stream::flush()的语义是数据已在本端入队,并按协商出的 wakeup 机制尝试通知对端;它不表示 对端已经 drain 共享内存队列或已经读到这些字节。SessionManager::close()以及 listener/session 关闭路径是 hard close。如果应用要求最后一帧在关闭前一定被对端读到,需要在关闭 session 前做应用层 ack 或 completion barrier。