Conversation
…ernel The gfx1250 bring-up split cache-routing dispatch into three non-replay phases and removed the replay branch, so MoriCombine.backward's replay dispatch sent no payload, leaving grad_x stale and causing exploding grad norms. Re-add the replay path: read (destPe, destTokId) back from the cached dispDestTokIdMap and scatter weights, indices, scales, and tokens. Token counters are intentionally untouched because the host derives total_recv from the routing handle in replay mode.
The replay-correctness test only checked routing-map immutability and the recv token COUNT, so the ROCm#532 regression that dropped the replay branch of EpDispatchIntraNodeKernel_body (replay dispatch moved zero payload) still passed. Route the same grad payload through a fresh cache-routing op and compare downstream combine outputs (source-token order, invariant to recv-slot assignment). Fails on the buggy kernel, passes with the restored replay branch. Co-authored-by: Cursor <cursoragent@cursor.com>
The cache-routing reference op in the replay-correctness payload guard still relied on a routing handle, making it vulnerable to the same class of bugs as the replay path. Switch the reference to the fully independent default path: default dispatch populates the op-owned routing maps, so the combine reads them directly and serves as a bug-independent oracle for the scattered payload.
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.
Motivation
Restore correctness of replay-mode dispatch, which is used in MoE training
backward pass. Since #532 (
ae9635f7, "feat(ep/intranode): bring MoE dispatch/combineup on gfx1250 (MI450)"), intra-node replay-mode dispatch moved no payload, so the
scattered activations/gradients were left stale. In Megatron-LM MoE training this surfaced
as an exploding gradient norm and immediate divergence,
while the forward loss stayed exactly correct.
The forward pass was unaffected because it uses cache-routing dispatch + replay-routing
combine (both intact). Only the backward pass uses replay-routing dispatch
(
MoriCombine.backward→op.dispatch(routing=...)), which was the single broken path.Technical Details
#532refactoredEpDispatchIntraNodeKernel_body(src/ops/dispatch_combine/intranode.hpp)into three block-cooperative phases (count tokens → reserve slots → scatter payload), and
gated all three on
if (args.tokenIndices && args.inpTokenBuf && !args.replayMode).This inadvertently dropped the pre-#532
elsebranch that handled replay routing, so inreplay mode the kernel skipped every phase and moved zero payload into
dispatchOut(
grad_xstayed stale).Fix: re-add the replay branch. In replay mode we recover
(destPe, destTokId)from thedispDestTokIdMapwritten by the matching cache-routing dispatch, then copy metadata(indices, weights, scales) and the token payload — exactly the pre-#532 behavior.
destPeTokenCounter/dispTokOffsetare intentionally left untouched: in replay mode thehost takes
total_recvfrom the routing handle rather than the kernel's signal, and thecompletion handshake is a pure ordering barrier.
Why existing tests missed it
test_replay_correctnessexercised replay-dispatch but only asserted routing-mapimmutability and the recv token count. A replay dispatch that moves zero payload leaves
the routing maps untouched and preserves the count, so all assertions passed while no
activation was scattered.
Regression guard added: after the replay dispatch, route the same payload through a
fresh op on the fully independent default path (no routing handle) and compare downstream
combine outputs. Combine outputs are in source-token order, so the check is invariant to
the recv-slot assignment that differs across ops; a broken replay (stale/zero payload)
diverges by ~activation magnitude and trips the assertion.
Files changed
src/ops/dispatch_combine/intranode.hpp— restore replay-mode payload dispatch.tests/python/ops/test_dispatch_combine_routing_handle.py— payload guard in_replay_correctness(covers IntraNode + InterNodeV1, single- and two-node).Test Plan
pytest tests/python/ops/test_dispatch_combine_routing_handle.py -k replay_correctnesson 8 GPUs, covering:
test_replay_correctness[intra]— IntraNode.test_replay_correctness[v1]— InterNodeV1, single-node.test_replay_correctness_v1_two_nodes— InterNodeV1, 2-node RDMA (gpu_per_node=4).Submission Checklist