Repository navigation
perf(mpsc): reduce intermediate copies in bounded receives - #407
Conversation
Signed-off-by: sanjay7178 <[email protected]>
orthur2
left a comment
There was a problem hiding this comment.
Thanks for working on this. The receive-side change looks good to me, but I'd drop the send-side part.
Benchmark follow-upRemoved the send hunk as requested; the implementation change now only simplifies receive result handling. The earlier combined before/after measurements did not establish the cause of the contended timing change. Measured on Linux x86-64 (AMD EPYC-Rome VM, 16 CPUs), rustc 1.99.1 / LLVM 23.1.1, optimized Cargo bench builds. All four variants use base Each cell is median ms (min–max); lower is faster.
The isolated ready case supports dropping the send change on this host. Contended timings remain noisy and platform-dependent; these data do not establish a lock-timing mechanism or resolve #401. The Raw recorded times in chronological order within each variant (ms) |
orthur2
left a comment
There was a problem hiding this comment.
Thanks for the update, LGTM.
If you'd like to keep going on #401, feel free to open a follow-up PR. |
Sure I'm on it |
|
Following up on the suggestion to continue #401, I tested several local designs after this PR merged. The most promising prototype uses receiver-local batches and atomic capacity returns for inline payloads of at least 1 KiB. These are results from a separate, unpublished experiment; the implementation merged in #407 remains the baseline. The receiver swaps buffers under the shared mutex and extracts payloads outside it. Each receive releases exactly one logical capacity unit. Capacity can be returned atomically when positive availability implies no linked sender waiters; otherwise it uses the existing locked FIFO grant path. Smaller payloads retain the original queue and plain capacity arithmetic. I also tested batching alone, applying the atomic path to all payload sizes, and replacing the mutex with parking_lot 0.12.5. Batching alone was inconclusive; the universal atomic path regressed small-message cases; the mutex replacement regressed the target workload. A cutoff above one machine word also regressed 64- and 256-byte payloads, leading to the current experimental 1 KiB cutoff. I examined Tokio's block queue and Crossbeam's stamped-slot queue as further design options, but did not implement those queue replacements. Baseline: merged commit Each cell is median ms (min–max); lower is better. Percentage changes are elapsed time, not throughput.
The contended 1 KiB and 4 KiB cases use four producers, one consumer, capacity 64, four Tokio workers, and 26 rounds. Ready cases use 1,000,000 rounds; try-roundtrip uses 3,000,000. The remaining 16/64/256-byte controls had overlapping baseline/candidate ranges; their modest median changes should not be read as established improvements. There are material tradeoffs: backing-buffer storage doubles for payloads >= 1 KiB (capacity 64 × 1 KiB goes from 64 KiB to 128 KiB, excluding metadata), the 1 KiB ready path regresses 7.6%, and all channels currently gain metadata and an additional counter allocation. Channel-construction cost was not benchmarked. The cutoff was selected on this VM; it needs independent hardware validation, especially macOS/ARM, plus large-message capacity/producer-count sweeps. These observations do not establish the precise cause of the timing changes or resolve #401. A separate 10-run ecosystem comparison for the 1 KiB contended case measured Asyncband at 367 ms (346–419), Tokio at 226 ms (192–256), AsyncChannel at 235 ms (196–282), and Flume at 666 ms (535–785). Asyncband still takes about 1.62× Tokio's elapsed time here. Validation passed: 44 MPSC integration tests, 5 public trait tests, Miri (41 passed, 3 OS-runtime-dependent tests skipped), and All final raw samples, in run order within each variant (ms)The additional task controls use 160 rounds for capacity one and 430 rounds for one producer/capacity 1024. Payload-sweep ready/task cases use 1,000,000/26 rounds respectively. |
Summary
Reduce intermediate payload copies in bounded MPSC receives by removing the payload/waker tuple from the receive path. Add matched inline and boxed benchmarks and ownership tests for cancellation, disconnection, and resumed sends.
A balanced four-variant benchmark on Linux x86-64 measured the retained receive-only change at 230.5 ms versus 250.5 ms for the inline ready case (16 samples per variant). Contended inline medians were 730 versus 747 ms with broadly overlapping ranges; this does not establish a significant contended throughput improvement. The send-side change has been removed following review. Full methodology and raw samples are in the benchmark follow-up comment.
Related to #401; further work is needed to close the larger throughput gap.
Validation
cargo x lint.