Skip to content

feat(BACKEND-TENSTORRENT-KEEPQUANT): the 27B fits — packed GDN projections, int8-dot captured decode, per-request logits gather (#3042) - #3183

Merged
lu-zero merged 9 commits into
mudler:mainfrom
lu-zero:row/BACKEND-TT-KEEPQUANT-W3
Sep 13, 2026
Merged

feat(BACKEND-TENSTORRENT-KEEPQUANT): the 27B fits — packed GDN projections, int8-dot captured decode, per-request logits gather (#3042)#3183
lu-zero merged 9 commits into
mudler:mainfrom
lu-zero:row/BACKEND-TT-KEEPQUANT-W3

Conversation

@lu-zero

@lu-zero lu-zero commented Sep 13, 2026

Copy link
Copy Markdown
Collaborator

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:

  1. 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.

  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 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]

@lu-zero
lu-zero force-pushed the row/BACKEND-TT-KEEPQUANT-W3 branch from 41d8c57 to 0d30f3d Compare September 13, 2026 15:28
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
lu-zero force-pushed the row/BACKEND-TT-KEEPQUANT-W3 branch from b881c86 to 192ba68 Compare September 13, 2026 17:06
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
lu-zero merged commit e61f2b1 into mudler:main Sep 13, 2026
19 of 28 checks passed
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
lu-zero deleted the row/BACKEND-TT-KEEPQUANT-W3 branch September 13, 2026 17:54
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]
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.

1 participant