Repository navigation
Conversation
added 2 commits
October 27, 2026 22:48
…hout the host read On HIP the DSA decode and verify graphs come in a dense and a sparse variant, chosen per replay by the batch's longest request. A speculative verify keeps no host copy of the lengths, so the choice reads them from the device (seq_lens.max().item()) and the host waits there for the draft to finish before it can prepare and launch the verify graph. A monokernel forward runs none of SGLang's attention, so both variants of a routed batch size run the same launches: when the spec declares StepMetadata.ATTENTION_GRAPH_VARIANT unread, those sizes are captured once, without variants, and their replay skips the choice.
…e DSA replay metadata they never read A draft spec declares its (forward mode, tokens per request) steps in MonoSpec.step_modes: SGLang routes EAGLE's draft decode and draft extend steps to it by graph key, checks each mode's attention backend and each draft graph runner's captured graphs (the EAGLE draft graph runners now run the capture tail hooks), and passes the served keys in BuildContext.served. A draft spec with no step keeps the normal forward. DeviceLimits.max_rows caps the rows a step is routed with per TP size, so a spec whose kernels cannot serve a width at some TP size leaves those steps to the model's forward instead of falling back inside its own: every served key is a routed key, which is what the unread-metadata contract relies on. Two StepMetadata members let the DSA backend skip its per-replay work on a routed step: DSA_REPLAY_PLAN (lengths, indexer schedule, kpool write plan) and DSA_REPLAY_PAGE_TABLE. With both unread the single-step replay (decode, verify, draft extend) and the multi-step draft decode replay keep the captured metadata; with only the plan unread a decode replay rebuilds just the page table the attention reads.
heslami
requested review from
BBuf,
DarkSharpness,
Fridge003,
HaiShaw,
HydraQYH,
Qiaolin-Yu,
Ying1123,
celve,
hebiao064,
hnyls2002,
ispobock and
merrymercy
as code owners
October 28, 2026 16:28
4 of 5 tasks
…s are the same On HIP a greedy EAGLE verify broadcasts its results from rank 0, because ranks whose draft tokens differ would accept different counts and deadlock the next collective. A monokernel forward that gathers its logits over the TP group (each vocabulary shard copied from the rank that computed it) returns them bitwise equal on every rank, and the top-1 and the greedy verify reduce them deterministically. A spec declares it with MonoSpec.gathered_logits; when the target's verify step and the draft's draft extend and draft decode steps are all served by such forwards and no rejection sampling is on, the greedy verify skips the broadcast.
4 of 5 tasks
… modes the spec does not serve
The EAGLE draft decode and draft extend runners now run the model runner's capture
hooks, so the monokernel capture check sees their graphs. A draft spec that wraps
the model's forward instead of declaring step modes serves the server's verify key,
and none of its draft runners' graphs are of that mode: the check refused them
("none of the N graphs EAGLEDraftCudaGraphRunner captured ... runs ..."), so such a
spec could not start with CUDA graphs on.
The check now skips a runner none of whose captured graphs is of a forward mode the
spec serves. A runner of a served mode still has to run the monokernels.
This branch has not been deployed
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
An AMD monokernel spec today serves the target's decode or verify step. An MTP draft runs other steps: EAGLE's draft decode (1 token per request) and draft extend (N tokens). So a draft spec has to wrap the model's forward itself, and SGLang neither checks its graphs nor knows which of its steps are routed.
On routed steps the DSA backend also rebuilds metadata on every replay that a monokernel forward never reads: lengths, page tables, the indexer schedule and the kpool write plan. This happens on the verify step, the draft extend and every draft decode step.
The unread-metadata contract (#44601) needs every served key to be routed. Some kernels can't serve every width at every TP size; GLM-5.3-Flash's 8-row sparse attention doesn't fit LDS at TP2, for example. Those specs fall back to the model's forward inside their own, and the backend would then skip metadata that the fallback reads.
Modifications
kernel_plugins/amd/spec.py:MonoSpec.step_modes(model): the (forward mode, tokens per request) steps a draft spec serves, e.g.[("decode", 1), ("draft_extend", 4)]. A draft spec returning none keeps the normal forward.ShapeKey.forward_modegains"draft_extend".DeviceLimits.max_rows: TP size → the most rows a step is routed with, when belowwidths. Wider steps run the model's forward. Every served key is then a routed key.BuildContext.served: the keys routed to the built forward.StepMetadata.DSA_REPLAY_PLAN: lengths, indexer schedule, kpool write plan.StepMetadata.DSA_REPLAY_PAGE_TABLE: the page table, skipped only together with the plan.kernel_plugins/amd/binding.py:max_rows;shape_keykeys DRAFT_EXTEND_V2 steps, and a draft decode replay that plans its metadata beforeinput_idsis set;_CaptureCheckchecks each graph runner's captures on their own, since a draft model has a draft decode and a draft extend runner.eagle_draft_cuda_graph_runner.py,eagle_draft_extend_cuda_graph_runner.py: run the model runner's capture tail hooks after each captured graph, asDecodeCudaGraphRunnerdoes. On a draft model runner those hooks are the AMD and NVIDIA plugin capture checks; the DSpark and DFlash draft-sampler hooks sit on their own workers' runners, not these.dsa_backend.py, on a routed replay:test_mono_framework.py:max_rowsat TP4, and not at TP2, leaves 6-row steps to the model's forward, and their metadata is built;req_to_tokenat each page's first token, divided by the page size, at page sizes 4 and 1); negative control: the requests in another order. It builds everything for an unread page table alone, an unserved width, or a page-table-only verify.test_mono_contract.pylists the draft runners'capture_bs.The draft spec this serves is a GLM-5.3 MTP draft spec for sgl-amd-mono, outside this repo and not yet upstream there. It declares both DSA members, as does the target spec, and builds the page tables it reads from
req_to_tokenin its own step-table launch.Stacked on #47101 (merge that first; this PR's own change is its last commit): both edit
shape_key, and the GLM-5.3 plugin needs both.Accuracy Tests
The page tables the plugin builds match SGLang's
req_to_token[req, 64j] // 64word for word. That is the plugin's own kernel check, outside this repo.Accept length at real acceptance (SGLang's default sampling), TP8, 1 request, 8K, two servers per arm in ABBA order, 30 requests each, paired by prompt:
test/registered/unit/kernel_plugins/amd(CPU): 24 passed (19 on its base). The neighbouring runner and DSA tests (test_decode_cuda_graph_runner.py,test_eagle_draft_cuda_graph_runner.py,test_dsa_backend_dp_padding.py,test_graph_routing_context.py): 44 passed, as on main.Speed Tests and Profiling
8× MI355X, TP8, GLM-5.3 FP8 with
--enable-amd-monokernel, EAGLE 3-1-4, FP8 KV, 8K. Acceptance is simulated at 2.99. One server per arm, 6 waves, paired by prompt:One round, so there is no same-arm A/A pair on this run; at C=1 the change is within what one round resolves. The change is the median per-request log-ratio, requests paired by prompt; at 2 requests only each wave's last-prefilled request is used, since the first one waits out the other's prefill. The trace shows where it comes from.
Time outside the launches per cycle: rank 0, 1 request, CPU+GPU trace (median of 37 cycles, profiled), wall / GPU-busy µs:
Setup:
lmsysorg/sglang-rocm:v0.5.22-rocm720-mi35x-20261025with this branch'spython/sglangmounted over the image's;zai-org/GLM-5.3@ba32f8c1;fa6710ewith the GLM-5.3 target and MTP draft specs that are not yet upstream in sgl-amd-mono;9f706f0b5c([Perf] [AMD] monokernels: Capture a routed step's DSA graph once, without the host read #47101), head6fc08c61f2.Checklist
CI States
Latest PR Test (Base): ❌ Missing
run-cilabel -- add it to run CI tests.Latest PR Test (Extra): ❌ Blocked --
run-ciis required first.Latest PR Test (AMD ROCm 10): ➖ No AMD PR run found for this commit.
Added: no verify broadcast when every rank's logits are the same
On HIP, a greedy EAGLE verify broadcasts its results from rank 0. Without it, ranks whose draft tokens differ would accept different counts and deadlock the next collective. A monokernel forward that all-gathers its logits over the TP group returns them bitwise equal on every rank, so every rank's draft and verify reach the same result on their own.
The commit makes two changes:
MonoSpec.gathered_logits.binding.gathers_logits(model, key)reports it for the routed keys only.topk=1, the verify, draft-extend and draft-decode steps of the batch size may all be served by declaring specs. When they are and rejection sampling is off,eagle_sampleskips the broadcast.Every other case keeps the broadcast: rejection sampling on (SGLang's HIP default for EAGLE), sampled requests, an undeclared or unrouted step, and CUDA.
The CPU test
test_mono_framework.pychecks that only routed steps of a declaring spec gather their logits; a width that isn't built, an undeclared spec, and a model without monokernels don't.test/registered/unit/kernel_plugins/amd: 25 passed (24 before).Development measurements on our GLM-5.3 monokernel stack, at TP4, 1 request, with rejection sampling off:
The sgl-amd-mono side is one line per spec,
gathered_logits=Trueon the GLM-5.3 target and draft specs, and it follows this PR.Added: the capture check skips draft runners whose modes the spec doesn't serve
With this PR, the EAGLE draft decode and draft extend runners run the model runner's capture hooks, so the monokernel capture check now sees their graphs.
That would break draft specs that don't declare step modes. A draft spec that wraps the model's forward serves the server's verify key, and none of its draft runners' graphs are of that mode. The check would refuse them ("none of the N graphs EAGLEDraftCudaGraphRunner captured … runs …"), so such a spec could not start with CUDA graphs on. This includes the GLM-5.3 NextN spec in sgl-amd-mono today.
_CaptureChecknow skips a runner none of whose captured graphs is of a forward mode the spec serves:The CPU test
test_mono_framework.pychecks that the draft runners of a spec without step modes pass, and that a verify runner whose graphs aren't routed still raises. It fails without the fix.test/registered/unit/kernel_plugins/amd: 26 passed.