Channel: Add batched append! and take! operations#16
Open
adienes wants to merge 1 commit into
Open
Conversation
Add `append!(c::Channel, iter)` and `take!(c::Channel, n[, buffer])` for moving many items through a channel at once. For a buffered channel, both operations acquire the channel lock once and copy a whole run of items into or out of the internal buffer before releasing it, rather than locking, notifying, and updating bookkeeping per item as a `put!`/`take!` loop does. This drastically reduces the number of lock and notify operations and is substantially faster when throughput matters. Bulk transfer is restricted to the cases where it is safe and beneficial: `append!` only batches when the source is an `AbstractArray` whose elements already have an acceptable type, and otherwise falls back to a plain `put!` loop with identical semantics; `take!(c, n)` batches drains from any buffered channel. The operations are not atomic: items may become visible to consumers before the call returns, and a batched `take!` returns however many items were available when the channel closes. Both are exception-safe, leaving the channel's buffer and availability count consistent if iterating the source or copying an element throws. This revives the idea explored in JuliaLang#56473. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.
Add
append!(c::Channel, iter)andtake!(c::Channel, n[, buffer])for moving many items through a channel at once.For a buffered channel, both operations acquire the channel lock once and copy a whole run of items into or out of the internal buffer before releasing it, rather than locking, notifying, and updating bookkeeping per item as a
put!/take!loop does. This drastically reduces the number of lock and notify operations and is substantially faster when throughput matters. Bulk transfer is restricted to the cases where it is safe and beneficial:append!only batches when the source is anAbstractArraywhose elements already have an acceptable type, and otherwise falls back to a plainput!loop with identical semantics;take!(c, n)batches drains from any buffered channel.The operations are not atomic: items may become visible to consumers before the call returns, and a batched
take!returns however many items were available when the channel closes. Both are exception-safe, leaving the channel's buffer and availability count consistent if iterating the source or copying an element throws.This revives the idea explored in JuliaLang#56473.
Benchmarks
Measured on this branch (
julia 1.14.0-DEV, single thread). Each figure is the minimum wall time over 20 trials, each on a fresh channel sized so nothing blocks — isolating the per-item overhead the bulk path removes.Producer —
append!(c, data)vs a per-itemput!loop:put!loopappend!Consumer —
take!(c, n)vs a per-itemtake!loop:take!looptake!(c, n)Even versus the best you can already do without these methods — manually grabbing the lock once and
push!-ing in a loop — the bulk copy is still several times faster, because it skips the per-itemnotify/bookkeeping and uses a singlecopyto!:append!The headline gains are on throughput in an uncontended buffer, which is what the per-item overhead dominates. Under heavy consumer contention the producer-side change still helps by cutting wakeup traffic (one
notifyper chunk instead of per item): an 8-thread test feeding 50k items through a 64-slot buffer to M competing consumers (which still take per-item) showed a steady ~1.2–1.3× from M=2 to M=32.Reproduce
This pull request was written with the assistance of generative AI (Claude Code).