Repository navigation
[Perf] [MI355X] glm53: FP8 mid hand-off, early split rows, per-row up/gate jobs, NextN entry in four parts, TP4 at 8 rows (preview) - #21
Closed
heslami wants to merge 5 commits into
Conversation
added 5 commits
October 28, 2026 15:41
Every down task polled all of mid as tagged f32 pairs from the scratch and quantized it itself: 256 CTAs reading the same 74 KB (4 rows) or 147 KB (8 rows). Now wave k of down task t quantizes block t + k x 256 (one wave at TP8; two at TP4 and 8 rows, where mid has 288 blocks) and publishes its FP8 bytes and scale (midq, midqs); every down task polls those, a quarter of mid's bytes. The quantization is the same, so every value is unchanged. MXFP4 experts keep their own path.
…and uk In a launch without the indexer the selection is the previous launch's, so a split tile's keys and their KV latent and k_pe cache rows are known at the launch start. Above 4 rows most split tiles share a CTA with a q_norm, q_b or uk task; their rows now go out after the CTA's q_norm and cache tasks, ahead of its q_b or uk task, and the tile reads them where it read the rows it loaded itself. The indexer launches, TP4 (more split tiles than CTAs) and 2 and 4 rows keep the old order. Every value is unchanged.
…at row's first picks Every CTA routed all four rows (a wave a row, 8 top-8 rounds each) before it issued its first up/gate job. With one up/gate task round (TP8), the CTA with tiles c of slot group g now runs row g % 4 at slots 1 + g / 4, 3 + g / 4, 5 + g / 4 and 7 + g / 4: the same (row, slot, c) tiles as before, on other CTAs, with each tile's chunk (and so its XCD) unchanged. Routing wave w takes row (row + w) % 4, so wave 0 routes this CTA's own row; the routing waves run two rounds and keep their state in LDS, the CTA issues the first job's weights, and the other six rounds, the route weights and the key stores run under them. These rounds take the wave maximum of the lane heads into every lane (DPP and cross-row swaps), and the lane holding it records its expert ID; a round whose maximum is in more than one lane hands the row to route_top8, which breaks the tie by ID as before. The picks, the route weights, mid and down are unchanged. The router CTAs issue their shared tile's weights where they run it. Other widths, TP4, dense layers and the NextN indexer launch keep their code.
… tiles Each of a NextN launch's 48 entry tiles (TP8) runs as four K parts of [enorm(e) | hnorm(h)] on 192 CTAs instead of two halves on 96: a part reads 98 KB of eh_proj, stages its half's normalized rows and sends its f32 partial to the first part, which adds them in part order and pushes the bf16 tile. With the indexer the entry starts past the front's index projection tiles (wrapping around the grid), so their weights go out at the launch start. TP4 keeps the two halves (192 tasks already). Registers: the entry stages 4 rows at a time, and at up to 4 rows a NextN layer polls the pushed rows two at a time in its front tiles. The other kinds' code is unchanged.
On MI355X, MTP at two requests at TP4 served as one 8-row launch is 14% a cycle faster than SGLang's forward, so TP4 now routes up to 8 rows as TP8 does: the target's decode and verify steps and the draft extend of two requests. The row cap per TP size goes; no code object changes. The kernel tests run TP4 at 8 rows too.
Collaborator
Author
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
Stacked on sgl-project#75 (and sgl-project#68, sgl-project#69, sgl-project#73, sgl-project#74 under it). Merge those first; this PR's diff is its last five commits.
A GLM-5.3 decode step runs 75 MoE layer launches, and with MTP each cycle also runs the NextN draft launches. This PR shortens three hand-offs inside the layer launch and the NextN entry, and lets TP4 run 8-row steps on the kernels:
Every output and every write stays bitwise equal to sgl-project#75, at TP8 and TP4, every width, on the bf16 and the E4M3 MLA cache (Accuracy Tests). The one exception is the layout of the NextN entry's partial-sum mailbox.
Result on MI355X, sgl-project#75 → this PR (TP8, FP8 E4M3 KV, MTP 3-1-4 at the golden accept length 2.99, 8K):
Modifications
Five commits, one per change. Each has its own check against its parent (Accuracy Tests) and its own kernel-only estimate (Speed Tests).
midq,midqs). Every down task polls those instead of all of mid. At TP4 and 8 rows (288 blocks) two waves a task quantize. MXFP4 experts keep their pathehpmailbox layoutmidqandmidqsgo in the scratch aftermid: 19 KB at 4 rows, 38 KB at 8.ehpholds three parts' partials instead of one.TP_MAX_ROWSand_Built.max_rowsgo. The framework'sDeviceLimits(widths=ROWS)already bounds every routed step at 8 rows.test_glm53_kernels.pyruns TP4 at widths 2, 4 and 8.Accuracy Tests
All on MI355X in the image below. The kernel checks ran on one GPU: one process runs rank 0 of a TP8 (or TP4) launch alone. Its peers' partial sums are zeros tagged with the launch's epoch, and its pushes go to scratch sinks. A NextN launch's tokens are the rank's own, and the peers' columns of its entry are the torch entry's. The harness wraps
check_layer.pyandcheck_nextn.pyunchanged, out of tree. Forcheck_nextn.pyit adds the NextN shared launch at 8 rows and takes the entry's bounds on the rank's own columns. A run of the in-tree tools under torchrun at TP8 and TP4 is TBD.Bitwise, per commit (
check_layer.py --dump,--compare):shared(3 launches),index(3),model(the first 8 launches) anddense(3).NextN launch = plain launch fed its own x, bitwise (
check_nextn.py --only entry):The new entry gives the old one's values:
E2E (
test/e2e/test_glm53_server.py, TP8): TBD.Unit tests on a CPU-only host, in the image without the SGLang overlay: 330 passed, with the same 7 failures and 3 collection errors as [Feat] [MI355X] glm53: The NextN draft steps on their own launches sgl-project/sgl-amd-mono#75. These are the spec-registration and contract tests, which need the overlay's SGLang, plus deepseek_v4 and minimax. The spec and draft tests this PR changes (the row routing, the draft build at TP4 with 8 rows) pass on a CPU host with SGLang 5044779f on the path.
Speed Tests and Profiling
Setup as sgl-project#75: image
lmsysorg/sglang-rocm:v0.5.22-rocm720-mi35x-20261025(squashfssha256:fb07e0ef78fe) with SGLang'spython/sglangoverlaid at5044779f, AITER 285579b6, FlyDSL 0.4.0,zai-org/GLM-5.3atba32f8c1, the e2e test's server flags with--kv-cache-dtype fp8_e4m3.TP8, FP8 E4M3 KV, MTP 3-1-4 at AL 2.99, cycle in ms:
TP4, FP8 E4M3 KV, MTP at AL 2.99, two requests: TBD → TBD ms.
Kernel-only (
time_layers.py), each commit against its parent: TBD.Registers
The compile survey (CPU only) covers every kind (index, shared, dense, embed, head, nextn_index, nextn_shared) at TP4 and TP8, widths 2 / 4 / 8, both caches,
stopon and off: 148 keys a tree.stop=True: 16–20 → 0.stop=Falsebuild: 1 → 3 VGPR spills (8 → 16 B).The kernel-argument lint (a scan for kernel arguments that are loaded but never read, or whose address halves mix two arguments) finds 0 hits in every object of every commit: 148 / 148 clean on sgl-project#75 and on commits 1 to 4.
Reviewer notes
kernel.pygrows by about 320 lines, a candidate for the stage-builder split.Checklist
pre-commit run --all-filespasses. (ruff, ruff-format and isort clean on the touched files; full run TBD)test/cover the change..github/CODEOWNERSthat names its owner. (No new model.)