Skip to content

[Feat] [MI355X] kimi_k3/kimi_k31: Kimi-K3's gfx950 launches, and Kimi-K3.1 steps of up to 8 rows on them - #103

Merged
kevin-mii merged 1 commit into
mainfrom
k31/fastest
Nov 4, 2026
Merged

kevin-mii merged 1 commit into
mainfrom
k31/fastest

Conversation

@kevin-mii

Copy link
Copy Markdown
Collaborator

Motivation

Kimi-K3.1 on MI35x is fastest when each step size runs the launch built for it. #102 covers verify steps of 2 .. 48 requests with the wide layer launch, and leaves a step of one request (6 rows with DFLASH at 6 draft tokens) on SGLang's path. This PR serves those steps on Kimi-K3's per-step launches, which K3.1 shares (the shapes are the same; K3.1 adds the DSA layers, which keep SGLang's attention up to o_proj).

It carries Kimi-K3's gfx950 launches from #13 (@billishyahao), with the work on top of them that the K3.1 bring-up measured, and supersedes #13.

Modifications

  • kimi_k3: Kimi-K3's launches at TP8, for decode and verify steps of up to 8 rows (from [Feat] [AMD] kimi_k3: Kimi-K3 layer monokernel on gfx950 #13, plus):
    • K1 on the KDA layers: in_proj, the conv window, the recurrence and the gated norm. It reads the committed state in place, or the ReplaySSM ring under --enable-linear-replayssm-spec; the conv window is staged and handed back in one Triton launch each.
    • mla_pre, one launch a MLA (DSA) layer: the AttnRes seam, fused_qkv_a, the gates, the q / kv norms, q_b, the indexer projections and the q absorb (q_nope @ w_kc). SGLang takes its outputs through _mono_pre / q_nope_out.
    • The layer launch (o_proj to the MoE output): the seam, the router, top-16 by candidate rank with the route table from a bitmask, the latent MoE (latent quantized once by its producers), the shared expert, and the residual stream written in place.
    • Sentinel all-reduces on o_proj, the latent and the final reduction; pre-AR loads; tagged x rows (SGLANG_AMD_MONO_K3_K2_OPT).
  • kimi_k31/spec.py: steps of up to 8 rows run the kimi_k3 launches; DSpark and DFLASH verify steps above 8 rows keep [Feat] [MI355X] kimi_k31: Wide-row layer launch for Kimi-K3.1 verify steps #102's wide launch. SGLANG_AMD_MONO_K31_WIDE=0 serves the narrow steps only, without ReplaySSM. The wide launch's requirements (ReplaySSM, 6 or 8 draft tokens, a speculative algorithm) are config refusals that name the switch.
  • muse_spark/draft.py: the DFlash2 draft spec names its hidden size in requires.hf_config. Kimi-K3.1's DFLASH draft is also DFlash2DraftModel with model type qwen3, so under --enable-amd-monokernel it picked up the Muse Spark spec and the load failed ('DFlashMLAAttention' object has no attribute 'qkv_proj'). SGLang keeps a draft's forward when the spec names another model ([Feat] [AMD] model: Add Kimi-K3.1 MI35x support sglang#48427).
  • common: the sentinel-word helpers (sync.py), wave and CTA prefix scans (ops.py).
  • Tests: test/kernels/kimi_k3 (K1, K2 and mla_pre against torch references, check_k2, time_k2), test/unit/test_kimi_k3_spec.py, test_kimi_k31_spec.py (narrow / wide routing), and the forward drift pins re-read against the current SGLang.
  • .github/CODEOWNERS, README.

Tested against SGLang sgl-project/sglang#48427 (kimi-k3.1-day-0-amd, which carries the _mono_pre / q_nope_out hooks) plus its binding fix.

Accuracy Tests

Kimi-K3.1-Pro, DFLASH (K31-DFlash2-MLA5-...-step55000, 6 draft tokens), TP8 on 8x MI350X, --enable-linear-replayssm-spec, on sgl-project/sglang#48427 plus its binding fix:

Check Monokernel off This PR
GSM8K 200, 16 threads (narrow and wide steps) 99.0% 99.0%
GPU faults 0 0
Plugin unit tests same 11 failures as main (other models' contracts against this SGLang); every kimi_k3 / kimi_k31 test passes

Speed Tests and Profiling

Step time of one request (cc1, 10K-token prompts, 1024 out, real acceptance, median over 2-3 rounds), same SGLang tree and session:

Build cc1 step
Monokernel off 19.06 ms
This PR (1ce5a34, then the final commit) 13.54 / 13.47 ms
The same launches before the merge onto main 13.43 ms

The gap to the pre-merge build is within run-to-run spread (13.42 .. 13.51 ms across earlier runs of that build). Verify steps of 2 .. 48 requests run #102's wide launch unchanged and were not re-measured here.

Not covered: DCP8 on the narrow path (the MLA front is declined under DCP; K1 and the layer launch were measured at DCP1 only).

Checklist

  • pre-commit run --all-files passes.
  • Tests under test/ cover the change.
  • The SGLang commit or PR this was tested against is named above.
  • Accuracy and speed results are attached.
  • A new model adds one line to .github/CODEOWNERS that names its owner.

🤖 Generated with Claude Code

…i-K3.1 steps of up to 8 rows on them

Kimi-K3 (kimi_k3, from #13): per-step monokernels at TP8 for decode and DSpark
verify steps of up to 8 rows. K1 on the KDA layers (in_proj, the conv window, the
recurrence and the gated norm, reading the committed state in place or the
ReplaySSM ring), the MLA projection front on the MLA layers (mla_pre: the AttnRes
seam, fused_qkv_a, the gates, q / kv norms, q_b and the q absorb), and the layer
launch (o_proj to the MoE output: the seam, the router, top-16 by candidate rank,
the latent MoE and the shared expert) on every MoE layer, with sentinel all-reduces
on its o_proj, latent and final reductions.

Kimi-K3.1 (kimi_k31/spec.py): steps of up to 8 rows (a decode step, or a verify
step of one request) run the Kimi-K3 launches; DSpark and DFLASH verify steps of
2 .. 48 requests keep the wide layer launch from #102. SGLANG_AMD_MONO_K31_WIDE=0
serves the narrow steps only (no ReplaySSM needed). Under DCP the MLA layers keep
SGLang's projection front.

muse_spark: its DFlash2 draft spec names its hidden size, so SGLang can tell a
Kimi-K3.1 DFlash2 draft (same architecture and model type) apart and keep that
draft's forward.

Co-authored-by: billishyahao <bill.he@amd.com>
Co-authored-by: wunhuang <wunhuang@amd.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kevin-mii kevin-mii changed the title [Feat] [MI355X] kimi_k3, kimi_k31: Kimi-K3's gfx950 launches, and Kimi-K3.1 steps of up to 8 rows on them [Feat] [MI355X] kimi_k3/kimi_k31: Kimi-K3's gfx950 launches, and Kimi-K3.1 steps of up to 8 rows on them Nov 4, 2026
@kevin-mii

Copy link
Copy Markdown
Collaborator Author

Review pass before merge (an independent read of the diff, then fixes in this commit):

Fixed here:

  • DCP8 on narrow steps: under DCP, SGLang's ROCm MLA forward replicates q_b and computes every head's q itself (q_replicate_active), while mla_pre hands over this rank's 12. Under DCP the MLA front now declines, and SGLang's projection front runs.
  • The wide launch's merged-in_proj check now covers every KDA layer it runs, not only the first.
  • The K3.1 drift test no longer duplicates the two outer-forward pins, which now live in the Kimi-K3 test.
  • Forward drift digests: re-pinned after reading the diff against SGLang. The new branches (MegaMoE, prefill-CP shards, the CP causal-LM forward, the attention's tuple result) are not taken by a routed step: the spec refuses an a2a backend and attention or prefill CP, and forward_pre_o_proj returns a tensor.
  • Comments: commit hashes, timing notes and references to files that are not in the repo are dropped.
  • time_k2.py defaults to the serving form (fold, route 2, rtab 2, K2_OPT).
  • README: a Kimi-K3.1 row, its recipe and its layout.

Left as they are, for follow-ups:

  • wide.wide_widths ([Feat] [MI355X] kimi_k31: Wide-row layer launch for Kimi-K3.1 verify steps #102) declares 312 .. 384 rows, which have no L=6 bucket. These are only reachable above 48 requests or with CUDA graphs off.
  • Kimi-K3's _backbone still collects the DFLASH taps in AuxHiddenStateList, not the packed buffer (same logits).
  • The K3 A/B switches (SGLANG_AMD_MONO_K3_*) stay, as other models' switches do.
  • Kimi-K3.1 now needs Kimi-K3's environment too (SGLANG_AITER_K3_OPT=1, AITER_SITUV2_A8W4=1), as the README recipe shows.

@kevin-mii
kevin-mii merged commit ce1290e into main Nov 4, 2026
2 of 3 checks passed
@kevin-mii
kevin-mii deleted the k31/fastest branch November 4, 2026 18:41
kevin-mii added a commit to sgl-project/sglang that referenced this pull request Nov 4, 2026
sgl-project/sgl-amd-mono#103 (ce1290efc4) serves Kimi-K3.1 steps of up to 8 rows
on Kimi-K3's gfx950 launches and keeps #102's wide launch above them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
yuychang added a commit to yuychang/sgl-amd-mono that referenced this pull request Nov 10, 2026
- Upstream sgl-project#103 squashed this branch's first commits; conflicts resolved against 88aee67 as the effective base
- kda/k1: take upstream's ReplaySSM ring and init_committed; re-apply the FP8 in-proj (wfmt) and bf16 state on top
- k1: find a KDA layer's state via upstream's state_layer().view_layer_id (sgl-project#113) instead of mamba_map
- k1: keep the request-scoped ring decline and check state dtypes for both the scratch and committed pools
- spec: drop _k1_bank_rows, upstream's warmup bank already holds the block-write row (sgl-project#108)
- layer/oproj: keep the FP8 o_proj (ofmt, w_os) next to upstream's fold, OPT bits and sentinel all-reduce
- check_k2: FP8 o_proj modes alongside upstream's fold / OPT checks; test_kimi_k3_spec follows _state_config
- GPU: test_kimi_k3_k1 17 passed, test_kimi_k3_kernels (TP8) 3 passed, test_kimi_k3_mla_pre 6 passed
bingxche added a commit that referenced this pull request Nov 10, 2026
…es, per PR and nightly (#107)

## What

CI on two dedicated MI355X nodes that runs the checks of the PR review
guideline, split in two:

- **PR test** (`pr-test-amd.yml`), maintainer-gated, for each model a PR
touches: its unit, kernel and serving tests, the kernel-only timing
scripts, GSM8K with the monokernels on and off, and InferenceX
fixed-length speed against the base branch. The workflow posts the
result as an `AMD PR test` commit status and keeps one PR comment
updated with a checklist in the guideline's order (rules, scope,
accuracy, kernel microbenchmark, E2E performance, tests, doc).
- **Nightly** (`nightly-amd.yml`), on main and the newest SGLang ROCm
image: for each Phase 1 model, GSM8K, GPQA (and MMMU-Pro for vision
models) and the speed test with the monokernels on, held to the model's
fixed baseline. Saturday's **weekly** run adds AIME26 ×4 and runs every
benchmark with the monokernels on and off.

## Why

The guideline asks reviewers to approve only when these checks pass,
plus a ≥5% gain for performance work. Today authors run the checks by
hand and paste the numbers. The long benchmarks would make every PR run
take most of a day, so GPQA runs nightly and AIME26 ×4 (~23K tokens per
answer, the costliest) runs weekly. A PR run keeps only the checks that
can block a merge.

## How it works

- Trigger: a maintainer adds the `run-ci` label, or runs the workflow
with a PR number. `run-ci-extra` does the same and adds GPQA, which
catches long-context accuracy losses that GSM8K's short answers can't
show. It's meant for PRs that touch attention, the KV cache or long
sequences. The run removes the label and pins the PR head SHA it saw, so
any later push needs a maintainer again.
- Scope (`scripts/ci/scope.py`): a model whose own package or tests
changed runs the full tier. A change to `common/`, `__init__.py` or
`pyproject.toml` runs every other model's smoke tier (tests, plus speed
at the covered widths only) and `common` (the unit tests and the
guard/dispatch kernel tests). The CI's scripts and `models.py` come from
the base branch, so a model package that `models.py` there doesn't list
(a model new in the PR) isn't run: the report warns ("New model"), and
the author gives its results by hand until its entry merges.
- Sides (`scripts/ci/run_model.py`): every server is the model's
`test/e2e` recipe from the PR. **base** is the PR's merge-base with its
base branch, monokernels on, used for speed only. **on** is the PR with
monokernels on. **off** is the same build with monokernels off, i.e.
SGLang's own path. The off side doesn't depend on the package, so the
weekly run's off result for the model's recipe is kept as the
**monokernel-off reference** (`$CI_SHARED_DIR/monokernel_off_ref`, on
shared storage so either node can read it): the PR test and the nightly
compare against it, a PR runs off itself only when the reference has no
entry yet, and only a weekly run writes it. The container mounts it
directly, writable in a weekly run and read-only in the PR test and the
nightly, so PR code can't change it.
- With monokernels on, an eval sends no more concurrent requests than
the launches cover (for Kimi-K3, one request; for DeepSeek-V4.1, eight).
A wider step falls back to SGLang's path, and the score would not
measure the kernels.
- Gate (`scripts/ci/report.py`):
- GSM8K (all 1319 questions, plus GPQA with `run-ci-extra`) is compared
on vs off question by question with an exact one-sided McNemar test, and
a significant loss with monokernels on (p < 0.05) fails;
- a median-TPOT (p50) loss of more than 2% vs base fails at a covered
concurrency and warns at the model's other concurrencies;
`[Perf]`/`[Feat]` PRs also need a ≥5% gain at a covered concurrency of
one of the PR's models. Output throughput is reported next to it and
warns on a loss over 2%. Other serving metrics swing too much with the
monokernels, so the report leaves them out;
- failed tests fail, and so do a server that logs no monokernel routing
and a step of the run that raises an error;
- edits to existing `.github/CODEOWNERS` lines fail, while an added
owner line for a new model is only a warning.
- The nightly holds each on score to a per-model `baseline` in
`models.py` (GSM8K below it fails, the rest warn). Each weekly report
suggests baselines from its off scores; the baselines start empty and
get set from the first weekly run. The weekly run gates on the paired
GSM8K test like a PR run; GPQA and AIME26 ×4 warn when they fall 5 and 3
points below the off run (Zijie's numbers), and MMMU-Pro 3 points.
- Before installing main, the nightly also checks the sgl-amd-mono the
SGLang image pins (`/sgl-workspace/sgl-amd-mono`): its
`test_contract.py`, and a server per model with the monokernels on
answering a few requests. A field SGLang adds to an op that the pinned
commit doesn't set fails that check the day the image picks it up (Kk's
case).
- Isolation: the PR workflow uses `pull_request_target`, and the scripts
that run outside the container come from the base branch. PR code runs
only inside the SGLang ROCm container. The cluster job fetches both
commits itself.

## Notes for the reviewer

- max-tokens leaves room for thinking models: 16384 for GSM8K, 64K for
GPQA and AIME26.
- Phase 1 nightly models: DeepSeek-V4.1-Flash, GLM-5.3, GLM-5.3-Flash,
Kimi-K3, Qwen3.8-Flash-Next.
- GLM-5.3-Flash and Qwen3.8-Flash-Next: their MXFP4 checkpoints aren't
on shared storage, but they were downloaded straight into both nodes'
mirror, so the nightly serves them (their completeness hasn't been
checked by a run yet).
- MiniMax-M3 is marked blocked until sgl-project/sglang#41998 and #41708
merge, because SGLang refuses `--enable-amd-monokernel` for it without
them.
- Kimi-K3.1, MiniMax-M3.1 and Muse-Spark run their unit and kernel tests
only, until their SGLang support lands.
- Not in CI: OCRBench and LiveCodeBench (sgl-eval doesn't run them yet).
SWE-bench stays with the author.
- GPQA is a gated dataset: the job fills the dataset cache with the HF
token in the runner account's `~/.sgl_amd_mono_ci/hf_token` (set up on
the cluster) before any PR code runs.
- Setup: the `run-ci` and `run-ci-extra` labels exist and the runners
are registered. Optionally, a repo admin can make `AMD PR test` a
required status after merge.
- Nodes: a job takes whichever of the two CI nodes is free;
`slurm_launch.sh` keeps jobs off the shared partition's other nodes with
a versioned `SLURM_EXCLUDE` list. A runner's `.env` must not pin a node:
Spur's `sbatch` honors `SBATCH_NODELIST` and ignores `SLURM_NODELIST`
(tested on the nodes; the stale `SLURM_NODELIST` lines in r1/r2's `.env`
are removed).
- Runners: r1/r2 (`sgl-project/sgl-amd-mono`) on the
`crs-m2m-cpu-spur-arc-016` runner VM, account `z1_sglangci`; each node
keeps its checkouts, checkpoint mirror and caches under
`/mnt/m2m_nobackup/sgl-amd-mono-ci`. r3/r4 are temporary runners on the
fork for the end-to-end trials below; unregister them after merge.
- A run of the nightly workflow with `weekly` ticked rewrites the
monokernel-off reference from whichever branch it runs, so run it by
hand on `main` only.

## Test plan

End-to-end on the fork (michaelzhang-ai/sgl-amd-mono) with this PR's CI
on top of current `main` (fork branch `e2e-base`), on the two MI355X
nodes, with the full settings (all 1319 GSM8K questions, every
concurrency):

- [PR
test](https://github.com/michaelzhang-ai/sgl-amd-mono/actions/runs/42789607786)
with `run-ci-extra` on [fork PR
#2](michaelzhang-ai#2) (a
`deepseek_v41`-only change), CI at d356704: label trigger and removal,
scope, merge-base base side, base speed test and timing script,
unit/kernel/serving tests, the on server (both launches routed:
target_verify steps of batch size 1–8), the off side, the report, the PR
comment and the `AMD PR test` status all ran. GSM8K on vs off: 0.9666 →
0.9666, 5 lost / 5 gained, p = 0.623. Median TPOT base → PR within ±1%
at the covered concurrencies. The status is red for reasons outside this
PR: GPQA could not be loaded (the HF token cannot fetch
`Idavidrein/gpqa`; f1eec28 now reports that as not run instead of an
error), main's known unit failures, and one serving test that imports
`sglang.srt.utils.hf_transformers_utils`, which SGLang moved in
sgl-project/sglang#48781.
- [Weekly
run](https://github.com/michaelzhang-ai/sgl-amd-mono/actions/runs/42789589746)
(`common` + `deepseek_v41`): the pinned-mono check correctly fails (the
11-09 image pins sgl-amd-mono e52803f, which lacks #118, so
DeepSeek-V4.1 cannot start with the monokernels on that image); on and
off with GSM8K and AIME26 ×4 ran. Writing and then reading the
monokernel-off reference (weekly → nightly) is still running.
- Cancelling a run cancels its cluster job
([run](https://github.com/michaelzhang-ai/sgl-amd-mono/actions/runs/42771116642),
on earlier CI code).
- Kernel compilation dominates the kernel tests (DeepSeek-V4.1's took 1
h 38 min, mostly FlyDSL JIT on a cold cache); persisting the compile
caches is a follow-up.

Also:

- Ran `scope.py` against the file lists of #95, #96, #97, #101, #103 and
#105; the models and rule results match what each PR touched (#96 and
#103 trip the CODEOWNERS rule).
- `report.py` produced the expected verdicts on synthetic results
(paired GSM8K: 7 of 7 discordant lost fails at p = 0.008, a 1/1 split
passes), and the review fixes were checked with mocks (step isolation,
PR runs not writing the reference, the PR-wide speed gain). The
reference's direct mount was tested on v3-060 with the SGLang image: a
weekly-mode write and read-back, a read-only mount refusing the write
(EROFS) while reads work, and the files handed to the runner account.
- Every supported checkpoint the cluster has (16 of 17;
`moonshotai/Kimi-K3.1` isn't on HF or the cluster) is mirrored to both
nodes' NVMe.

---------

Co-authored-by: bingxche <Bingxu.Chen@amd.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Zijie Chen <300606707+zijiecode@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant