[TRTLLM-15178][fix] Pad an empty attention-DP scheduled batch so the fleet can make forward progress - #17379
Conversation
…n make forward progress
`_can_queue` vetoes the forward pass on every attention-DP rank when any one
rank's *scheduled* batch is empty:
tp_batch_sizes = self.dist.tp_allgather(scheduled_batch.batch_size)
can_queue = 0 not in tp_batch_sizes
`_pad_attention_dp_dummy_request` is meant to make that unreachable, but it
guarantees a different invariant -- every rank has at least one *active*
request -- and it necessarily runs before `_schedule()`, so the capacity
scheduler's verdict cannot inform it. A rank whose only active request does not
fit the free KV cache therefore counts as one active request, receives no
padding dummy, and schedules an empty batch.
A rank-local cache shortage then stalls the whole fleet: peers never execute the
context chunks they did schedule, because `_update_request_states` -- the only
caller of `move_to_next_context_chunk()` -- runs under `if can_queue:`. No chunk
completes, so no KV cache is released, so the starved rank stays starved.
Nothing on that path raises or times out, so the stall is silent: clients see
zero errors and zero progress. On a disaggregated context server, where the
starved rank is waiting on its own in-flight cache transfers to release the
blocks it needs, the state can persist indefinitely.
Fix: after `_schedule()`, a rank left with an empty batch appends a generation
dummy to the scheduled batch. Rank-local by design -- `_schedule()` performs a
`tp_allgather` inside `_balance_adp_requests`, so re-scheduling on only the
empty ranks would desynchronize that collective. A generation dummy is used
rather than the context dummy the pre-schedule path would pick, because a rank
that is empty precisely because it is short of KV cache must not be asked for
`max_num_tokens` worth of it. Every allocation failure degrades to today's
behaviour rather than propagating, since all ranks have yet to agree on
`can_queue` and a rank-local raise would strand the peers in the collectives
that follow. The pipeline-parallel loop does not call
`_prepare_and_schedule_batch` and is unaffected.
Verified on 4-node disaggregated attention-DP context servers on GB200 and
GB300: the stall reproduced deterministically without the change, and with it
the empty-batch condition still occurs and the fleet drives through every
occurrence.
Signed-off-by: Xiao Wang <24860335+xwang233@users.noreply.github.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Enterprise Run ID: 📒 Files selected for processing (1)
WalkthroughAttention-DP scheduling now pads eligible rank-local empty batches with generation dummies. Allocation failures do not raise. Dummy resources integrate with cleanup and rollback rules. Tests cover eligibility, limits, failures, admission, cleanup, and rollback behavior. ChangesAttention-DP padding
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant PyExecutor
participant Scheduler
participant KVCache
participant QueueAdmission
PyExecutor->>Scheduler: schedule active requests
Scheduler-->>PyExecutor: return rank-local batch
PyExecutor->>KVCache: allocate generation dummy for empty batch
KVCache-->>PyExecutor: return allocation or error
PyExecutor->>QueueAdmission: submit scheduled batch
QueueAdmission-->>PyExecutor: accept or reject batch
PyExecutor->>KVCache: roll back tentative dummy resources on rejection
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
tests/unittest/_torch/executor/test_py_executor.py (1)
1740-1755: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueAnnotate the new test helpers.
Annotate
_run_pad_emptyand_unfittable_rankparameters and return values.As per coding guidelines, “Annotate every function.”
🤖 Prompt for 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. In `@tests/unittest/_torch/executor/test_py_executor.py` around lines 1740 - 1755, Add type annotations to the parameters and return values of the test helpers _run_pad_empty and _unfittable_rank. Use the existing types for stub and scheduled_batch, and annotate _unfittable_rank with the tuple type matching the returned executor stub and ScheduledRequests instance.Source: Coding guidelines
🤖 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 `@tensorrt_llm/_torch/pyexecutor/py_executor.py`:
- Around line 6088-6092: Update the post-schedule dummy handling in
py_executor.py around _pending_adp_dummy_request so every padding dummy is
registered for failed-queue cleanup, independent of
_enable_dsv4_adp_dummy_fixes, and ensure failed queueing releases it and removes
it from active requests. Add a regression test in
tests/unittest/_torch/executor/test_py_executor.py covering
_enable_dsv4_adp_dummy_fixes=False and verifying cleanup after queue failure.
---
Nitpick comments:
In `@tests/unittest/_torch/executor/test_py_executor.py`:
- Around line 1740-1755: Add type annotations to the parameters and return
values of the test helpers _run_pad_empty and _unfittable_rank. Use the existing
types for stub and scheduled_batch, and annotate _unfittable_rank with the tuple
type matching the returned executor stub and ScheduledRequests instance.
🪄 Autofix
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: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: ab43b2fd-b84f-4416-aa82-6088df05c550
📒 Files selected for processing (2)
tensorrt_llm/_torch/pyexecutor/py_executor.pytests/unittest/_torch/executor/test_py_executor.py
…mments Address review feedback: the docstring and inline comments carried more detail than the code needs. Trim to the reasoning a reader must have -- why the padding runs after _schedule(), why it is rank-local, and why a generation dummy -- and leave the full analysis to the PR description. Signed-off-by: Xiao Wang <24860335+xwang233@users.noreply.github.com>
… model The post-schedule dummy was only registered for rollback under _enable_dsv4_adp_dummy_fixes, so on other models a fleet-wide can_queue=False left it parked with its KV cache allocated until the next successful forward. _pending_adp_dummy_request is written in exactly two places -- the DeepSeek-V4 branch of _pad_attention_dp_dummy_request and the new padding path -- and every other flow leaves it None, where _finalize_adp_dummy_allocation already returns early. Dropping the model gate from that method is therefore a no-op for the existing paths and closes the gap for the new one. Rollback is needed because the usual teardown, which terminates every attention-DP dummy in _handle_responses, runs only under 'if can_queue:'. Signed-off-by: Xiao Wang <24860335+xwang233@users.noreply.github.com>
|
/bot run --disable-fail-fast |
|
PR_Github #64425 [ run ] triggered by Bot. Commit: |
|
PR_Github #64425 [ run ] completed with state
|
… stub
`_pad_empty_attention_dp_batch` is called unconditionally from
`_prepare_and_schedule_batch` and reads `self.enable_attention_dp` on its
first line. `TestOneModelMTPDraftTokenScheduling` builds its executor with
`object.__new__(PyExecutor)` and never assigned that attribute, so the new
call raised:
AttributeError: 'PyExecutor' object has no attribute 'enable_attention_dp'
failing CPU-Generic-arm-1 in `unittest/_torch/executor`.
Set it to False on the stub, matching `test_benchmark_disagg.py`'s executor
stub, so the new padding path returns immediately for a test that does not
exercise attention DP.
Signed-off-by: Iman Tabrizian <10105175+tabrizian@users.noreply.github.com>
|
/bot run --disable-fail-fast |
|
PR_Github #64503 [ run ] triggered by Bot. Commit: |
Description
Under attention DP,
_can_queuevetoes the forward pass on every rank when any one rank's scheduled batch is empty:_pad_attention_dp_dummy_requestis meant to make an empty scheduled batch unreachable, but it guarantees a different invariant — every rank has at least one active request — and it necessarily runs before_schedule(), so the capacity scheduler's verdict cannot inform it. A rank whose only active request does not fit the free KV cache therefore counts as one active request, receives no padding dummy, and schedules an empty batch.A rank-local cache shortage then stalls the whole fleet:
can_queueeverywhere, so peers never execute the context chunks they did schedule —_update_request_states, the only caller ofmove_to_next_context_chunk(), runs underif can_queue:;Nothing on that path raises or times out, so the stall is silent: clients see zero errors and zero progress. On a disaggregated context server, where the starved rank is waiting on its own in-flight cache transfers to release the blocks it needs, the state can persist indefinitely.
Fix
PyExecutor._pad_empty_attention_dp_batch(), called immediately after_schedule()— the condition it repairs does not exist until the capacity scheduler has returned its verdict. A rank left with an empty batch appends a generation dummy to the scheduled batch.Design points:
_schedule()performs atp_allgatherinside_balance_adp_requests, so re-running scheduling on only the empty ranks would desynchronize that collective. The dummy is appended toscheduled_batch.generation_requestsinstead — pure rank-local state.max_num_tokensworth of it. A generation dummy costs1 + max_total_draft_tokenstokens, and peers already run generation dummies alongside real context chunks.OutOfPagesError,NoFreeSlotsError, insufficient capacity) degrades to today's behaviour rather than propagating: all ranks have yet to agree oncan_queue, so a rank-local raise here would strand the peers in the collectives that follow.max_num_active_requests, so it cannot trip theexpected_num_active_requestsassert in_pad_attention_dp_dummy_request; refuses to double-allocate the singletonATTENTION_DP_DUMMY_REQUEST_ID; honors the benchmark-disagg fill gate, which suppresses forwards on purpose._revert_gen_allocskips it, since reverting would shrink a real allocation._prepare_and_schedule_batchand is unaffected.Test Coverage
tests/unittest/_torch/executor/test_py_executor.py— nine new CPU-only cases covering the padding path: a rank with an unfittable active request gets a generation dummy in its scheduled batch; no-ops for non-empty batches, attention DP disabled, no active requests, an already-live dummy, and the active-request cap; graceful degradation on insufficient capacity and on either allocation error; and exclusion of the dummy from_revert_gen_alloc.Validated on hardware, 4-node disaggregated attention-DP context servers:
PR Checklist
[JIRA/NVBUG/None][type] SummaryDev Engineer Review
PyExecutornow pads rank-local empty scheduled batches with a generation dummy under attention data parallelism.QA Engineer Review
tests/unittest/_torch/executor/test_py_executor.pyfor:tests/integration/test_lists/.test-db/orqa/coverage entry was identified.