amd-agi/geak/tuning-in-sglang
Claude Code and agent skill by amd-agi/geak
- community
Checking that the SKILL.md file is still in the repository.
Tune ops inside an SGLang serving deployment on AMD Instinct — capture real shapes, tune the Triton fused-MoE path into the correct triton_<version> config directory, deploy via SGLANG_MOE_CONFIG_DIR, and read the server log to distinguish an exact config hit from a silently degraded version fallback. Use when tuning for a live SGLang workload.
Install
npx vskill@latest install amd-agi/geak/tuning-in-sglangSKILL.md
Tuning inside SGLang
Read ../tuning-core/SKILL.md first, and set the environment up per ../env-setup/.
SGLang's tuning surface is close to vLLM's — capture real shapes, tune, deploy a config, prove engagement — with one structural addition that changes both where artifacts go and what "engaged" means: configs are keyed by Triton version as well as by device.
Before measuring anything on the decode path, read `../tuning-core/graph_captured_benchmarking.md`. SGLang runs decode under captured HIP graphs by default, which has two consequences that invalidate the obvious harness: a kernel or config change takes effect only at capture time, i.e. only across a server restart, and a kernel timed alone in eager mode carries launch overhead it never pays inside a ~790-kernel graph. Because every candidate needs a restart, your noise floor is the restart-to-restart spread — measured at 0.36% here against 0.014% within a single process, a 26× difference (../tuning-core/measurement.md Rule 3b). A harness that misses this reports wins that are really drift.
Measured on gfx942 / MI300X in primussafe/sglang:v0.5.12-rocm720-mi30x-profilerfix and on gfx950 / MI355X in primussafe/sglang:v0.5.12-rocm720-mi35x-profilerfix — SGLang 0.5.12, Triton 3.6.0, ROCm 7.2.0 in both. tuning_workspace/verify_sglang_950.py reproduces every claim below against a live image.
1. Configs are keyed by Triton version, and the shipped ones do not match
The MoE config path has an extra directory level:
Because — per the comment in SGLang's own lookup code — a config tuned under one Triton version can produce negative gains under another. The version is part of the key for the same reason the architecture is.
What actually ships, against an installed Triton of 3.6.0 in both images:
| config dir | mi30x files | MI300X files | mi35x files | MI355X files | |
|---|---|---|---|---|---|
triton_3_1_0 | 127 | 7 | 127 | 0 | |
triton_3_2_0 | 35 | 0 | 35 | 0 | |
triton_3_3_0 / 3_3_1 | 1 / 21 | 0 | 1 / 21 | 0 | |
triton_3_4_0 | 33 | 0 | 33 | 0 | |
triton_3_5_1 | 103 | 0 | 103 | 0 | |
triton_3_6_0 | 8 | 0 | 8 | 0 | |
| total | 322 | 7 | 328 | 0 |
Two separate problems, one per column.
On MI300X, every matching config is in `triton_3_1_0` — five minor versions behind the installed Triton. They still get used, via a fallback that walks version directories newest-first, but by the codebase's own reasoning that is the situation the versioned layout exists to flag. Re-tuning into triton_3_6_0 is concrete headroom, not speculative.
On MI355X there is nothing to fall back to. All 328 files are present, none of them names this device — the only AMD names anywhere under configs/ are AMD_Instinct_MI325X (15) and AMD_Instinct_MI300X (7). So every fused-MoE lookup on MI355 in this image reaches the third case below and runs the default config. Verified directly:
That makes MoE tuning the single highest-value item for sglang on MI355, and it is starting from zero rather than from a stale baseline. Note also the generated filename contains a literal space in block_shape=[128, 128] — only device_name gets its spaces stripped. Generate the name with get_config_file_name() rather than by hand.
2. Read the log — a hit and a fallback are different outcomes
SGLang distinguishes three cases, and the distinction is the whole point of §1. All three were reproduced on gfx950 by planting files in a scratch directory and reading the emitted log:
Exact match (logger.info):
Version fallback (logger.warning):
No config at all — defaults, and you are leaving the most on the table. This is the current state of every MoE layer on MI355 in this image (§1).
A fourth case exists in this version and is not in the docs: with down_moe=True and no …_down.json present, it logs Using MoE kernel config with down_moe=False, which is a partial hit — the up-projection config gets reused for the down-projection. Treat it as a miss on the down side; tuning_fused_moe_triton_sep.py (§5) is what produces the missing half.
Treat the fallback warning as a miss, not a hit. It is the failure mode that looks most like success: a config was loaded, the server runs, performance is plausible. A naive check for "did it find a config" passes. Only the version in the path tells you whether the config was tuned for the compiler you are running.
3. The device name
Without GPUs passed in (--device=/dev/kfd --device=/dev/dri --group-add video) this returns None and the filename becomes device_name=None.json. If you see None or an empty string, fix the container before doing anything else (../env-setup/ §2).
The two frameworks report different names for the same silicon. Measured on one physical MI355 node, same GPU, two containers:
| source | reports | |
|---|---|---|
sglang get_device_name() (mi35x image) | AMD Instinct MI355X | |
torch get_device_name() (mi35x image) | AMD Instinct MI355X | |
vLLM current_platform.get_device_name() (vllm image) | AMD Instinct MI355 OAM | |
torch get_device_name() (vllm image) | '' — empty string |
Consequences worth being explicit about:
- A tuned MoE config does not transfer between the two frameworks, even on the same node with
the same Triton version, because the device name is in the filename. Tune twice, or rename.
- `torch.cuda.get_device_name()` works here and is empty in the vllm image. So a helper
script that builds filenames from torch is correct in sglang and silently wrong in vLLM — which is exactly how that bug survives. Always use the framework's own helper.
- The vLLM image ships two
MI355_OAMconfigs and oneMI355Xconfig; theMI355Xone is
unreachable from vLLM and would be the reachable name from sglang, where it does not exist. The same silicon has configs stranded on both sides of the name split.
Then check your filename against one that ships:
4. Capture real shapes
Replay captured commands verbatim — M and N appear swapped relative to the torch call (../tuning-hipblaslt/ §1). For the aiter path, AITER_LOG_TUNED_CONFIG=1 inventories which GEMM shapes have no tuned config.
Tune in call-count order. ../benchmark/shapes.py is the fallback when you cannot run the real model.
5. Tune
Fused MoE — SGLang ships its tuner:
(tuning_fused_moe_triton_sep.py alongside it tunes the up/down projections separately, and is what produces the _down.json whose absence causes the partial hit in §2.) Output is the JSON-per-M-bucket format; write it into the triton_<installed version> directory so it is an exact hit rather than a fallback. On MI355 this is the highest-value work in this skill, because §1 shows there is nothing shipped to fall back to.
Dense GEMM goes through aiter, and the sglang image is the easy case: aiter is a git checkout at /sgl-workspace/aiter, so gradlib/gradlib/gemm_tuner.py and the seven per-op CK tuners are already present — no source to fetch (../env-setup/image_tool_matrix.md). Run and deploy per ../tuning-aiter/, including the /tmp/aiter_configs trap.
All seven CK tuners were exercised on gfx950 in this image; results, the two real wins, and three traps — a tuner that installs regressions, a shipped shape list that aborts on this part, and an environment-variable name that silently does nothing — are in ../tuning-ck/ §3 and §3b. Read that before running one, not after.
The authoring-time knobs behind those MoE configs — num_warps, waves_per_eu, block sizes — are covered in ../tuning-triton/. Worth knowing which of them actually move the number before spending tuning time on a wide space.
6. Deploy without editing the package
Note the layout: SGLang appends configs/triton_<version>/ beneath the directory you name. Two things about getting that wrong, both measured on gfx950:
Pointing the variable at the version directory raises, it does not miss. With SGLANG_MOE_CONFIG_DIR set to the triton_3_6_0 directory itself, the exact-path check fails, and the fallback scan then calls os.listdir(<dir>/configs) unguarded:
That is an unhandled exception at the first MoE layer construction, so it surfaces as a server that fails to start rather than as a quiet performance loss. Of the two failure modes this is the good one — but do not expect a warning.
The override replaces the package directory, it does not extend it. config_dir is used as the root for both the exact lookup and the newest-first fallback scan, so with the variable set, none of the 328 shipped files are on any search path. On MI355 that costs nothing, since none of them match this device anyway. On MI300X it means a directory holding one tuned config turns the other six shipped MI300X configs into misses. If you deploy by env var there, copy the shipped tree in first:
This is the opposite of vLLM's behaviour, which checks the override then the package directory and names both in its miss warning (../tuning-in-vllm/ §4). Same intent, two different semantics; do not carry an assumption across.
Using the env var still keeps tuned artifacts separate from what shipped, survives image updates, and makes A/B a matter of unsetting one variable.
6b. One switch that discards all of it
get_moe_configs returns before it looks at any file. Verified on gfx950: with a valid exact-hit config in place, enable_deterministic_inference=True loads nothing and logs only the line above. If a reproducibility flag gets turned on later in the deployment's life, the MoE tuning silently stops applying — and the warning does not mention configs going unused in a way that reads as a problem. Check that flag before investigating a regression in tuned MoE performance.
This is the start of the file. Read the full SKILL.md on GitHub.
Security
- Security scanPassed, 95 of 100Pattern checks on the skill's files.
- Model reviewPassed
Quality evaluation
- Task evaluationNot runNo evaluation yet. Run one in Skill Studio.
Source
- SKILL.md in the repositoryCheckingperf_knowledge/expert_skills/tuning/tuning-in-sglang/SKILL.md
Popularity
- Repository stars
- 182
- Repository forks
- 56
- 7-day trend score
- 26
Stars and forks are for the whole GitHub repository, not this skill alone. Not refreshed yet.
Works with
Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.
- Claude Code
- Cursor
- GitHub Copilot
- Windsurf
- Codex
- Gemini CLI
- Cline
- Amp
- Roo Code
- Goose
- Continue
- Kilo Code
- Junie
- Trae
- OpenHands
- Qwen Code
- AI Agent
- Tabnine Chat
- Sourcegraph Cody
- Amazon Q
- Firebase Genkit
- Sweep
- Devin
- Phind
- Mentat
- Claude Desktop
- Aider
- GPT Pilot
- Plandex
- bolt.new
- v0
- Replit Agent
- Anse
- TypingMind
- BetterChatGPT
- LibreChat
- Warp AI
- Pieces
- Fig AI
Embed badge
[](https://verified-skill.com/skills/amd-agi/geak/tuning-in-sglang)