refactor(mpsc): replace the legacy queue backend - #247
Conversation
|
OK. I'll review this PR tomorrow, or within the next few days. |
orthur2
left a comment
There was a problem hiding this comment.
Thanks for taking this on.
For reference, I ran these local benchmarks on an Apple M5 MacBook Pro (10-core CPU and arm64) running macOS 26.4.
I also wanted to check whether the bounded queue really needed the stamped ring. I replaced it locally with a naive single-lock Mutex<VecDeque> queue. That version passed the existing tests and removed all unsafe code from the MPSC module, but it was roughly 1.7–1.9x slower with four producers and 2.3–3.0x slower with eight. A more sophisticated safe implementation might do better, but the straightforward version would clearly regress the existing contention path. It would be useful to capture that tradeoff briefly in the Design Notes.
The description says that the full benchmark suite covers both channel flavors, but the only compare numbers shown are for bounded MPSC. Could we also include the unbounded figures and the machine and OS? On the same machine, the new unbounded backend was about 1.6x faster than the base with one producer and 7.3x faster with eight. Those numbers show the main performance benefit of this change much more clearly than the bounded-only table.
I would be happy to take another look once these comments are addressed.
|
@orthur2 All requested changes are addressed in 13a100e: the legacy |
Summary
std::sync::mpscbackends with MPSC-owned queue implementations while preserving the public sender, receiver, error, and auto-trait contracts.Syncimplementations and document the bounded slot's localized unsafe invariant.Closes #209.
Design Notes
The unbounded queue keeps sender-visible storage and receiver liveness behind one mutex, while the single receiver moves messages into a local batch to reduce shared-lock contention without introducing unsafe code.
The bounded queue allocates its capacity up front and uses per-slot generation stamps. Producers reserve slots through the tail cursor, while the single consumer advances the head cursor; cache padding prevents producer and consumer cursor updates from sharing a cache line.
A straightforward safe bounded implementation using one
Mutex<VecDeque>passed the existing tests and removed the MPSC unsafe code, but orthur2 measured it at roughly 1.7–1.9x slower with four producers and 2.3–3.0x slower with eight on an Apple M5 MacBook Pro (10-core arm64) running macOS 26.4. The stamped ring is retained to avoid that contention regression.Unsafe code is confined to initialized slot access and
Slot<T>: Sync. Each unsafe operation is paired with a documented reservation, publication, acquisition, and reuse invariant. The queue exposes no references to stored values, completes ownership transitions before user code can unwind, preserves the legacy endpoint auto-trait matrix, and exercises receiver-disconnect cleanup across a wrapped ring under Miri.Validation
cargo x testcargo x checkcargo x miri(74 tests, including wrapped receiver-disconnect cleanup)cargo x lint(Clippy, rustfmt, typos, license, and rustdoc pass; local Taplo reports pre-existing formatting differences in three unchangedCargo.tomlfiles)cargo x benchBenchmark Comparison
The following bounded MPSC medians were measured in adjacent runs on an Intel Core i7-11700K (8 cores/16 threads) running 64-bit Windows 11 Pro 10.0.26200, with 16,384 messages per sample.
In orthur2's comparison on the Apple M5 machine described above, the new unbounded backend was about 1.6x faster than the base with one producer and 7.3x faster with eight producers.
The existing ecosystem benchmark continues to cover bounded and unbounded channels with 1, 2, 4, and 8 producers and 16,384 messages per sample.