Skip to content

GLM-5.2-FP8 on gfx950: re-measure on SGLang 0.5.17, fill the two empty cells - #3

Merged
jhinpan merged 4 commits into
jhinpan:mainfrom
williamqwu:feat/glm52-mi355x-mtp-fp8kv
Aug 22, 2026
Merged

jhinpan merged 4 commits into
jhinpan:mainfrom
williamqwu:feat/glm52-mi355x-mtp-fp8kv

Conversation

@williamqwu

@williamqwu williamqwu commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

The gfx950 cell declares two gaps. Both are closed on rocm/sgl-dev:v0.5.17-rocm724-mi35x-20260820 : no fork, no patch, same 8× MI355X shape you published on.

Calibration first. Your published recipe, re-run unchanged on the new image, reproduces your own ISL 8192 / c=16 row to 0.7% (535.66 → 531.74 aggregate tok/s; TPOT 25.22 → 25.01 ms). Everything below is a delta measured on that one harness.

1. The two mandatory bpreshuffle patches are retired

Your gotcha named its own exit condition — "not in aiter 7d604afe5". This image ships d9e5ef7ce, with the CK fix (ROCm/rocm-libraries#8639) present. Keeping the gate forced off now costs 7–9% across six shapes, with GSM8K identical (0.980 both ways). The gotcha
is rewritten rather than deleted: below ROCm 7.2 it is still required.

2. gaps[1] "MTP / speculative decode — not enabled on AMD" — it is

NEXTN works. BS=1, ISL 8192 / OSL 1024: your published 69.3 tok/s → 204.0 with fp8 KV + NEXTN (2.94×).

Isolated with a one-flag control (--speculative-algorithm NEXTN on/off, everything else identical), the prefill cost is 1.03–1.06×, not the 2× a cross-image comparison suggests.
Accept length is workload-dependent, and --dataset-name random overstates it — exactly as your Kimi-K3 §5.5b says. Your own degeneracy_probe.py, run unmodified against GLM-5.2, returns 9 unique tokens out of 512, top 8-gram ×27.

workload accept (of 4) BS=1 decode gain
random, ISL 8192 3.994 200.4 2.58×
GSM8K n=1319 3.5565
ShareGPT — real text 2.969 144.7 1.72×

The published rows use the synthetic figure for comparability with your existing cells; 1.72× is the number to plan against.

3. "KV-cache dtype must be bfloat16 with the DSA tilelang backend" — that is a CUDA rule

_check_tilelang_dsa_fp8_kv raises only when not hip. On ROCm, fp8_e4m3 is legal and roughly doubles the pool: 1,645,440 → 3,194,368 tokens at the same --mem-fraction-static.

4. gaps[0] — both cells filled

cell KV dtype spec KV pool (tok) BS=1 decode peak output tok/s GSM8K n=1319
low-latency bfloat16 NEXTN 1,645,440 200.4 1099 @C64 97.1%
balanced fp8_e4m3 NEXTN 3,194,368 204.0 1328 @C32 97.3%
high-throughput fp8_e4m3 3,717,888 80.9 1276 @C64 97.2%

Three honest new gaps replace the two closed ones — chiefly that fp8 KV is not cleared for long-context accuracy; the ISL ladder measures speed, not correctness.

The gotcha worth the most time

MTP + fp8 KV at --mem-fraction-static 0.88 aborts the server process on a 131k prompt — not the request, the process. Speculative graphs are captured out of the remainder after the pool is sized, which is also where a long chunked prefill draws its working set,
so the correction runs downward. 0.85 clears 262k for 5.5% of pool and no measurable speed cost, and that is what the shipped recipe uses.

One change that is not mine, and is easy to drop

The last commit adjusts 6 published total_tok_s values, which currently hold output-only throughput against your own caption and gen_cookbook_rows.py. The proof is inside the cell: the same c=1 point appears as 47.8 and 430.7, and 47.8 × 9 = 430.2. It is last in the
series and invents no data — drop it and the rest applies cleanly.

Checks

verify-cookbook.js passes at every commit, test_render.js at the tip; all three cells render with all flags documented. gfx942 rows untouched. New rows are generated by gen_glm52_mi355x_rows.py from bench_serving JSON, not transcribed. glm52_fp8_playbook.md keeps its gfx950 sections and gains supersede pointers, in the style of Kimi-K3 §5.4.

Environment: 8× MI355X (gfx950), TP=8 · ROCm 7.2.4 · SGLang 0.5.17.dev20260820+g47fc97d754 · aiter d9e5ef7ce · tilelang 0.1.7.post3

Summary by Sourcery

Re-measure GLM-5.2-FP8 on MI355X with current ROCm and SGLang support, replacing obsolete limitations with three verified deployment recipes and updated benchmark metadata.

New Features:

  • Add a verified GLM-5.2-FP8 MI355X/gfx950 deployment with low-latency, balanced, and high-throughput recipes.
  • Enable documented NEXTN speculative decoding and ROCm-only fp8_e4m3 KV-cache support for the gfx950 recipes.
  • Publish generated benchmark and accuracy data for the new MI355X configurations.

Bug Fixes:

  • Retire the obsolete gfx950 bpreshuffle workarounds on newer SGLang/ROCm images while preserving guidance for older environments.
  • Correct published throughput metadata so total and output token rates are represented consistently.

Enhancements:

  • Document KV-pool capacity sizing, scheduling behavior, graph limits, and long-context operational caveats for the new recipes.
  • Update existing GLM-5.2 documentation and model metadata with superseding pointers, platform-specific support, and remaining validation gaps.
  • Expand CLI help text for KV caching, scheduling, continuous decoding, and speculative-decoding options.

Documentation:

  • Add a dedicated MI355X playbook covering the SGLang 0.5.17 / ROCm 7.2.4 environment, launch recipes, benchmarking, accuracy results, and gotchas.
  • Update the README and existing GLM-5.2 playbook to distinguish current gfx950 guidance from unaffected gfx942 guidance.

Tests:

  • Generate new cookbook rows directly from bench_serving JSONL results to avoid manual transcription.

Re-measures the gfx950 cell on rocm/sgl-dev:v0.5.17-rocm724-mi35x-20260820
(ROCm 7.2.4, aiter d9e5ef7ce). Three of the existing cell statements have
expired on that image, two of them being its own declared gaps; the playbook
documents what changed and how to check each claim, and the generator turns
bench_serving JSON into models.js rows the way gen_cookbook_rows.py does for
Kimi-K3. No data change in this commit.

The continuity check that makes the rest worth reading: the published recipe
carried onto the new image reproduces its own c=16 row to 0.7% (531.7 vs
535.66 output tok/s, TPOT 25.01 vs 25.22), so everything else is a delta
measured on one harness rather than two labs.
verify-cookbook.js fails a launch command containing a flag with no glossary
entry, and the three new gfx950 recipes use four it does not have yet:
--schedule-policy, --num-continuous-decode-steps, --speculative-num-steps and
--speculative-eagle-topk.

Also rewrites --kv-cache-dtype, which said bf16 is required with the DSA
tilelang path. That is true on CUDA and not on ROCm: _check_tilelang_dsa_fp8_kv
raises only when not hip, and the measured gfx950 pool roughly doubles on
fp8_e4m3.
…P works, fp8 KV legal

Replaces the single gfx950 cell with three and closes both gaps it declared.
gfx942 is untouched. Rows generated by gen_glm52_mi355x_rows.py; each cell's
published launch command is built from the argv.txt of the server that produced
its numbers, so a recipe cannot drift from its own table.

1. The two mandatory bpreshuffle patches are retired on ROCm >= 7.2. The cell
   stated its own exit condition -- 'the CK rewrite ROCm/rocm-libraries#8639
   supersedes the disable workaround; not in aiter 7d604afe5'. This image ships
   aiter d9e5ef7ce and #8639 is in its CK submodule, and sglang 0.5.17 gates
   rather than disables. A/B on identical argv: GSM8K 0.980 both arms, and the
   patched arm is slower at every concurrency on both request shapes
   (-8.8/-8.5/-9.0% at ISL 8192, -7.2/-7.0/-7.0% at 76k), with output-token
   counts identical to 0.00% in all six pairs. The gotcha is rewritten, not
   deleted: below ROCm 7.2 the patches are still required.

2. gaps[1] 'MTP not enabled on AMD' -- it is. GlmMoeDsaForCausalLMNextN, draft
   in the same checkpoint, one flag. Isolated against a control differing by
   exactly that flag: 2.58-2.76x decode from 8k to 256k for 1.03-1.06x prefill.

3. 'KV-cache dtype must be bfloat16 with DSA tilelang' is a CUDA rule. On ROCm
   fp8_e4m3 takes the pool 1,654,976 -> 3,194,368 tokens at +0.0pp GSM8K and
   -0.38% accept length.

4. gaps[0] 'balanced / high-throughput' -- both cells filled.

Two things the cells say out loud rather than bury. The balanced recipe ships
--mem-fraction-static 0.85, not 0.88: at 0.88 it aborts the process on a 131k
prefill with HSA_STATUS_ERROR_OUT_OF_RESOURCES at a KV token usage of 0.04, and
0.85 clears 262k for 5.5% of pool and no measurable speed. And the benchmark
rows are --dataset-name random, which this repo already established overstates
accept length; degeneracy_probe.py run against GLM-5.2 returns 9 unique tokens
of 512 with a top 8-gram repeated 27 times, and accept length is 3.994 on
random against 2.969 on ShareGPT and 3.555 on GSM8K. The rows stay for
comparability with the cell they replace; the cell says to size from real text.

Three honest gaps replace the two closed ones: long-context accuracy under fp8
KV (unverified, with the diff harness to run), AIME25 not re-run on 0.5.17, and
DP-attention + DeepEP still untried.
DROP THIS COMMIT IF YOU DISAGREE -- it is the only one here that changes a
number you published, and it is deliberately last so it lifts cleanly. If it
goes, the new gfx950 rows should drop total_tok_s too, or the compare table's
'peak total' column reads a 19x MI300X -> MI355X gap that is a units change.

app.js captions the column 'total tok/s adds input tokens', and
gen_cookbook_rows.py emits it from bench_serving's total_throughput. The
glm-5.2-fp8 rows carry aggregate OUTPUT throughput there instead. Three
independent confirmations, no measurement involved:

  1. The gfx942 cell holds the same c=1 point twice, once per convention:
     total_tok_s 47.8 in one row and 430.7 in the other. 47.8 x 9 = 430.2.
  2. bench_serving defines total_throughput = (in+out)/duration and
     output_throughput = out/duration, so the ratio is exactly 1 + isl/osl
     independent of duration, and exact for --random-range-ratio 1.0.
  3. Re-running the gfx950 recipe on a newer image reproduces its published
     c=16 figure as OUTPUT throughput to 0.7% (531.7 vs 535.66), while that
     run's actual total_throughput is 4785.6.

So nothing measured is rewritten: the published figure moves to output_tok_s,
where it is correct, and total_tok_s / tok_s_per_gpu are recomputed. The row
that was already input-inclusive is detected and left alone -- which is also
the check that the factor is right, since the recomputed twin lands on 430.2
against its 430.7.
@sourcery-ai

sourcery-ai Bot commented Aug 22, 2026

Copy link
Copy Markdown

Reviewer's Guide

Re-measures and extends the GLM-5.2-FP8 MI355X (gfx950) cookbook support on SGLang 0.5.17/ROCm 7.2.4 by retiring the old bpreshuffle patches, enabling MTP/NEXTN and fp8 KV on ROCm, filling the previously empty balanced/high-throughput cells via an automated row generator, updating playbooks and model metadata, and refining CLI flag docs and performance/accuracy notes.

Sequence diagram for NEXTN speculative decoding on ROCm

sequenceDiagram
    participant Client
    participant Server as SGLangServer
    participant Draft as NEXTN_Draft
    participant Target as GLM52_Target
    Client->>Server: Request with --speculative-algorithm NEXTN
    Server->>Draft: Draft model forward pass
    Draft-->>Server: Propose 4 tokens
    Server->>Target: Verify proposed tokens
    Target-->>Server: Accepted prefix and correction
    Server-->>Client: Stream verified output
Loading

State diagram for gfx950 bpreshuffle compatibility

stateDiagram-v2
    [*] --> LegacyImage
    LegacyImage: Older SGLang/aiter or ROCm below 7.2
    LegacyImage --> Patched: Disable bpreshuffle at both sites
    Patched --> Correct: GSM8K remains valid
    ModernImage
    ModernImage: SGLang 0.5.17 / ROCm 7.2.4 / aiter d9e5ef7ce
    ModernImage --> FastPath: Keep bpreshuffle enabled
    FastPath --> Correct: CK fix is present
    ModernImage --> SlowPath: Apply legacy patches
    SlowPath --> Correct: Accuracy unchanged, throughput reduced
Loading

Flow diagram for MI355X recipe selection and KV capacity

flowchart TD
    Start["Choose MI355X recipe"] --> Latency["Need low single-stream latency?"]
    Latency -->|yes| Low["low-latency\nbfloat16 KV + NEXTN\nKV pool 1,645,440 tokens"]
    Latency -->|no| Spec["Need speculative decode?"]
    Spec -->|yes| Balanced["balanced\nfp8_e4m3 KV + NEXTN\nKV pool 3,194,368 tokens"]
    Spec -->|no| High["high-throughput\nfp8_e4m3 KV\nKV pool 3,717,888 tokens"]
    Low --> Capacity["Check peak context"]
    Balanced --> Capacity
    High --> Capacity
    Capacity --> Rule["capacity = floor(KV pool tokens / peak context tokens)"]
    Rule --> Safe["Keep runnable concurrency at or below capacity"]
Loading

File-Level Changes

Change Details Files
Document GLM-5.2-FP8 support for MI355X with bf16 and fp8 KV, including speculative decoding and new playbook.
  • Extend README model table entry for GLM-5.2-FP8 to list gfx950 / MI355X and note FP8 weights with bf16 or fp8 KV cache.
  • Add a new GLM-5.2-FP8 on MI355X playbook that supersedes the gfx950 half of the existing GLM-5.2 playbook, detailing three recipes (low-latency, balanced, high-throughput), environment, launch commands, benchmarks, accuracy, and gotchas.
  • Update the original GLM-5.2-FP8 playbook to point gfx950 users to the new MI355X playbook, clarify that bpreshuffle patches are image-specific and now superseded on 0.5.17, and annotate gfx950 sections as historical for ROCm 7.2.0 only.
README.md
glm52_fp8_mi355x_playbook.md
glm52_fp8_playbook.md
Clarify server CLI documentation for KV dtype, scheduler controls, and speculative decode parameters in the web app.
  • Relax and qualify the --kv-cache-dtype help text to describe fp8_e4m3 benefits and ROCm-only support for tilelang DSA, instead of stating bf16-only.
  • Add explanations for how --max-running-requests interacts with KV pool capacity, what --schedule-policy does, and how --num-continuous-decode-steps trades off scheduling overhead vs admission granularity.
  • Document additional speculative decoding knobs --speculative-num-steps and --speculative-eagle-topk in the help map.
  • Clarify several scheduler/DSA-related flag descriptions to be more operational (e.g., concurrency ceilings, decode graphs).
app.js
Automate generation of GLM-5.2-FP8 MI355X gfx950 cookbook rows directly from bench_serving JSONL outputs.
  • Introduce a script that scans per-config result directories, parses the last JSON object from bench_serving .jsonl files for known workloads, and materializes table rows in the same layout as existing cookbook generators.
  • Encode workload-to-shape mappings (ISL/OSL) and sort keys to match existing table ordering (concurrency sweep then ISL ladder).
  • Compute derived metrics like decode_tok_s, total_tok_s, and tok_s_per_gpu and annotate each row with a structured source string identifying section and environment.
gen_glm52_mi355x_rows.py
Refresh GLM-5.2-FP8 model metadata to match the new MI355X measurements, capabilities, and launch recipes.
  • Broaden precision description to allow bf16 or fp8_e4m3 KV and update the top-level summary, memory, speculative decode, KV cache, tuning, and benchmarking notes to describe 0.5.17 behavior and findings (bpreshuffle fix, NEXTN, accept lengths, KV pool sizing).
  • Replace the previous MI300X benchmark rows’ total/derived token-throughput values with corrected ones that match the published output_tok_s and concurrency semantics.
  • Replace the old gfx950 single MI355X config with three MI355X configs (low-latency, balanced, high-throughput), each with its own launch command, environment, aiter metadata, GSM8K accuracy, bench_serving metrics, gotchas, and provenance using the new 0.5.17 image.
  • Adjust gaps to describe remaining open items (fp8 KV long-context accuracy, AIME25 on 0.5.17, DP-attention/DeepEP).
models.js

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="glm52_fp8_mi355x_playbook.md" line_range="147-148" />
<code_context>
+
+```bash
+# gfx950 / MI355X, balanced: MTP speculative decode ON TOP OF an fp8_e4m3 KV
+# cache. fp8 KV is ROCm-only on the tilelang DSA path. mem-fraction is 0.88
+# and not higher on purpose -- see the lazy-kernel gotcha.
+export SGLANG_USE_AITER=1
+export PYTORCH_HIP_ALLOC_CONF=expandable_segments:True
</code_context>
<issue_to_address>
**issue:** The balanced recipe comment says `--mem-fraction-static 0.88` is intentional, but the executable command uses `0.85`; the same playbook documents that the speculative fp8 configuration aborts at 0.88 during a long prefill. A reader copying the documented setting can therefore reproduce the process-aborting configuration instead of the shipped safe recipe.

**Triggers:** When a user follows the balanced recipe's inline comments or copies the stated memory fraction rather than the command argument.

**Suggested fix:** Change the comments to state `0.85`, and explicitly identify `0.88` as the failing tested value.

```suggestion
# cache. fp8 KV is ROCm-only on the tilelang DSA path. mem-fraction is 0.85.
# The tested value 0.88 fails during long prefill -- see the lazy-kernel gotcha.
```
</issue_to_address>

Sourcery assessment

Approval pending. 1 finding to address first.

Blocking findings: glm52_fp8_mi355x_playbook.md:148


Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +147 to +148
# cache. fp8 KV is ROCm-only on the tilelang DSA path. mem-fraction is 0.88
# and not higher on purpose -- see the lazy-kernel gotcha.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue: The balanced recipe comment says --mem-fraction-static 0.88 is intentional, but the executable command uses 0.85; the same playbook documents that the speculative fp8 configuration aborts at 0.88 during a long prefill. A reader copying the documented setting can therefore reproduce the process-aborting configuration instead of the shipped safe recipe.

Triggers: When a user follows the balanced recipe's inline comments or copies the stated memory fraction rather than the command argument.

Suggested fix: Change the comments to state 0.85, and explicitly identify 0.88 as the failing tested value.

Suggested change
# cache. fp8 KV is ROCm-only on the tilelang DSA path. mem-fraction is 0.88
# and not higher on purpose -- see the lazy-kernel gotcha.
# cache. fp8 KV is ROCm-only on the tilelang DSA path. mem-fraction is 0.85.
# The tested value 0.88 fails during long prefill -- see the lazy-kernel gotcha.

@jhinpan
jhinpan merged commit 51da672 into jhinpan:main Aug 22, 2026
1 check passed
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.

2 participants