engine: notice when a tensor's format silently disables the fused Metal decode path - #827
Conversation
|
Second Apple datapoint for this, from the other side of the same wall. I hit this exact path last week converting a GLM-5.2 REAP-504B container to fmt=6 on an M5 Curing it (rebuilding the container per-row so dense matches, The load-time notice would have saved me a full reconversion of diagnosis. Worth having. |
β¦e gates' predicate
The load-time format notice covered kv_b_proj only, and had already drifted
from the gates it describes: both fused Metal decode gates carry
(kv_b.fmt==2 || (kv_b.fmt==4 && !g_moe_exact)), but the notice tested
fmt!=2 && fmt!=4 -- so under COLI_METAL_MOE_EXACT=1 a grouped-int4 (fmt=4)
kv_b closed both gates while the notice stayed silent: exactly the silent
CPU-fallback trap the notice exists to kill.
Type the per-layer format condition ONCE, in a shared predicate:
- metal_fused_layer_fmt_miss(l) returns a bitmask (METAL_FUSED_*) of the
8 fused-bound weight tensors whose format blocks the fused path: kv_b on
the gates' own two-format+mode term, q_a/q_b/kv_a/o and (sparse layers
only) sh_gate/sh_up/sh_down on the metal_fused_fmt_ok {1,2,3,4} allowlist.
- Both gates now consult it against the mask of tensors their kernel binds
(METAL_FUSED_ATTN_TENSORS / METAL_FUSED_LAYER_TENSORS). No behavior
change: the masked test is the same boolean the inline conditions computed.
- kvb_fmt_gate_notice becomes metal_fmt_gate_notice: one line per offending
tensor KIND (bounded at 8 lines, never per layer), sh_* counted over the
sparse-layer population, MTP head excluded, silent without Metal. Because
the notice consumes the gates' predicate, the two cannot drift apart again.
- g_moe_exact moves next to g_metal_enabled, outside #ifdef COLI_METAL (the
same hoist JustVugg#587 did for g_metal_enabled): the predicate compiles on every
platform's test build; the env parse stays Metal-only, so the flag stays 0
elsewhere, which is correct there.
test_kvb_notice.c grows to a superset of the previous scenarios: per-kind
misses, multi-kind bounded output, all-pass/Metal-off/MTP silences, dense
sh_* exemption, predicate truth table and per-bit gate-mask membership in
both g_moe_exact states, mixed dense/sparse denominators, and the new
MOE-exact cases (fmt=4 kv_b notices under the mode, stays silent outside
it). test_fp8_load.c Part F re-pins the source-text wiring at the new shape:
7 metal_fused_fmt_ok(l->...) sites (all inside the helper) plus both gates'
predicate consultation. docs/FORMATS.md gains the format-consumer note for
the fused Metal decode path with anchors at this tree.
β¦rdening Review fixes on the widened fmt-gate notice: - The kv_b line's remedy was circular under COLI_METAL_MOE_EXACT: the converter's --group-size defaults to 64, so the suggested --kvb-bits 4 mints grouped int4 (fmt=4) -- exactly what the mode keeps off the fused path. The remedy clause is now selected on g_moe_exact: the plain suggestion outside the mode (unchanged), and under the mode the two real cures -- an ungrouped requant (--kvb-bits 4 --group-size 0 -> fmt=2) or, for a kv_b already at fmt=4, unsetting COLI_METAL_MOE_EXACT. Tests pin both variants (positive pins under the mode, a negative --group-size pin outside it). - The notice header claimed the fused gates never see the MTP head; false -- attention_rows evaluates the MTP row and its INT8 kv_b closes the gate there (carried, correct CPU fallback). Comment corrected to the actual rationale (main-layers-only by decision; a line about the INT8-by-design MTP head would be noise on every load). Comment-only: no gate change. - New accepted-behavior test case: runtime-quant dense configs (qt_alloc fmt=0 at bits>=16, fmt=3 at 3-bit) produce bounded, accurate output -- 8 lines all naming fmt=0, and exactly one kv_b line at fmt=3 -- so the behavior is claimed and pinned rather than accidental. - The test's stderr-capture seam now aborts loudly if dup/freopen fails instead of limping on with an undefined stderr. FORMATS.md anchors recomputed for the shifted lines.
353f5ea to
574aac1
Compare
|
Authored by Fable 5 in Claude Code, analysis in partnership with @monotophic. Rebased and re-derived onto current dev (base 1. Caused by dev moving. The kv_b gate condition on dev is now two formats with a mode term β 2. A pre-existing defect, found during the re-derive β with run evidence. The narrow notice now on dev has already drifted from the gates it describes: its condition omits the 3. The original widening. One per-layer format-miss bitmask covering kv_b plus the seven allowlist tensors ( The four promises this change makes:
Capstone matrix β the single decisive check per promise, at
Full requirement β instrument β result matrix
Durable vs current-state. The shared-predicate architecture and the tests are durable. Calibrations, checkable at Observations, not changes (pre-existing, disclosed so they're on the record): |
Authored by Fable 5 in Claude Code, analysis in partnership with @monotophic.
A container can mint any fused-bound tensor at a format the fused Metal decode
kernels don't accept β and today that silently pushes the affected layers'
decode attention onto the CPU path with no signal anywhere. We measured the
kv_b_proj case on an M5 Max at +22% end-to-end once cured. This PR makes the
condition visible at load, and makes it impossible for the diagnostic to drift
out of sync with the gates it describes:
metal_fused_layer_fmt_miss, a purebitmask over the 8 fused-bound tensor kinds) now backs BOTH fused-gate call
sites (
attention_rows,layer_forward_rows) and a new one-line-per-kind[METAL]stderr notice atmodel_init. Gate behavior is bit-identical βsee the matrix below. The notice names the tensor kind, the offending fmt,
the affected-layer count, the fmt 1/2/3/4 allowlist requirement (kv_b:
fmt=2 exactly, with the
--kvb-bits 4remedy), and stays silent on cleancontainers and non-Metal builds. sh_* kinds count over the sparse-layer
population (the only layers that load them); the MTP head is excluded by
design (commonly INT8, never fused).
obstacle; it does not claim the fused path would otherwise engage (the
gates' dims/batch/config preconditions are deliberately not duplicated).
requirements with anchors to the predicate, both gates, and the notice.
test_kvb_notice.c), incl. per-bit gate-mask membership; 7-mutation battery β every mutation caught, incl. a single mask bit droppedmake checkgreen at every commit; METAL=1 build zero warnings vs baselineCommit structure, verification detail, and observations
Commits by origin:
2078199β the original kv_b-only notice, mechanically rebased ontocurrent dev. Only resolution of note: the hand-maintained
TEST_BINSaddition was dropped β dev now auto-derives test binaries from build rules,
which removes that whole conflict class.
988223dβ the widening (all 8 fused-bound kinds) + the shared-predicateconsolidation (replaces 11 scattered allowlist call sites with 7 inside one
helper; the gates' comment blocks updated to match).
9fe7894β review round: sh_* denominators (sparse-layer population),per-bit mask-membership test (a dropped mask bit previously passed the
whole suite), mixed dense/sparse test shape, format-only semantics
documented in the notice's comment.
353f5eaβ the FORMATS.md consumer-requirements note.Verification: the equivalence sweep enumerated fmt β {0..8} per tensor Γ
sparse β {0,1} (all values outside {1,2,3,4} are one behavioral class, so the
domain covers every class with margin) and compared the old gate expressions,
transcribed verbatim from the base revision, against the shipped predicate at
-O3 and -O0. Compiler output at -O3 confirms the helper inlines into both
gates, the attention gate's masked-away sh_* reads are dead-code-eliminated,
and the remaining check vectorizes β it is not slower than the chain it
replaces. Dense layers never load sh_* tensors, so those kinds are checked on
sparse layers only (a literal all-layers check would false-positive on every
real GLM load). Test suite: kv_b miss Β· each allowlist kind Β· multi-kind
bounded output (β€8 lines) Β· all-pass silence Β· Metal-off silence Β· MTP
exclusion Β· dense-exemption Β· predicate truth table Β· mixed dense/sparse
counts Β· per-bit mask membership.
Observations for maintainers (nothing here is changed by this PR):
make metal-testdoes not compile on current dev in eitherCOLI_METAL_RESSETstate:quant.h:483fp8_nblk(returnsint64_t)collides with
tests/test_backend_metal.mm:99's localstatic int fp8_nblk. Pre-exists this branch (verified at the untouched base). Happyto send the trivial fix separately.
metal_fused_fmt_okcomment block said "fmt 1/2/4" while the predicateincludes
fmt==3; the consolidated comment now just names the allowlist.attention_rowsgained the(S<=4 || g_metal_prefill)prefill scope whilelayer_forward_rowsdid not β the two gates have diverged on batch scope(flagging only; format conditions are what this PR unifies).
the notice's kind arrays must move together β the per-bit membership test
now enforces the mask half of that lockstep.