Repository navigation
perf(mpmc): back bounded queues with a ring buffer - #382
Open
ariesdevil wants to merge 1 commit into
Open
ariesdevil wants to merge 1 commit into
ariesdevil wants to merge 1 commit into
Conversation
ariesdevil
force-pushed
the
dev_ariesdevil
branch
3 times, most recently
from
October 21, 2026 07:31
2e2ded8 to
94d174b
Compare
Replace the bounded queue's VecDeque behind one mutex with a preallocated ring of stamped slots adapted from crossbeam-channel's array flavor. Sends, receives, and reservations take no lock unless a sender or receiver has to wait, while wait-queue order, exact capacity, permits, cancellation, and disconnection behave as before. Storage for the full capacity is allocated when the queue is created. The capacity that permits and grants hold, and a bit marking queued senders, live in the tail cursor beside the position, so every exchange on it compares the reservation state it was computed from. While senders are queued, a send with a permit grants the capacity that receivers free until it finishes, so a single receiver does not have to wake every queued sender itself. Positions are 64 bits, and capacity is limited to 2,097,151, so a stalled exchange can be fooled only after at least 2^40 sends. A 32-bit cursor would leave as few as two lap bits, so the crate now requires 64-bit atomics and supports x86, x86-64, ARMv6 or later, and AArch64. Copy CachePadded from crossbeam-utils and record the incorporated crossbeam portions in LICENSE, licenserc.toml, and .asf.yaml. Add benchmarks against crossbeam-channel: a spinning batch that isolates queue contention from wakeups, a capacity sweep, the 1P/8C topology, and a reservation micro-benchmark. Split the bounded adapter trait so channels without async operations can join the thread workloads.
ariesdevil
force-pushed
the
dev_ariesdevil
branch
from
October 21, 2026 08:39
94d174b to
4ea81c4
Compare
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Partial related: #298
Summary
Back the bounded MPMC queue (added in #265) with a preallocated ring of stamped slots, adapted from crossbeam-channel's array flavor, instead of a
VecDequebehind one mutex. Sends, receives, and reservations no longer take a lock unless a sender or receiver has to wait. Pendingsendandreserveoperations still receive capacity in wait-queue order, permits still hold capacity without a slot, and cancellation and disconnection behave as before. Storage for the full capacity is now allocated when a queue is created, capacity is limited to 2,097,151, and the crate now requires targets with 64-bit atomics.The unbounded queue keeps its mutex and drops the bounded-only code.
CachePaddedis copied from crossbeam-utils, andLICENSE,licenserc.toml, and.asf.yamlrecord the incorporated crossbeam portions.The benchmarks gain a crossbeam-channel adapter, a spinning batch that isolates queue contention from wakeups, a capacity sweep, the 1P/8C topology, and a reservation micro-benchmark.
Design Notes
tailbetween the slot index and the lap, so every exchange ontailcompares the reservation state it was computed from. The other values a sender reads,headand the slot stamps, only advance, so reading them early can only understate free capacity.try_reserveand dropping an unused permit take no lock unless senders are queued, and granting the last queued sender clears the queued bit in the same update. A grant removes the waiter's node and sets a flag in its pinned future, so the woken sender claims its permit without the lock.perfprofiles on x86. Keeping the count in its own word behind a version intailwas incorrect: 2^24 reservations wrap the version, after which a stalled send can claim capacity that a permit holds. A regression test covers that interleaving.Benchmarks
Changes against
ecc1780, from the medians of per-run Divan medians over rotated rounds, 10 on Linux and 6 on macOS, with rustc 1.99.0. Negative changes are faster. Each batch moves 16,384 messages, and capacity is 64 unless noted.ecosystemmpmc::boundedthread_batchthread_batch_capacity1024thread_batch_capacity1spin_batchtask_batch, current-threadtask_batch, 4 workersprimitivesmpmc::boundedtry_send_then_try_recvsend_then_recvtry_reserve_then_sendwake_blocked_senderOn Linux,
spin_batchtakes 0.49x to 0.97x of crossbeam-channel's time, and blocking 2P/2C and 8P/8C take 0.68x and 0.47x. Blocking 1P/8C takes 3.2x and capacity 1 takes 3.3x to 11x, becauseFutureExt::block_onparks without spinning first. The remaining regressions are on paths where every operation waits: capacity 1, a blocked sender's handoff, and 1P/8C, where the producer wakes a parked consumer for most messages. On macOS, a multi-thread Tokio runtime with one producer is also slower.The unbounded queue changed by −6% to +2%. Competitor controls in the same binaries moved by a median of −0.1% on Linux and −0.2% on macOS, and 102 of 112 Linux and 95 of 112 macOS control cells stayed within ±5%.