frank-primary cutover: GLM-5.3-Flash W4A16 int4 (Intel AutoRound)

Payload: Intel/GLM-5.3-Flash-W4A16-AutoRound (AutoRound symmetric int4 group
128, auto_round:auto_gptq, 34 shards + 2 extras, 113,074 tensors, ~170 GiB).
It is the only W4A16 checkpoint this fork can serve with expert offload: its
quant_method "auto-round" is overridden to "inc", which routes the MoE to
libr4d's grouped W4A16 GEMM (R4dW4a16MoEMethod). A compressed-tensors W4A16
build of the same model resolves to TritonWNA16Experts, which has no offload
recipe, and refuses to serve at all (170 GiB of experts cannot fit 64 GiB of
VRAM, so offload is mandatory).

Shape: tcclaviger RDNA4 vLLM fork (same digest-pinned image as the 2026-09-29
qwen cutover), TP=2, 131,072 ctx x 2 seqs, bf16 KV (fp8 is ILLEGAL here — it
disables the sparse MLA backend's rope-free Triton path, the only MLA decode
gfx12 has), util 0.98, NBATCH 2048, --enable-expert-offload
--expert-precise-memory, prefix caching, glm47 tool + reasoning parsers,
MTP and expert prefetch absent by measurement.

Measured: decode 9.9-10.0 tok/s, prefill 918 tok/s (warm), arena 14.51 GiB =
2,383 slots/rank over 12,096 host-backed experts (73.64 GiB pinned/rank).
THE 35 tok/s TARGET IS NOT MET and the shortfall is measured: live telemetry
(R4D_LRU_TELEMETRY=1) gives a 0.53-0.66 per-layer hit rate, a step moves
0.82 GiB of misses in ~100 ms = 8.2 GB/s effective against a link measured at
22.74 GB/s — the same structural gather-path gap the qwen campaign found.
Growing the arena 13.08 -> 14.51 GiB changed decode by nothing.

Rejected by measurement: MTP4 (head loads, acceptance 3.76-4.90, but decode
unchanged and prefill collapses 918 -> 255 — a verification forward gathers the
union of the batch's experts), NBATCH 8192 (identical prefill).

Also: scripts/patches/gpu_worker-kv-cap.patch (fork-local fix for a KV-reserve
model that under-counts GLM's per-sequence KDA state; not needed by this shape),
runbook, AGENTS.md row + section, both indexes, work log.

Rollback: 2339fae (qwen FP8 + MTP4).
This commit is contained in:
Frank
2026-09-30 04:22:11 -07:00
parent 2339faeaa4
commit f2bcf84e88
12 changed files with 668 additions and 224 deletions
+65
View File
@@ -0,0 +1,65 @@
# gpu_worker-kv-cap.patch — fork-local fix for the expert-offload KV cap.
#
# STATUS: NOT part of the deployed frank-primary shape. It exists because the
# fork's plan under-models this model's KV, and it is the enabler for a future
# MTP attempt (the production GLM shape runs without MTP and boots fine on the
# plan's own reserve).
#
# THE BUG: the plan models KV as 13600 B/token + a small mamba term, but
# GLM-5.3-Flash also carries a per-sequence KDA state (34 linear-attention
# layers) and, under MTP, the draft model's KV. gpu_worker then CAPS the KV
# cache at that reserve, so the plan's own arithmetic can make a legal
# --max-model-len unbootable. Measured 2026-09-30 on the pinned image:
# - no MTP, max_model_len 131072: reserve 1.74 GiB >= need 1.86 GiB -> boots
# - MTP4, max_model_len 131072: reserve 1.74 GiB < need 2.07 GiB -> refuses
# - MTP4, max_model_len 262144: reserve 3.43 GiB < need 3.62 GiB -> refuses
# ("To serve at least one request with the model's max seq len ...")
# Raising max_model_len does not fix it: the cap's slope (~13,845 B/token) only
# overtakes the need's (~12,700 B/token + 0.52 GiB fixed) around 440k tokens,
# where the KV reserve would eat a third of the expert arena.
#
# THE FIX: let an explicit operator KV request raise the cap. The compile and
# graph-capture headroom still comes off the top, so the blast radius is one
# flag the operator chose.
#
# APPLY (into a host dir that is then bind-mounted read-only over the image):
# IMAGE=docker.io/tcclaviger/vllm@sha256:da4029666668b90a0386c1ed23ea370c79916ad4e1a6ad1dd9db098b9fa069ea
# D=/home/dank/glm-patch; mkdir -p $D/vllm/v1/worker
# podman run --rm --entrypoint cat $IMAGE \
# /opt/vllm/lib/python3.14/site-packages/vllm/v1/worker/gpu_worker.py \
# > $D/vllm/v1/worker/gpu_worker.py
# ( cd $D && patch -p1 < .../scripts/patches/gpu_worker-kv-cap.patch )
# # then add to the podman run:
# # -v $D/vllm/v1/worker/gpu_worker.py:/opt/vllm/lib/python3.14/site-packages/vllm/v1/worker/gpu_worker.py:ro
# # -e CLAV_KV_RESERVE_FLOOR_GIB=<needed> (or pass --kv-cache-memory-bytes)
#
# Base file sha256 (the pristine file out of the pinned image):
# 1f3942b1a01af075317161b06077a2e658086f1da0566c9f297129ceebe37dd7
# Patched file sha256 (verified 2026-09-30, patch applies clean and reproduces
# these bytes exactly):
# 80cd160bbc01270b0851f7b6fe7552b06ae2fae2428134fc29238d7f452af840
#
--- a/vllm/v1/worker/gpu_worker.py
+++ b/vllm/v1/worker/gpu_worker.py
@@ -824,6 +824,21 @@
expert_cfg = self.vllm_config.offload_config.expert
if expert_cfg.enable_expert_offload and expert_cfg.planned_kv_reserve_bytes > 0:
cap = int(expert_cfg.planned_kv_reserve_bytes)
+ # LOCAL PATCH (GLM-5.3 KDA + MTP draft): the plan models KV as
+ # 13600 B/token + a small mamba term, but this model also carries a
+ # per-sequence KDA state and (under MTP) the draft's KV, so the
+ # reserve can sit BELOW what max_model_len legitimately needs -- and
+ # the cap then makes a legal config unbootable (measured: need
+ # 2.07 GiB vs cap 1.74 GiB at max_model_len 131072 with MTP). An
+ # explicit --kv-cache-memory-bytes, or CLAV_KV_RESERVE_FLOOR_GIB,
+ # is the operator's word: let it raise the cap. The compile/capture
+ # headroom still comes off the top.
+ _explicit = self.cache_config.kv_cache_memory_bytes
+ _floor = os.environ.get("CLAV_KV_RESERVE_FLOOR_GIB")
+ if _explicit:
+ cap = max(cap, int(_explicit))
+ if _floor:
+ cap = max(cap, int(float(_floor) * (1 << 30)))
if self.available_kv_cache_memory_bytes > cap:
logger.info_once(
"expert offload: KV capped at the plan's reserve %s GiB; %s GiB of "