Skip to content

feat(glm52 mono): serve a BF16-attention checkpoint - #3266

Open
XiaobingSuper wants to merge 1 commit into
mainfrom
xiaobingsuper/glm52-mono-bf16-attention
Open

XiaobingSuper wants to merge 1 commit into
mainfrom
xiaobingsuper/glm52-mono-bf16-attention

Conversation

@XiaobingSuper

Copy link
Copy Markdown
Contributor

Ready for review. No blocking issues.

GLM-5.2 mono read attention only as PTPC FP8, so amd/GLM-5.2-MXFP4 served as shipped (BF16 attention, no online_quant_config) refused every width and ran the production path. This serves it.

Dev record

  • Found: on a BF16 checkpoint production keeps the projections row-major BF16 and W_K / W_V per-tensor FP8 (group-128 FP8 activations), the kernel's PTPC absorb path. The kernel's BF16 mode would pack a copy of every projection, 7.4 GB a GPU at TP4.
  • Decided: AttentionWeight.BF16_ABSORB_FP8 reads the BF16 projections in place (unit_bf16_rows) with production's FP8 W_UK / W_UV. The format comes from the loaded linears (attention_format), agreed over TP, named in the kernel. Mixed formats and ATOM_MLA_ABSORB_BF16=1 are refused; the fused indexer stays PTPC-only.
  • Decided: at S=8 the 32 MB q_a/kv_a weight is read in one 8-sample tile, not 6 + 2: 131.1 to 124.5 us a layer. Other builds are unchanged.
  • Surprised: PTPC's fused indexer is 1-4% slower than external at C1/C2, so a BF16 one is not included.
  • Left undone: AgentX replay. TP4 BF16 S=6/10/12 builds need 12-144 B of scratch (PTPC's S=5/10 already do); they ran without a hang.

Named result

Mono on vs off at its fastest MTP depth, 1K/1K, golden acceptance, tok/s:

C1 C2
TP4 312.2 vs 263.8 (+18.3%) 465.8 vs 458.0 (+1.7%)
TP8 393.3 vs 290.9 (+35.2%) 560.8 vs 514.8 (+8.9%)

Gates

  • CPU: tests/models/glm52, tests/test_native_monokernel.py 175 passed; black, ruff clean.
  • GSM8K 250 q 3-shot, MTP3, real acceptance, on vs off: TP4 0.928/0.944 vs 0.956/0.932 (paired p 0.41), TP8 0.932/0.928 vs 0.924/0.952; acceptance not lower.
  • Per layer vs production on the same input: BF16 mono deviates as much as PTPC mono.
Evidence

Output tok/s, mean of 2 runs, 1K/1K random, --spec-decode-acceptance-length 2.99 / 3.33 / 3.61 for MTP3 / 4 / 5, recipe flags (kv_cache_dtype fp8, index_cache_dtype fp4, block-size 64, max-num-seqs 2, graphs [1,2]), MI355X.

mono MTP3 off MTP3 off MTP4 off MTP5
TP4 C1 312.2 251.2 262.9 263.8
TP4 C2 465.8 438.8 458.0 452.1
TP8 C1 393.3 267.6 275.1 290.9
TP8 C2 560.8 488.1 513.5 514.8

Mono at MTP4 / MTP5 is slower than at MTP3 (TP4: 290.3 / 291.7 at C1, 310.8 / 370.0 at C2): each verify row adds about 9 experts' weights.

With an fp8 index cache and #3265 (DSA indexer mono on the indexers), mono off at its fastest depth (MTP5): TP4 323.0 / 475.4 at C1 / C2 against 268.5 / 469.5; TP8 410.1 / 607.7 against 281.1 / 527.1.

Mono layer time, TP4 (server trace, rank 0):

S=4 S=8
PTPC 81.0 us 123.3 us
BF16, two q_a tiles at S=8 87.7 us 131.1 us
BF16, this PR 87.7 us 124.5 us

The BF16 cost over PTPC is the extra ~50 MB a layer at HBM rate. Rejected after a single-layer A/B: prefetching W_o into the MALL or into registers before the uv tasks, deferring the q_b prefetch, wave-0 pre-polls, BF16 W_UK / W_UV, a packed copy of the projections (2% faster, +7.4 GB a GPU).

Compile (gfx950, external indexer): every BF16 build at TP4 / TP8, S = 1..12, fits LDS; scratch only at TP4 S=6 (12 B), 10 (144 B), 12 (24 B).

Per layer against production on the same input (eager, 16 GSM8K questions, mean over 75 layers): attention output 2.52% (PTPC mono 2.61%), layer output 5.65% (5.70%).

🤖 Generated with Claude Code

GLM-5.2 mono read attention only as PTPC FP8, so amd/GLM-5.2-MXFP4 served
without online_quant_config refused before building and ran the
production path.

Mono now reads the attention storage production loaded. On a BF16
checkpoint q_a/kv_a, q_b and W_o are read in place, row-major, and the
absorbed W_UK / W_UV keep production's per-tensor FP8 with group-128 FP8
activations (AttentionWeight.BF16_ABSORB_FP8). The format is taken from the
loaded linears, agreed over TP and named in the kernel; such a deployment
runs the external indexer. At S=8 the 32 MB BF16 q_a/kv_a weight is read
in one sample tile instead of two.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings November 12, 2026 15:02

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota. Try requesting a review at a lower effort level.

@github-actions

Copy link
Copy Markdown
Contributor

🏷️ CI Guide

Runs automatically on every eligible PR before approval:

  • ✅ Pre Checkin: Black, Ruff, catalog schema validation, non-GPU unit tests

Heavy model tests:

  • ✅ Run after the PR is approved and Pre Checkin passes
  • ✅ Run immediately when an approval review is submitted
  • ✅ Can be requested before approval with labels
Label Tests
ci:full Run all heavy PR model tests: native ATOM, vLLM, and SGLang
ci:atom Run native ATOM model accuracy tests
ci:vllm Run ATOM vLLM OOT model accuracy tests
ci:sglang Run ATOM SGLang model accuracy tests

Heavy jobs are skipped when the PR is not approved and no matching ci:* label is present.
Add labels via the sidebar or gh pr edit 3266 --add-label <label>

@valar-cop valar-cop 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.

a few small things..

per-width numerics for the tp4 bf16 s=6/10/12 mtp builds? gsm8k on/off and per-layer deviation

ci green.

self._refused.add(rows)
logger.warning("GLM-5.2 MonoKernel fallback before launch: %s", error)
return False
if self._attention is None:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

the disagree path is never tested, only the agree one. add a test that stubs group_max/group_min to disagree and asserts rows is refused?

This branch has not been deployed

No deployments
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.

4 participants