Add: early-dispatch for require_sync_start SPMD cohorts (a2a3 tmr)#1304
Add: early-dispatch for require_sync_start SPMD cohorts (a2a3 tmr)#1304ChaoWao wants to merge 2 commits into
Conversation
📝 WalkthroughWalkthroughThis PR adds a gated sync_start drain protocol with a doorbell rendezvous mechanism to the a2a3 scheduler: a new ChangesSync-start drain rendezvous feature
Estimated code review effort: 4 (Complex) | ~75 minutes Sequence Diagram(s)sequenceDiagram
participant Dispatcher as scheduler_dispatch
participant Completion as SchedulerContext (drain)
participant Tracker as CoreTracker
participant Scheduler as PTO2SchedulerState
participant Payload as PTO2TaskPayload
Dispatcher->>Completion: enter_drain_mode(consumer, logical_block_num)
Completion->>Completion: reset drain_stage_go, drain_stage_done_mask, drain_running_staged
Completion->>Completion: elect drain worker, publish sync_start_pending
Completion->>Completion: handle_drain_mode(thread_idx, out_ack_wait_cycles)
Completion->>Tracker: get_mix_split_cluster_offset_states / core_offset_to_pending
Completion->>Completion: drain_stage_cores(...) parallel staging
Completion->>Payload: seed running_slot_count for gated drain
Completion->>Completion: sync on drain_stage_done_mask, reopen gate
Completion->>Scheduler: propagate_dispatch_fanin
Scheduler->>Scheduler: maybe_rendezvous_ring(slot_state)
Scheduler->>Scheduler: ring doorbells when running_slot_count matches staged core count
Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Code Review
This pull request parallelizes the sync_start drain staging and introduces a rendezvous mechanism to prevent deadlocks during early dispatch. It also adds split placement for MIX shape blocks, updates completion and dispatch logic, and introduces profiling support for the drain phases alongside a new system test. The review feedback highlights two important issues: a potential out-of-bounds memory write in drain_stage_cores due to a missing bounds check on the core ID, and undefined behavior from signed integer overflow in the test kernel's spin loop.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@src/a2a3/runtime/tensormap_and_ringbuffer/runtime/scheduler/scheduler_context.h`:
- Around line 351-353: The `handle_drain_mode` profiling output is mislabeled:
`out_ack_wait_cycles` in `scheduler_context.h` still reads like ack-barrier
time, but the implementation records stage/finalize wall time. Update the
parameter name and surrounding comments to match the actual value, or change the
implementation if ack-wait was intended, so the meaning is consistent across
`handle_drain_mode` and its caller-facing profiling data.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 49a3f1c5-87c4-4fb6-9f7d-19acdf8ffe44
📒 Files selected for processing (13)
src/a2a3/platform/include/common/l2_swimlane_profiling.hsrc/a2a3/runtime/tensormap_and_ringbuffer/runtime/pto_runtime2_types.hsrc/a2a3/runtime/tensormap_and_ringbuffer/runtime/scheduler/pto_scheduler.hsrc/a2a3/runtime/tensormap_and_ringbuffer/runtime/scheduler/scheduler_cold_path.cppsrc/a2a3/runtime/tensormap_and_ringbuffer/runtime/scheduler/scheduler_completion.cppsrc/a2a3/runtime/tensormap_and_ringbuffer/runtime/scheduler/scheduler_context.hsrc/a2a3/runtime/tensormap_and_ringbuffer/runtime/scheduler/scheduler_dispatch.cppsrc/a2a3/runtime/tensormap_and_ringbuffer/runtime/scheduler/scheduler_types.hsrc/a5/platform/include/common/l2_swimlane_profiling.hsrc/common/platform/shared/host/l2_swimlane_collector.cpptests/st/a2a3/tensormap_and_ringbuffer/spmd_sync_start_early_dispatch/kernels/aiv/kernel_spmd_write_slow.cpptests/st/a2a3/tensormap_and_ringbuffer/spmd_sync_start_early_dispatch/kernels/orchestration/spmd_sync_start_early_dispatch_orch.cpptests/st/a2a3/tensormap_and_ringbuffer/spmd_sync_start_early_dispatch/test_spmd_sync_start_early_dispatch.py
0d08ed3 to
efbaed6
Compare
efbaed6 to
0ef84c8
Compare
Aligns the a2a3 tensormap_and_ringbuffer scheduler with one occupancy model: two task sources (normal ready ▸ speculative early), and within each source sync_start > MIX > C/V, idle > pending. - Share one staging-order skeleton. dispatch_ready_tasks and try_early_dispatch each carried a hand-mirrored copy of the same order (MIX strict priority, IDLE before PENDING, cross-thread idle gating). Extract it into one run_staging_order helper parameterized by the per-shape stage callback; the two sources now differ only in that callback (normal = pickup/is_ready, early = gated). Behavior-identical. - Ready sync_start as strict Tier-0 in normal dispatch. Normal sync_start cohorts previously rode ready_queues[shape] mixed with regular tasks, so an AIC/AIV sync waited behind regular MIX. Park them in dedicated per-shape ready_sync_queues[] and drain as a Tier-0 pass before regular ready work. The pass reuses dispatch_shape unchanged (now parameterized by the queue array): same fits-local-inline vs stop-the-world drain decision, per-core MIX placement, and per-task head-start spacing. The regular tier passes ready_queues exactly as before, so regular dispatch is byte-identical; the early-dispatch gate now also drains the sync lanes so early never preempts a queued normal sync. Scope: a2a3 tensormap_and_ringbuffer only; regular dispatch path unchanged. Independent of the sync_start early-dispatch work (#1304) — this adds only the normal-source sync tier plus the order unification. Testing: a2a3sim 13 scene tests; a2a3 onboard 8 scene tests + spmd_sync_start_ stress x30 (0 failures, no 507018); full CI green.
0ef84c8 to
642025f
Compare
Let require_sync_start SPMD cohorts pre-stage onto their cores while the producer still runs, then launch together via a unified doorbell, instead of waiting idle for the producer to fully retire. - Pre-stage gated: a flagged (allow_early_resolve) producer's dispatch bumps consumers' dispatch_fanin; a sync_start block that reaches fanin_actual_count stages onto its cores with not_ready=1 (spec_state STAGING) so it waits on a DATA_MAIN_BASE doorbell rather than launching early. - Rendezvous launch: running_slot_count counts staged gated CORES (shape- agnostic); when it reaches popcount(staged_core_mask) AND the producer has released, maybe_rendezvous_ring() rings every staged doorbell atomically so the cohort starts as one. - MIX split placement: a MIX cluster (1 AIC + 2 AIV) stages per-core into whatever running/pending slot each core has free, gated so partial placement is safe; count_mix_split_clusters gates availability. - Parallel drain: handle_drain_mode runs the stop-the-world sync_start drain across the dispatch threads (CAS-claimed block indices, per-thread local staged_core_mask merged with one fetch_or, reopen before propagate_fanin), replacing the serial stage that spread the cohort launch across the round. - pending_gated fix: a sync_start block stays gated after the producer releases (DISPATCHED), so pending_gated = STAGING || (DISPATCHED && sync_start); the STAGING-only test misclassified a released-but-still-gated pending block as a normal task and deadlocked waiting for an ack that never came. - Deadlock avoidance (early-candidate gate): propagate_dispatch_fanin no-ops until the producer is FULLY dispatched (next_block_idx == logical_block_num). A flagged SPMD producer with more blocks than cores (e.g. a 50-block AIC proj on 24 cores) dispatches in waves; if its first wave triggered a downstream MIX cohort to gate every core, the producer's remaining blocks would find no core, never complete, and the cohort's rendezvous -- which waits for that producer to release -- would never ring. Gating on full dispatch keeps the producer's cores committed before any consumer pre-occupies. next_block_idx is the gate because it advances on every placement path (normal dispatch, early-release, drain), covering all propagate_dispatch_fanin call sites. - Swimlane: Drain / DrainPrepare / DrainPublish sched phases (gated on PTO2_PROFILING + SCHED_PHASES) make the drain visible instead of a blind spot. - Docs: RUNTIME_LOGIC.md 8.6 (two-source x sync-tier dispatch model, drain + rendezvous, early-candidate gate, MIX per-core placement). - Tests: spmd_sync_start_early_dispatch drives a wide flagged producer into a gated sync_start consumer; spmd_sync_start_mix_spill exercises the MIX per-core pending-spill rendezvous path. Verified: qwen3-14B batch-16 decode completes at 1GB and 2GB ring heap (previously deadlocked every run -- Heap Exhausted at 1GB, running-stalled at 2GB); 14 a2a3 st tests pass (spmd / sync_start / mix / paged / stress). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
642025f to
c4cf46f
Compare
Update wiring tests to model partial and complete block claims, and turn the sync-start scene into the AIC50-to-MIX24 resource-cycle regression. Keep every dispatched batch item in the deferred propagation list so a final AIV claim beyond MAX_CLUSTERS cannot miss the only post-gate retry. Document next_block_idx as a reservation counter rather than a publication counter.
Summary
Let
require_sync_startSPMD cohorts pre-stage onto their cores while theproducer still runs, then launch together via a unified doorbell, instead of
sitting idle until the producer fully retires. Cohort launch collapses from a
serial per-core spread to a single atomic rendezvous.
allow_early_resolve) producer bumpsconsumers'
dispatch_fanin; a sync_start block reachingfanin_actual_countstages onto its cores with
not_ready=1(STAGING), waiting on aDATA_MAIN_BASEdoorbell rather than launching early.running_slot_countcounts staged gated cores(shape-agnostic); when it reaches
popcount(staged_core_mask)and theproducer has released,
maybe_rendezvous_ring()rings every staged doorbellatomically so the cohort starts as one.
whatever running/pending slot each core has free; gated, so partial placement
is safe.
count_mix_split_clustersgates availability.handle_drain_moderuns the stop-the-world sync_startdrain across the dispatch threads, replacing the serial stage that spread
cohort launch across the round.
releases (
DISPATCHED), sopending_gated = STAGING || (DISPATCHED && requires_sync_start()), else a released-but-still-gated pending block ismisclassified as a normal task and deadlocks on an ack that never comes.
propagate_dispatch_faninno-ops until every producer block range has reserved a core slot
(
next_block_idx == logical_block_num). A flagged SPMD producer with moreblocks than cores (for example, a 50-block AIC projection on 24 AICs) dispatches
in waves; if its first wave triggered a downstream MIX cohort to gate every
slot, the producer's unclaimed blocks would never dispatch and the cohort's
rendezvous would never ring.
next_block_idxis a reservation counter, not apublication counter; it covers normal dispatch, early staging, and drain claims.
prop_listnow matchesthe full per-thread batch capacity, so a final AIV claim after item 21 cannot
lose the only full-reservation propagation retry.
Drain/DrainPrepare/DrainPublishsched phases (gatedon
PTO2_PROFILING+SCHED_PHASES) make the drain visible.RUNTIME_LOGIC.md§8.6: two-source × sync-tier dispatch model,drain + rendezvous, early-candidate gate, MIX per-core placement.
Testing
ring heap — previously deadlocked every run (
Heap Exhaustedat 1GB,running-stalledat 2GB).AIC50 -> MIX24 require_sync_startregression passes ona2a3simand onboarda2a3; the pre-fix runtime stalls.full-claim propagation coverage.
spmd_batch_dispatch_oobalso passes.spmd_basic,spmd_multiblock_aiv,spmd_multiblock_mix,spmd_sync_start,spmd_sync_start_aiv,spmd_sync_start_early_dispatch,spmd_sync_start_edge,spmd_sync_start_mix_spill,spmd_starvation,spmd_sync_start_stress,spmd_batch_dispatch_oob,spmd_paged_attention,spmd_paged_attention_highperf,mixed_example— 14 passed.Notes
spmd_sync_start_mix_spill/exercises the MIX per-core pending-spillrendezvous path.
spmd_sync_start_early_dispatch/is the full-reservation gate regression:a 50-block flagged AIC producer feeds a 24-block MIX sync-start consumer.
Rebased onto #1308 / #1319 (per-core MIX placement + sync_start Tier-0 now
in main): reuses their per-core placement (
prepare_block_for_dispatchwithto_pending=true) and sharedrun_staging_orderskeleton.