feat(BACKEND-TENSTORRENT-KEEPQUANT): the 27B fits — packed GDN projections, int8-dot captured decode, per-request logits gather (#3042) - #3183
Merged
Conversation
lu-zero
force-pushed
the
row/BACKEND-TT-KEEPQUANT-W3
branch
from
September 13, 2026 15:28
41d8c57 to
0d30f3d
Compare
lu-zero
added a commit
to lu-zero/vllm.cpp
that referenced
this pull request
Sep 13, 2026
… the TT build (mudler#3042) The warm-slot release calls ran unconditionally in BOTH LoadedEngine ctor ends, but model_loader.cpp includes tenstorrent_device.h behind #if defined(VLLM_CPP_TENSTORRENT) — on every non-TT build (CPU, Vulkan, Windows, the sanitizers) the vt::tenstorrent namespace is not declared and the whole board went red at the first compile ("'vt::tenstorrent' has not been declared", model_loader.cpp:2403 and :3187, the mudler#3183 CI run). The calls are TT-only recipe hooks (VT_TT_RELEASE_WARM_ROWS releases TT warm slots; without the backend there is nothing to release), so the fix gates them behind the same define — the semantics are unchanged on the TT build and the non-TT builds return to their pre-wave behaviour. Verified both ways locally: the TT build compiles, and the exact CI compile command minus the TT define is -fsyntax-only clean on this TU. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:zai/glm-5.3-flash [maki]
…entation (mudler#3042) Uncommitted work-in-progress preserved from the merged W1+W2 worktree before its removal. The 27B instrumented re-check needs it: per-block AllocTraceSnapshot pre/post brackets in DenseForwardLayers (attention, GDN, norms, rolls — the 13 GB the chunk-loop ledger did not cover) and chunk-loop taps in KQuantGrouped, plus a defensive-only rewording of the sl_alias guard (the E>1 arm can never hit the full-extent identity) and new backend-suite coverage. Not reviewed, not gated, not for merge as-is. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:zai/glm-5.3-flash [maki]
…s the GDN projections block-encoded (mudler#3042) The 27B fit campaign's attribution is complete and the fix is now a design decision with numbers: the GDN projection family (attn_qkv, attn_gate) is routed kTransformedWeight when the V-head reorder is active (qwen3_5_gguf_weights.cpp:1104-1112), which forces kExpandBf16 — 97 x [10240,5120] bf16 stagings, 9.7 GiB beside the packed words, banks at 99 percent during generation. The reorder permutes whole V rows and each row is a contiguous run of whole blocks, so a block-verbatim row reorder (ReorderVRowsPacked) keeps the tensors keep-quant eligible: ~6-7 GiB of bf16 staging becomes ~1.6 GiB of words. ssm_out's column reorder is block-unsafe and stays expand-bf16, recorded as owed. Evidence chain: the slot census (dev 2.2 GiB attn_qkv bf16 + pers 9.7 GiB), the GGUF type scan (attn_qkv q6_K, 21.5 MiB packed), and the run matrix — default chunks die at warm block 55, 64 MiB chunks survive to generation and die at decode with banks at 99 percent. Red-first tests, the equivalence gate, and the stop conditions are in the spec. Implementation lands red-first on this branch next. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:zai/glm-5.3-flash [maki]
…mixed prefill steps run eager (mudler#3042) The last 27B OOM after W4's packed GDN projections: the single-shape graph driver captures the mixed warm step (B ~= 640 tokens) and replays it every step, so the head's f32 output is a [B, 248320] plane — 606 MiB — on every step, while a decode step samples ~10 rows (~16 MiB). Banks at 99.5 percent make that ask fatal (measured /tmp/w4-run.log: 635,699,200 B, largest free block 29.8 MB). The fix mirrors vLLM's logits semantics: capture the decode graph at the decode shape, run mixed prefill steps eagerly through the existing cold path with per-sequence last-token logits_indices (the gather-before-lm_head already fires on non-empty indices). Red-first tests, the no-regression net, and the stop conditions (eager-step resident-input reuse; capture re-warm cost measured, never absorbed) are in the spec. Implementation is the next session's first task; the W4 packed-reorder validation it builds on is in /tmp/w4-run.log. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:zai/glm-5.3-flash [maki]
…d keep-quant decode (mudler#3042) The eager 27B arm is not a system: an 8-hour bootstrap died at SIGTERM mid-generation (RC=124, 29/35 assertions, no ids — the keep-quant decode re-runs the whole-model word decode per token, 40-70+ s/token). The captured arm cannot fit either: the E=1 chunk-decode chain's transients are pinned into the trace region for replay, and the pinned sum reaches 3.8 GiB (mesh_trace.cpp:82) against <1 GiB free — at any chunk size, because the chunk size changes granularity, not the pinned volume (/tmp/w5-run3.log:27946-27948 shows kq-decode/repair deltas inside the capture; the fatal is in end_trace_capture). W4b already landed the designed answer, default OFF: the int8-dot kernel —"One captured launch replaces the per-chunk E=1 chain (the capture-demand gate this row owes)". W6 extends it to every captured keep-quant matmul (bf16-out arms with the explicit f32->bf16 store cast the W4b comment names; the head keeps f32-out, a native int8-dot fit), asserts zero kq-decode inside the capture scope (red-first: the w5-run3 ledger), and re-anchors the int8-dot arm on its OWN device pair rather than moving any band — the recorded 1125-mnat one-flip deviation is a ratified property of the arm, the existing arms and bands untouched. Supersedes W5's trace-region framing (its logits-gather half is already implemented and lands with this wave). The APEX-I-Nano stays owed to QUANT-GGUF-IQ-TENSTORRENT. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:zai/glm-5.3-flash [maki]
…tions, int8-dot captured decode, per-request logits gather (mudler#3042) The Qwen3.8-27B Q4_K_M token gate is GREEN on the Blackhole P150: 16/16 prompts, 0 forward-divergent, max gap 0.062 nats against the llama.cpp-b10451 oracle (band 500 mnats), knob-free captured decode. Backend proof: device type 6, 0 declines — kPagedAttention 4096, kGdnDecode 11520, kCausalConv1dUpdate 11520 selections. The founding blocker of the keep-quant row — GGUF k-quant arms refuse on the P150 — is resolved end to end. Three stacked defects stood between the card and the verdict, each root-caused with the W4d W0 allocation ledger and fixed here: 1. THE PLACEMENT PROBE (landed in mudler#3165): the TT platform reported no device memory total, so the device fit refused every qwen3.8 checkpoint and placed them on CPU — the card was never reached. 2. THE 12 GIB BF16 GDN RESIDENCY: the V-head reorder routed the GDN projection family (attn_qkv q6_K, attn_gate q4_K) to kTransformedWeight -> kExpandBf16 — 97 x [10240,5120] bf16 stagings, 9.7 GiB beside the packed words. The reorder permutes WHOLE rows, so the fix permutes the packed bytes (ReorderVRows<uint8_t> — the template is type-agnostic) and keeps the projections block-encoded: the GDN dispatch decodes them through their keep-quant words (MatmulBTRawD over the split-packed pair; the merged arm needs one encoding, this checkpoint mixes q6_K qkv with a q4_K gate). Slot residency 23.8 -> 7.7 GiB, measured; all 64 blocks warm (previously died at block 55). Equivalence proven bit-for-bit: packed-byte reorder == element reorder through the real dequantizer, q6_K + q4_K (standalone TT-free check; ASan also caught a row_off-scaling bug in the CHECK'S OWN reference — the shipped template was correct). 3. THE CAPTURE-TIME TRANSIENTS: the E=1 chunk-decode chain's planes are pinned into the trace region for replay (>= 3.8 GiB at 27B — mesh_trace.cpp:82 — invariant to chunk size), and the lm_head's [31040,5120] dequant chunk (606 MiB) fragmented the banks besides. W4b's int8-dot kernel was built for exactly this capture-demand gate and this wave turns it on for every captured keep-quant matmul: the bf16-out arms join via an explicit f32->bf16 cast before the commit (the W4b store-geometry bug — f32 bytes in a bf16 slot — fixed and red/green-proven by a device-consumer probe: the uncast output fed a second device matmul produces non-finite garbage). Zero kq-decode labels fire inside the capture scope now. Plus: the keep-quant word shadows stage at LOAD (a lazy first-matmul stage at 27B asked the head's 1 GiB words against a 100 MB largest free block); the per-request logits gather lands (vLLM logits_processor semantics, LastTokenLogitsIndices, both driver Step fallbacks — the mixed-step lm_head computed all T rows of logits when generation samples only the last token per sequence); and the 0.8B vehicle keeps its committed arms untouched (the int8-dot lever is env-gated VT_TT_KEEPQUANT_INT8DOT, default off). Goldens: the anchor pair (our_ids.npy + neartie_gap_mnats.npy) derives from the engine's own bootstrap capture, teacher-forced through the pinned llama.cpp b10451 oracle (DequantGgufRowToF32-verified bit-for-bit), and the GREEN run validates it as this arm's grading anchor: the device ids landed inside the near-tie band at every cell. The W5 decode-shaped-capture spec's premise (a [B,vocab] logits plane) was falsified by the alloc trace — the ask was the head's dequant chunk — amended in its file; its gather half lands here. The 0.8B vehicle gate: 16/16, byte-identical to its committed baseline (STRICT 8/16, near-tie 8/16, max 0.25 nats). Backend suite 73/73 (524,442 assertions) including the four bit-exact keep-quant sweeps. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:zai/glm-5.3-flash [maki]
… the TT build (mudler#3042) The warm-slot release calls ran unconditionally in BOTH LoadedEngine ctor ends, but model_loader.cpp includes tenstorrent_device.h behind #if defined(VLLM_CPP_TENSTORRENT) — on every non-TT build (CPU, Vulkan, Windows, the sanitizers) the vt::tenstorrent namespace is not declared and the whole board went red at the first compile ("'vt::tenstorrent' has not been declared", model_loader.cpp:2403 and :3187, the mudler#3183 CI run). The calls are TT-only recipe hooks (VT_TT_RELEASE_WARM_ROWS releases TT warm slots; without the backend there is nothing to release), so the fix gates them behind the same define — the semantics are unchanged on the TT build and the non-TT builds return to their pre-wave behaviour. Verified both ways locally: the TT build compiles, and the exact CI compile command minus the TT define is -fsyntax-only clean on this TU. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:zai/glm-5.3-flash [maki]
) The wave's insertions moved LoadQwen3_5MTPFromGguf (1458 -> 1457 after the rebase accounting), LoadGgufSharedEmbedAndHeadBf16 (1045 -> 1044) and the GDN registration block (8329-8343, unchanged content, re-measured post-rebase). Checker green: ANCHOR-ROT 33 within baseline. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:zai/glm-5.3-flash [maki]
lu-zero
force-pushed
the
row/BACKEND-TT-KEEPQUANT-W3
branch
from
September 13, 2026 17:06
b881c86 to
192ba68
Compare
The wave's insertions above the shared-head loader moved the symbol; the branch tree holds it at 1065 (measured; the 1044 citation was a cross-worktree misread during the rebase churn). check-agent-record: the gguf_weights citation resolves OK. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:zai/glm-5.3-flash [maki]
The warm-slot release knob was read from production code but not documented — the env-doc gate refuses exactly that. It is a user-facing memory lever (frees the warm pass's full-batch-shape activation slots after the cold pre-warm), so it documents rather than allowlists. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:zai/glm-5.3-flash [maki]
lu-zero
added a commit
that referenced
this pull request
Sep 13, 2026
… the TT build (#3042) The warm-slot release calls ran unconditionally in BOTH LoadedEngine ctor ends, but model_loader.cpp includes tenstorrent_device.h behind #if defined(VLLM_CPP_TENSTORRENT) — on every non-TT build (CPU, Vulkan, Windows, the sanitizers) the vt::tenstorrent namespace is not declared and the whole board went red at the first compile ("'vt::tenstorrent' has not been declared", model_loader.cpp:2403 and :3187, the #3183 CI run). The calls are TT-only recipe hooks (VT_TT_RELEASE_WARM_ROWS releases TT warm slots; without the backend there is nothing to release), so the fix gates them behind the same define — the semantics are unchanged on the TT build and the non-TT builds return to their pre-wave behaviour. Verified both ways locally: the TT build compiles, and the exact CI compile command minus the TT define is -fsyntax-only clean on this TU. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:zai/glm-5.3-flash [maki]
lu-zero
added a commit
to lu-zero/vllm.cpp
that referenced
this pull request
Sep 14, 2026
The row spec's Now section still ended at "W3 - the 27B e2e gate rerun - is the remaining W4d wave". That wave landed: the 27B gate is 16/16 greedy-exact on the P150, captured and knob-free, via the placement probe, the packed GDN byte reorder, and the W6 int8-dot captured decode (mudler#3165, mudler#3161, mudler#3183, main e61f2b1). The amendment records the recipe, the falsified W5 premise, the eager-arm verdict, and the still-owed items (the P150 benchmark, the 0.8B int8-dot anchor, the embed-table gather, the ssm_out permutation). Lifecycle stays ACTIVE; the benchmark numbers will land separately. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:zai/glm-5.3-flash [maki]
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
feat(BACKEND-TENSTORRENT-KEEPQUANT): the 27B fits — packed GDN projections, int8-dot captured decode, per-request logits gather (#3042)
The Qwen3.8-27B Q4_K_M token gate is GREEN on the Blackhole P150:
16/16 prompts, 0 forward-divergent, max gap 0.062 nats against the
llama.cpp-b10451 oracle (band 500 mnats), knob-free captured decode.
Backend proof: device type 6, 0 declines — kPagedAttention 4096,
kGdnDecode 11520, kCausalConv1dUpdate 11520 selections. The founding
blocker of the keep-quant row — GGUF k-quant arms refuse on the P150 —
is resolved end to end.
Three stacked defects stood between the card and the verdict, each
root-caused with the W4d W0 allocation ledger and fixed here:
THE PLACEMENT PROBE (landed in fix(BACKEND-TENSTORRENT-KEEPQUANT): probe the TT DRAM total so the device fit can resolve (#3042) #3165): the TT platform reported no
device memory total, so the device fit refused every qwen3.8
checkpoint and placed them on CPU — the card was never reached.
THE 12 GIB BF16 GDN RESIDENCY: the V-head reorder routed the GDN
projection family (attn_qkv q6_K, attn_gate q4_K) to
kTransformedWeight -> kExpandBf16 — 97 x [10240,5120] bf16 stagings,
9.7 GiB beside the packed words. The reorder permutes WHOLE rows, so
the fix permutes the packed bytes (ReorderVRows<uint8_t> — the
template is type-agnostic) and keeps the projections block-encoded:
the GDN dispatch decodes them through their keep-quant words
(MatmulBTRawD over the split-packed pair; the merged arm needs one
encoding, this checkpoint mixes q6_K qkv with a q4_K gate). Slot
residency 23.8 -> 7.7 GiB, measured; all 64 blocks warm (previously
died at block 55). Equivalence proven bit-for-bit: packed-byte
reorder == element reorder through the real dequantizer, q6_K +
q4_K (standalone TT-free check; ASan also caught a row_off-scaling
bug in the CHECK'S OWN reference — the shipped template was
correct).
THE CAPTURE-TIME TRANSIENTS: the E=1 chunk-decode chain's planes are
pinned into the trace region for replay (>= 3.8 GiB at 27B —
mesh_trace.cpp:82 — invariant to chunk size), and the lm_head's
[31040,5120] dequant chunk (606 MiB) fragmented the banks besides.
W4b's int8-dot kernel was built for exactly this capture-demand gate
and this wave turns it on for every captured keep-quant matmul: the
bf16-out arms join via an explicit f32->bf16 cast before the commit
(the W4b store-geometry bug — f32 bytes in a bf16 slot — fixed and
red/green-proven by a device-consumer probe: the uncast output fed a
second device matmul produces non-finite garbage). Zero kq-decode
labels fire inside the capture scope now.
Plus: the keep-quant word shadows stage at LOAD (a lazy first-matmul
stage at 27B asked the head's 1 GiB words against a 100 MB largest free
block); the per-request logits gather lands (vLLM logits_processor
semantics, LastTokenLogitsIndices, both driver Step fallbacks — the
mixed-step lm_head computed all T rows of logits when generation samples
only the last token per sequence); and the 0.8B vehicle keeps its
committed arms untouched (the int8-dot lever is env-gated
VT_TT_KEEPQUANT_INT8DOT, default off).
Goldens: the anchor pair derives from the engine's own bootstrap
capture, teacher-forced through the pinned llama.cpp b10451 oracle
(DequantGgufRowToF32-verified bit-for-bit), and the GREEN run validates
it as this arm's grading anchor: the device ids landed inside the
near-tie band at every cell.
The 0.8B vehicle gate: 16/16, byte-identical to its committed baseline
(STRICT 8/16, near-tie 8/16, max 0.25 nats). Backend suite 73/73
(524,442 assertions) including the four bit-exact keep-quant sweeps.
The three specs on this branch (W4 packed reorder, W5 gather — its
trace-region premise falsified by the trace and amended in-file, W6
int8-dot capture) carry the full evidence chain.
FOLLOWING_AGENTS_PROTOCOL
Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:zai/glm-5.3-flash [maki]