You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Bring WaitGroup completion to parity with waitgroup #397
Completing a group with one pending observer took nearly three times as long as waitgroup 0.1.2 in the original comparison. Both adapters create a group, register the observer, drop the worker handles and poll to completion. The four-worker case drops four handles sequentially on the same thread.
Apple M4 Max, macOS arm64, rustc 1.101.0-nightly. Each cell gives rank, elapsed time relative to Asyncband and the whole-batch median over 10 interleaved runs. Lower is faster. Ecosystem measurements
The baseline maintains a participant count as well as Arc ownership. Even the first observer uses a locked, cancellable waker collection. A candidate shares allocation ownership across participants, stores the first waker inline and uses the collection for additional observers. In two launches the one-worker medians were 81 vs 97 ms and 86 vs 97 ms (Asyncband vs waitgroup). With four workers they were 119 vs 125 ms and 127 vs 124 ms. That ranking changed between launches. Candidate and raw samples in PR #395
First contribution
Run the command on 36ba6f2 and on the candidate in #395 on the same machine. Report the two completion rows on independent launches, ideally also on Linux. The candidate adds a four-observer drain case. Check its before/after result separately because its peers compose a different multi-observer mechanism.
cargo x bench --bench ecosystem -- waitgroup/ --repeat 10 --raw
The goal is repeatable parity or better on both original rows while preserving completion visibility, independent observers, cancellation and wakeups. Measurements and correctness review on #395 are both welcome.
Could you assign this to me? I'd run waitgroup/ on 36ba6f2 and the #395 head on an Apple M4 (10 cores, macOS 27.0.1), with independent launches, then repeat on Linux. I'd report the two completion rows and the four-observer drain separately.
From #395's table, complete-4-workers is the row that's still behind (135 vs 127 ms), so I'd start there.
One question: waiters.rs uses RwLock only for writes. In a small standalone probe on this Mac, a fresh Mutex allocated 64 bytes on first lock and a fresh RwLock didn't. Was that the reason for choosing it?
I haven't run the benchmarks yet. I used Claude to help me read the code and write the probe.
Reproduced both scenarios on Apple M5 (10 cores), macOS 27.0.1, rustc 1.99.1 stable, in a disposable checkout of 629e073 (main), with cargo x bench --bench ecosystem -- waitgroup --repeat 10 --raw. Cells give the median of ten repetitions, with min..max ranges.
Scenario
Rounds
Asyncband
waitgroup
complete-waiter
3,500,000
175 ms (172..450)
61 ms (61..94)
complete-4-workers
2,600,000
170 ms (168..173)
66 ms (65..67)
A #[global_allocator] counting probe over 1,000,000 complete-waiter rounds records three allocations per round for Asyncband (208 bytes in total) against one (40 bytes) for waitgroup 0.1.2. Backtraces identify all three sites, and each recurs on every round:
80 B: the Arc<State> allocation in WaitGroup::new
64 B: pthread-mutex boxing when the waiters lock is first taken in Wait::poll (macOS only; futex-based targets lock inline)
64 B: the first Arena<Waker> insert for the registered waker, also under Wait::poll
I then picked up #395 (head 540858b) and re-ran the same protocol back-to-back in the same session (the waitgroup columns below come from that session's run). Both registered scenarios close the gap, and the fan-in and drain scenarios the branch adds sit at parity or better:
On the #395 tree the same probe records one allocation per round — 56 B, 16 B above waitgroup's 40 B, covering the inline first-waker slot. In drain-4-observers the comparison set also includes a Tokio TaskTracker adapter at 352 ms (351..370), 2.73x Asyncband's time.
PR #421 continues #395 on top of current main: the series rebases cleanly — git range-diff shows three commits identical and only the CHANGELOG entry moving to the current Unreleased section — and cargo x test and cargo x lint pass on the rebased branch.
Activity
rokadepiyush49-rgb commentedon Nov 6, 2026
Could you assign this to me? I'd run
waitgroup/on36ba6f2and the #395 head on an Apple M4 (10 cores, macOS 27.0.1), with independent launches, then repeat on Linux. I'd report the two completion rows and the four-observer drain separately.From #395's table,
complete-4-workersis the row that's still behind (135 vs 127 ms), so I'd start there.One question:
waiters.rsusesRwLockonly for writes. In a small standalone probe on this Mac, a freshMutexallocated 64 bytes on first lock and a freshRwLockdidn't. Was that the reason for choosing it?I haven't run the benchmarks yet. I used Claude to help me read the code and write the probe.
tisonkun commentedon Nov 6, 2026
Thanks for your interest! We don't assign issues to individual contributors. Anyone is welcome to submit a PR directly for review.
nanami-0713 commentedon Nov 9, 2026
Reproduced both scenarios on Apple M5 (10 cores), macOS 27.0.1, rustc 1.99.1 stable, in a disposable checkout of
629e073(main), withcargo x bench --bench ecosystem -- waitgroup --repeat 10 --raw. Cells give the median of ten repetitions, with min..max ranges.A
#[global_allocator]counting probe over 1,000,000complete-waiterrounds records three allocations per round for Asyncband (208 bytes in total) against one (40 bytes) for waitgroup 0.1.2. Backtraces identify all three sites, and each recurs on every round:Arc<State>allocation inWaitGroup::newwaiterslock is first taken inWait::poll(macOS only; futex-based targets lock inline)Arena<Waker>insert for the registered waker, also underWait::pollI then picked up #395 (head
540858b) and re-ran the same protocol back-to-back in the same session (the waitgroup columns below come from that session's run). Both registered scenarios close the gap, and the fan-in and drain scenarios the branch adds sit at parity or better:629e073On the #395 tree the same probe records one allocation per round — 56 B, 16 B above waitgroup's 40 B, covering the inline first-waker slot. In
drain-4-observersthe comparison set also includes a TokioTaskTrackeradapter at 352 ms (351..370), 2.73x Asyncband's time.PR #421 continues #395 on top of current main: the series rebases cleanly —
git range-diffshows three commits identical and only the CHANGELOG entry moving to the current Unreleased section — andcargo x testandcargo x lintpass on the rebased branch.