Skip to content

dasLLAMA: the fp32 batch GEMM tile, the Qwen3-ASR conv front across lanes, the Granite Rapids diagnosis - #4365

Merged
borisbat merged 6 commits into
masterfrom
bbatkin/xeon-asr-leg
Oct 25, 2026
Merged

borisbat merged 6 commits into
masterfrom
bbatkin/xeon-asr-leg

Conversation

@borisbat

@borisbat borisbat commented Oct 25, 2026 •

Copy link
Copy Markdown
Collaborator

Behavior change: the fp32 batch GEMM's outputs round in a 4x4 tile's accumulators - a batched tower GEMM no longer matches the per-token dot bit for bit (within 1e-5 relative of a double oracle); Q8 paths are untouched.

Why. The Granite Rapids board read the 35B prefill at 0.91x of llama.cpp and the Qwen3-ASR CPU row at 0.58x. The prefill loss is the DeltaNet scan on eight lanes, not the AMX path (the tune crowns move neither model end to end). The ASR loss was the audio towers' fp32 batch GEMM, a dot per output cell at two loads a FMA, plus the Qwen3-ASR conv front's im2col and erf GELU running in one lane. Separately, ast-grep 0.51 silently disabled the MCP grep_usage and outline tools, and lcpp_bench --prof printed an empty report.

What changes.

  • matmul_batch_core walks four-row by four-token tiles (f32_batch_tile, a [tuned] kernel the loop-hint tuner races) over an absolute lattice: the dispatch unit is a row tile, a token chunk is whole tiles, a 256 KB X block, so which kernel rounds an output depends on the output alone and any lane count lands the same bits. The tile serves every dot length; the row nest stays as the race's arm behind set_f32_batch_tile_min_n.
  • harness/f32_batch_race.das races the two constants and the tile against the nest in one process (the ledger entry carries its lines).
  • The Qwen3-ASR conv front's im2col is the shared threaded im2col_k3s2p1 (gemma4a and canary call it too) and its GELU the row-threaded gelu_erf_batch; the same bytes, more lanes.
  • The MCP tools pass --custom-languages allow when the ast-grep probe reports the warning.
  • --prof raises the log floor to info when the logger would filter its report.
  • Tests: test_matmul_batch compares against a plain double oracle under a NaN sentinel with an all_finite assert, holds the threaded walk to the one-lane walk bit for bit, and the row nest under the knob to the per-token matmul bit for bit (the tile at the shipped knob rounding apart is the control); the kitten seam bars widen to 2.5e-4 of peak (conv_post) and 1e-3 absolute (waveform) with a rotated-input control that must land outside both; the gemma4a Metal transcript compares word lists with a half-silenced-clip control; check_exceeds logs the control's distance.
  • Docs: ARCHITECTURE_CPU_KERNELS.md#f32-batch-tile-lattice, ARCHITECTURE_MEASUREMENT_RELEASE.md#board-rows-release-boundary (a board row lands at a release re-mint), ARCHITECTURE_MEASUREMENT_AUDIO.md#asr-cpu-references; REVIEW_MEASUREMENT.md's figure rule and re-mint rule (the between-release diff writes a dated ledger entry and names the followup row the release re-mint owes); REVIEW_MEDIA_CELLS.md admits a word-list compare where the flipped token is punctuation or casing.
  • Ledger: two dated entries (the Xeon diagnosis; the tile, the race lines and the three boards' re-measured rows); row 225 becomes row 239, row 240 carries the bf16 arithmetic, the decode attention and the conv front's remainder, row 238 points the kitten rows at the release re-mint, row 241 names the bench exe's missing per-rep spread.

Observable behavior.

  • Batch GEMM at the conv front's shapes (harness/f32_batch_race.das -- --reps 5, GFLOP/s, tile against nest): M5 1405 against 509 and 1493 against 525; Xeon 1064 against 86 and 999 against 85. X block on the Xeon: 1053 at 256 KB, 349 at 4 MB.
  • Qwen3-ASR gb1, das against llama-mtmd-cli, total / encode ms: Xeon 26609 / 13996 against 15555 / 7076 -> 15797 / 2917 against 21610 / 7304; Zen4 16875 / 7257 -> 12784 / 3071 against 15981 / 8051; Graviton4 16446 / 10522 -> 9209 / 3168 against 67958 / 64362.
  • The threaded batched mixer (test_qwen4exp) still equals the inline run bit for bit.
  • ast-grep 0.51: 11 red MCP tool tests -> 497 of 497, 0.42.1 unchanged.
  • No board row or site file changes: every re-measured row stays in the ledger until the release re-mint.

Where to look. matmul_batch_core and f32_batch_block in dasllama_math.das with ARCHITECTURE_CPU_KERNELS.md#f32-batch-tile-lattice; conv2d_s2 in dasllama_qwen3a.das and im2col_k3s2p1 in dasllama_tower.das; the two entries at the head of PERF_LEDGER.md.

Validation, claims, ledger

Validation

  • The three AWS boards (m8i Xeon 6975P-C, m7a EPYC 9R14, m8g Graviton4) minted their own dasllama-bench exe on the branch and ran the Qwen3-ASR, whisper and kitten CPU cells against their references; the walls are in the ledger entry with their commands, the record stores are not committed (the boards publish with the release re-mint, row 238). The bench exe prints each clip's best of its reps alone, so these cells carry no per-rep spread - a rig gap, followup_general.md row 241.
  • The Xeon stage split (asr_stage_probe, debug-jit, gb1): conv.mm 13293 -> 5719 -> 1446 ms, encode 15227 -> 7728 -> 2924 ms; the M5 encode 287 ms throughout (its blocks on the Metal seat).
  • Fat exe (daspkg release --fat arm-i8mm from this tree, the 1B Q8_0 at -r 2): sanity: frozen fixture - 8/8 opening tokens match ", in a small village nestled in the"; sanity (tg128): warmup argmax 315 " of" (logit 7.824408); the run printed no tune gate: line.
  • dasLLAMA on the M5: model-free suite; --area audio (11 files, the only red test_vulkan_tower_kernels.das, the M5's Vulkan class - no device); --suite image --arm mtower under DASLLAMA_PARITY_FULL=1 (52 passed, 0 failed - gemma4a E2B and E4B with the half-silenced control); test_matmul_batch 13/13; the kitten and kokoro oracle cells with the rotated-input control; test_qwen4exp's mixer cell.
  • Codex review rounds 1-4 folded, round 4 on the final diff clean.
  • Negative controls run by hand: the nest's dot length mutated reds the nest-arm cell; the tile's store swapped reds the 100x13 and 1152x480 cells; the old seam bars against the shipped code red (23 conv_post and 1057 waveform elements outside).
  • Preflight --full ran once on this tip (the fast tier red on three long lines, wrapped; the lanes then ran targeted: lint, docs, tests-cpp, tests-interp, tests-jit, tests-aot, utils-tests all green); the one red is this box's environment, homebrew Sphinx without sphinx_sitemap, the same as PR 4364's.
  • Arch citations: f32_batch_block cites #f32-batch-tile-lattice; f32_dot_rows cites #kernel-visibility-lifted-workers; f32_batch_tile and matmul_batch_core carry none (the lattice section names them).

Claims - stated, not tested

  • The tile's error against a double reference is the same class as the dot's (2e-6 against 3e-6 at n 1152, 6e-6 against 4e-6 at n 4320), which is why the parity bars moved rather than the kernel.
  • The branches the tdd audit found untested and left so: the 256 KB X block's value (only a value pin could catch a change; the race is its instrument), lcpp_bench --prof through main, outline's file-plus-glob command (no caller passes both), the C++ scans' --custom-languages flag (ast-grep 0.51 answers the same either way).

Not done

  • The bf16 arithmetic family (vdpbf16ps, bfdot, the AMX bf16 tile over bf16 planes with an f32-to-bf16 activation panel) that the reference's encoders run, the conv front's first layer and GELU, the Qwen3-ASR decoder's attention over the ~3000 audio tokens: row 240, the next leg, on the same three boxes (stopped, not terminated).
  • The DeltaNet scan's one-head-per-lane parallelism: row 239.
  • matmul_bf16_batch_at keeps the old nest; it moves with the bf16 family.
  • The kitten rows and the six held entries: the release re-mint, row 238.
  • Three older REVIEW_MEASUREMENT.md rules (the changed board cell's before/after rows, the re-minted row, the new cell's mint) still write rows between releases; they route to the release re-mint in a separate rule pass.

borisbat and others added 4 commits October 24, 2026 17:24
ast-grep 0.51.0 ignores the native custom-language libraries sgconfig.yml
names unless every invocation carries --custom-languages allow, and the
daslang grammar is such a library, so grep_usage and outline (and the C++
scans beside them) answered nothing on a box with that version. The
version probe already reads the binary's "custom languages are ignored"
warning; it now records it, and every run/scan carries the allow flag when
the binary printed it. Older binaries, which reject the flag, see no
change: utils/mcp/test_tools.das 497/497 on both 0.42.1 and 0.51.0.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The forward_profile report is an info-level log, which the default log
filter drops, so a --prof run printed the window header and nothing under
it. The flag now raises DAS_LOG_LEVEL to info when the environment leaves
it unset (the builtin to_log reads the variable live), and the refusal on
a rail-less build names -jit-no-cache: the JIT DLL cache serves the
compile that erased the markers.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
matmul_batch_core computed every output as its own dot, two loads a FMA,
and the audio towers' conv fronts and the TTS linears run every fp32 GEMM
through it: Qwen3-ASR's conv2d im2col GEMM was 13.3 s of a 15.2 s encode
on the Granite Rapids box. The core now walks the row range by token
blocks that keep the X rows in L2, and inside them f32_batch_tile - four
weight rows by four tokens, sixteen accumulators in the kernel's own lanes,
eight loads a sixteen FMAs - with the row and token tails through dot. The
tile is a [tuned] kernel (fallback vec8) the loop-hint tuner races with the
other twenty-five; its outputs agree with the per-token dot to the lanes'
rounding, so test_matmul_batch holds the fp32 path to 1e-5 relative and the
Q8 path bit for bit as before, with a tails shape and a many-block shape
added.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
conv2d_s2 filled its im2col columns in one lane and ran the erf GELU
over every output in one lane - two million erf evaluations a chunk -
while the GEMM between them ran on all of them; on the Granite Rapids box
that was most of a 7.7 s encode of the gb1 clip. The im2col splits its
output rows across lanes (pure copies, the same bytes under any split)
and the GELU takes gelu_erf_batch, the row-threaded twin of the same erf,
so every element rounds as before. The encode reads 2.9 s on that box
after it (asr_stage_probe, debug-jit).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 25, 2026 00:49

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.

🔵 Needs a closer look

It changes a core fp32 GEMM to a non-bit-exact tile whose rounding can flip near-tie MoE prefill routing and shift multiple audio models' outputs, a numeric/behavioral change across performance-critical paths that warrants human verification despite no concrete defect being found.

No open issues 🎉

What changed in this PR

This PR advances dasLLAMA's CPU inference performance work on three fronts. It replaces the fp32 batch (prefill) GEMM's dot-per-cell row nest in matmul_batch_core with a 4×4 weight-row-by-token register tile (f32_batch_tile, a [tuned] loop-hint kernel) that dispatches over an absolute lattice, so any lane/row/token split produces identical bits while the tile accumulates each dot in its own SIMD lanes (a deliberate, documented departure from the per-token dot, held within 1e-5 of a double oracle). It also unifies the Qwen3-ASR/gemma4a/canary conv-front im2col into a shared threaded im2col_k3s2p1 and routes GELU through the row-threaded gelu_erf_batch. Supporting changes fix two tooling regressions (ast-grep 0.51 custom-languages flag for the MCP C++/daslang scanners; lcpp_bench --prof raising the log floor) and record the Granite Rapids diagnosis across ledgers and architecture docs.

Changes:

  • New fp32 batch GEMM tile + absolute-lattice dispatch, with its race harness, tuner wiring, and bit-for-bit/oracle tests.
  • Shared im2col_k3s2p1 across the three audio conv fronts; MoE-router parity comment updated to reflect tile rounding.
  • Tooling fixes (ast-grep --custom-languages allow, --prof log floor) plus extensive ledger/architecture/followup documentation.
File Description
dasllama/​dasllama_math.das Core fp32 batch GEMM rewritten as f32_batch_tile/f32_batch_block/f32_dot_rows over an absolute 4×4 lattice with X-block and min-n knobs.
dasllama/​dasllama_tower.das New shared threaded im2col_k3s2p1; adds gelu_erf_batch usage context.
dasllama/​dasllama_qwen3a.das, dasllama_gemma4a.das, dasllama_canary.das Replace inlined im2col loops with im2col_k3s2p1; qwen3a also switches to gelu_erf_batch.
dasllama/​dasllama_common.das MoE-router prefill comment notes tile rounding can part a near-tie top-k.
harness/​f32_batch_race.das New one-process race of the two constants and tile-vs-nest.
harness/​tune_kernels.das Registers f32_batch_tile grid + bench; bumps TUNED_KERNEL_COUNT to 26.
tests/​test_matmul_batch.das Oracle/lanes/nest-arm tests with NaN sentinels and poison controls.
tests/​_compares.das, _model_tier.das, _tts_parity.das, test_whisper.das, test_model_image.das transcript_words word-list compares, widened seam bars with rotated-input controls, knob save/restore, check_exceeds logging.
benchmarks/​lcpp_bench.das --prof raises DAS_LOG_LEVEL to info so the report prints; clearer rail-missing hint.
utils/​mcp/​tools/​*.das ast-grep --custom-languages allow threaded through grep_usage/outline/cpp scanners.
*.md (ARCHITECTURE_, REVIEW_, PERF_LEDGER, followup_general, CLAUDE) New release-boundary/CPU-reference sections, ledger entries, and followup rows 239/240.

Review effort: Balanced

borisbat and others added 2 commits October 24, 2026 18:13
The ledger carries the diagnosis (the DeltaNet scan and the f32 conv front,
not the AMX crowns), the tile race's lines, the three boxes' re-measured ASR,
whisper and kitten rows with their provenance, and the reference recipes
behind them. ARCHITECTURE_MEASUREMENT_RELEASE.md states where a board row is
published - at a release re-mint, never from a branch - and
ARCHITECTURE_MEASUREMENT_AUDIO.md the ASR CPU references.
REVIEW_MEASUREMENT.md's figure rule narrows to the pick the diff lands and its
re-mint rule routes the board row to the release re-mint; row 241 names the
bench exe's missing per-rep spread.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The gemma4a Metal cell and the whisper word-level cell read both transcripts
through transcript_words (each_word over the text), so punctuation and spacing
a backend renders apart no longer red a cell whose words agree; the gemma4a
cell's control is the GPU route fed a half-silenced clip reading other words.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@borisbat
borisbat force-pushed the bbatkin/xeon-asr-leg branch from 3c0bd02 to d48a31f Compare October 25, 2026 01:14
Copilot AI balanced review requested due to automatic review settings October 25, 2026 01:14

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.

🔵 Needs a closer look

It rewrites a core fp32 prefill GEMM with deliberate floating-point behavior changes (batch output no longer bit-identical to per-token matmul, and MoE prefill routing can diverge from decode on near-ties), affecting multiple models, which needs human numerical/perf verification.

No open issues 🎉

Review effort: Balanced

@borisbat
borisbat merged commit 2d7d99f into master Oct 25, 2026
29 checks passed
@borisbat
borisbat deleted the bbatkin/xeon-asr-leg branch October 25, 2026 01:48
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.

2 participants