Coalesce communication channel notifications - #815
Open
kboniadi wants to merge 2 commits into
Open
Conversation
Preserve an immediate first-message wake while batching redundant notifications until the pusher's done boundary. Close input batches explicitly and cover same-epoch reactivation and inter-thread tail delivery.
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.
Problem & Solution Overview
Timely's communication counters currently enqueue a channel activation for every pushed message and for the final
done()call. The worker later sorts and deduplicates those channel IDs, so a batch ofNmessages createsN + 1notifications even though most collapse into one useful scheduling decision. This adds MPSC traffic, thread wake attempts, event-queue growth, and deduplication work under bursty or backpressured workloads.Timely previously had done-only coalescing, but it was disabled because some producers did not reliably close batches and because waiting until
done()delayed receiver pipelining. This change uses bounded coalescing instead: notify immediately for the first message, suppress intermediate notifications, and emit one trailing notification for a multi-message batch. Inputflush()andsend_batch()now establish explicit batch boundaries so same-epoch input remains responsive after a worker becomes idle.Summary
N + 1notifications to one notification for a single-message batch or at most two for a multi-message batch.InputHandle::flush()andsend_batch()close their batches;send_batch()closes exactly one combined buffered/direct batch.Pushimplementations may coalesce follow-up notifications untildone().Details
pingpongscenario.done()calls produce no notification, and single-message batches do not need a trailing wake.done()when dropped. The input changes supply the missing boundaries identified in issue Communication: coalesce counters #243.done()boundary can still defer later messages after the first wake. Eliminating that requirement would need a more invasive receiver-acknowledged atomic state spanning allocator event delivery.send_batch()restores one notification per direct batch--the same useful activation behavior that per-message notification previously provided.Performance Results
The example syntax is
exchange <batch> <rounds> -w <workers>:batchis the number of records inserted at each logical timestamp/epoch.roundsis the number of timestamps/epochs executed.-wis the number of Timely worker threads.For example,
exchange 1 200000 -w 8runs 200,000 one-record epochs across eight workers. This intentionally emphasizes per-batch notification and scheduling overhead rather than record-processing throughput.Measurements used release builds on local macOS and alternated clean
masterand patched executions to reduce run-order bias.Notification hot-path microbenchmark
This synthetic benchmark wraps a no-op data pusher with the inter-thread counter and drains its notification channel on another thread. It measures only sender-side notification-channel sends and wake attempts; it does not execute Timely operators, progress tracking, exchange routing, serialization, or record processing.
masterThis result establishes the direct mechanism-level saving. It is not an expected whole-dataflow speedup.
End-to-end: controlled worker scaling
The batch size and epoch count are fixed at
exchange 1 200000; only the worker count changes. Each row reports the median of ten runs per revision.mastermedianThe near-neutral one-worker result is expected: it uses intra-thread delivery and avoids the MPSC sends and cross-thread wakeups targeted by the largest part of this change. The benefit grows with worker count as those costs become a larger share of each one-record epoch.
End-to-end: larger record batch
This row reports the median of eight runs per revision and demonstrates that the change remains beneficial when useful record-processing work dominates notification overhead.
mastermedianexchange 100000 5000 -w 2Steady-state
pingpongremained neutral. The intended benefit is lower scheduling overhead and bounded notification backlog for frequent batch boundaries, bursty senders, and lagging receivers--not a claim that every Timely workload becomes faster.Result scope summary
exchange 1 200000, 1-8 workersexchange 100000 5000 -w 2pingpongTesting Done
This change added tests and was verified as follows:
Testing Done
send_batch()and explicitflush()reactivation coveredValidation completed:
cargo test --workspace --quiet: 265 passed, 48 ignored, 0 failedtimelyandtimely_communicationwith warnings denied after allowing only lint classes already present on cleanmasterunder the current Rust toolchaincargo test -p timely_communication --test counterscargo test -p timely --test coalesced_inputgit diff --checkexchangebenchmark