Repository navigation
[Bugfix][ROCm][MoRIIO] Wait for MoRIIO WRITE mode completions in short chunks - #64330
Merged
shen-shanshan merged 2 commits intoNov 3, 2026
Merged
shen-shanshan merged 2 commits into
shen-shanshan merged 2 commits into
Conversation
Signed-off-by: kliuae <kuanfu.liu@embeddedllm.com> Co-authored-by: Claude Opus <noreply@anthropic.com>
kliuae
requested review from
NickLucche,
wzhao18 and
xuechendi
as code owners
October 28, 2026 18:04
Contributor
|
No blocking issues. Reproduced the same stall on MiniMax-M3.1 1P1D over XGMI, which this PR covers. The sliced wait adds at most 10 ms per request there, and our slowest wait (2.1 s) is far from the 30 s bound. Data below. Evidence
|
Collaborator
|
Looks correct — no required changes from me. Two optional, non-blocking notes:
|
Contributor
|
/ci run --allow-stale |
|
✅ Triggered Buildkite CI #99493 for commit
|
shen-shanshan
approved these changes
Nov 3, 2026
6 tasks done
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.
Overview
In MoRIIO WRITE mode, the prefill (P) instance waits for a request's KV copies before it sends
write_doneto the decode (D) instance, and the READ barrier waits for its reads the same way. Both go throughMoRIIOWrapper.waiting_for_transfer_completewhich makes one MoRIwait_allcall with the wholetransfer_timeout. On MoRI's xGMI backend, that call polls each status once and then sleeps until its deadline, so a copy that is still running at that poll holds the wait for the full timeout (30s by default). This also happens on MoRI's scatter/gather copy path under load, where a wait that still seesIN_PROGRESSat its deadline fails the transfer and leaves the decode request waiting. This PR callswait_allin 10ms intervals, and checks the statuses between the intervals, up to the sametransfer_timeout.Claims
transfer_timeout30s: on main all 6 of 6 waits took 29.999 s, even though every one of the 118 statuses had succeeded. With this PR, 100 of 100 waits ended after 0.210 s (median; min 0.183 s, max 0.210 s), all succeeded.fail_fast=True(WRITE), or once no status is in flight withfail_fast=False(READ barrier), as the polling fallback already does. Timeouts and error messages are unchanged.MORI_IO_XGMI_SCATTER_GATHER_THRESHOLD=0(MoRI's scatter/gather copy path) on prefill, 16K-token prompts: on main, two of three runs at concurrency 8 left requests that never completed (92 and 95 out of 96), each after a completion wait that failed withIN_PROGRESSwhen its 30stransfer_timeoutexpired. In the run that finished, concurrency 16 reached only 2.28 req/s with a client TTFT p90 of 30.2 s. With this PR, all 96 requests completed in each of three runs, with no failed wait, at 6.96–7.20 req/s.Validation
Unit tests
tests/v1/kv_connector/unit/test_moriio_transfer_completion.py: the native wait returns once the statuses succeed, followsfail_fastafter a failure, and callswait_allin 10ms slices.tests/v1/kv_connector/unit/test_moriio_tp_ack.py: the READ barrier's native-timeout test runs on a fake clock and checks that the wait laststransfer_timeoutin slices of at most 10ms.Completion wait over xGMI
On MI355X with MoRI 1.2.3.post1 (no
MORI_IO_XGMI_SCATTER_GATHER_THRESHOLD, so MoRI uses its default copy path). A prefill process on GPU 0 writes 118 layers of 128 blocks (32 KiB each) into a decode process's buffer on GPU 1, onebatch_writeper layer, and waits for them throughMoRIIOWrapper.waiting_for_transfer_completewithtransfer_timeout=30, as the WRITE producer does beforewrite_done.reproducer (moriio_wait_repro.py)
P/D serving over xGMI
DeepSeek-V2-Lite-Chat, prefill and decode both served with TP1, MoRIIO WRITE over xGMI, using MoRIIO toy proxy,
MORI_IO_XGMI_SCATTER_GATHER_THRESHOLD=0on the prefill instance, benchmarked with ISL/OSL=16k/16.IN_PROGRESSOn main the unfinished requests are the ones whose transfer failed with the prefill write worker raising
TransferError("MoRIIO batch wait failed (StatusCode.IN_PROGRESS): RDMA transfer timed out after 30s") while the decode instance being told nothing, so the decode request kept waiting for its KV. In main's second run at concurrency 16, the waits returned at their 30s deadline instead of failing. As the write worker handles one transfer at a time, every transfer queued behind such wait was delayed too.serving and benchmark commands
Details
On MoRIIO's WRITE path of the prefill instance under PD disaggregated serving, for one request:
batch_writeon a MoRI session)._finalize_if_completewaits for every copy of the request (waiting_for_transfer_complete) and sendswrite_done.Step 4 makes one MoRI call with the whole timeout:
On MoRI's xGMI backend, a status completes only when it is polled, and a bounded
wait_allpolls each status once and then sleeps until its deadline. If the copies are still running at that one poll, the wait costs the wholetransfer_timeout; when the statuses are stillIN_PROGRESSat the deadline, the transfer fails withTransferError, and on main no failure notice reaches decode. With the default copy path the last layers are nearly always still running when step 4 starts, so the wait lasts the wholetransfer_timeoutand the request'swrite_donereaches D 30s late. As shown in the the reproducer above, however, every status had succeeded long before the call returned. On the scatter/gather path the copies are fast, but when prefill has already queued its next step, they complete only after that step, so under load the one poll misses them there as well.This PR addresses this by calling
wait_allwith at most 10ms and checking the statuses withpoll_transfer_batchbetween calls, untiltransfer_timeout:fail_fast=True, or once nothing is in flight withfail_fast=False(on main the native path ignoredfail_fastand always waited for the deadline);AI agent was used in this PR.
Pull Request Checklist
I used vLLM's
/pr-checklistskill. (Mandatory for agents, optional for humans).AI assistance was used during the creation of this PR.
Design Fit: Minimizes impact on core components, reuses existing functionality, and justifies added complexity.
Testing and Validation: Validates the change and ensures any added tests are meaningful and reliable, with CI coverage or documented CI resource constraints and validation performed outside CI.
Code Quality and Style: Keeps code and comments clear and concise, and updates relevant documentation and examples.
Pull Request Contents: Includes a brief summary and relevant links, supports claims with evidence, explains root causes and implementation trade-offs, and follows the contributing guide.