Round 35 Track C: MTP Draft Kernels (mtp.metal / mtp_chain.metal) & router.metal Optimization Report #
- Agent:
argon-r35-mtp-and-router-opt(gemini-4-argon) - Patch:
/tmp/argon_r35_mtp/argon_r35_mtp.patch(verified clean withgit -C vendor/strata apply --check) - Owned files:
metal/src/engine/msl/mtp.metalmetal/src/engine/msl/mtp_chain.metalmetal/kernels/router.metalmetal/tests/route_parity.cpp
1. Summary of Changes & Root Causes #
A. metal/src/engine/msl/mtp.metal (mtp_rms_xprep, mtp_combine_gnorm, mtp_xprep) #
mtp_rms_xprep:- Pre-offset + unrolled reduction (
a.n == 2560anda.n == 10240): Pre-offsetxrp = xr + tidand specialized the two MTP widths (2560formtp.pre_fc_norm_embedding.weight, 10 steps of 256;10240formtp.pre_fc_norm_hidden.weight, 40 steps of 256) with#pragma clang loop unroll(full)so all loads use constant immediate offsets[256 * i]. - Prepared-output loop specialization & division elimination:
- For
a.n == 2560 && a.xp_n == 2560,a.n / 16 == 160 < 256, so each threadtid < 160executes at most 1 iteration (if (tid < 160)), eliminating the loop back-edge and all three runtime integer divisions (a.n / a.xp_n,i / per,i % per). - For
a.n == 10240 && a.xp_n == 2560,per == 160anda.n / a.xp_n == 4are compile-time constants (q = i / 160u, k = i - q * 160u), replacing expensive 32-bit runtimeudiv/uremsequences on AGX with a single constant magic-multiply.
- For
- Pre-offset + unrolled reduction (
mtp_combine_gnorm:- Pre-offset
rp = r + tid,o += tid, andw += tidin then == 2560fast path so all 40 unrolled loads/stores (hp,ep,rp,w,o) use constant immediate offsets[256 * i].
- Pre-offset
mtp_xprep:- Specialized
per == 160u ? gid / 160u : gid / perandi = gid - t * perto avoid runtime integer division/modulo forn == 2560.
- Specialized
B. metal/src/engine/msl/mtp_chain.metal (mtp_embed, mtp_slop_pen, mtp_pick) #
mtp_embed<F>(-15.8% to -31.6% across all 13 GGUF formats):- Previously,
mtp_embed<F>calledX<F>::load(posv, posv, u, ps, xsum)on every unit to read 16 or 32floatindices from global bufferposv(0.0f .. 2559.0f), computed unusedxsum[0..1], convertedint(ps[j])back toint, and performed 16 or 32 dynamic-index scalar scatter storesy[pos] = ...plus runtime-indexedkind_scale<F>(pos). - Because unit base offset
p0(andp1forUNIT == 32) is always a multiple of 16 (16 * u,32 * u, or the K-quant block/subblock formula),(p0 + j) & 7 == j & 7,(p0 + j) & 3 == j & 3, and(p0 + j) & 1 == j & 1. - Replaced
X<F>::load(posv, ...)and scalar scatter stores with direct analytical unit offsetp0/p1, compile-time constantkind_scale<F>(j), and coalescedfloat4stores toy + p0(andy + p1), plusif (U <= 256) { if (int(tid) < U) ... }loop elimination forn == 2560.
- Previously,
mtp_slop_pen(-31.0% to -96.7%):- Fast-exit when
a.j == 0 && !any_knob(mode=0): Immediately writesstate[0] = a.stateontid == 0and returns without barriers or table scans (24.90 us -> 0.81 us, -96.7%). - Parallel token classification (
kNewline/kBlank): Bothslop4::kNewline(200 tokens) andslop4::kBlank(222 tokens) fit within the 256-thread threadgroup (tid < 200,tid < 222), replacing 6 serial 422-element constant-memory linear scans ontid == 0with a single parallel compare across the threadgroup. - Zero
threadstack frame (mtp_slop_penalties): Emits penalties directly intothreadgroup int ids[80]andthreadgroup float vals[80]using file-scopeconstant int32_ttables and#pragma clang loop unroll(full)instead of allocatingslop4::Pen pen[80](640B) + 20 local arrays on the per-thread DRAM stack. - Parallel
inverse[]lookup &lg[ids[tid]]hold/removed evaluation: Replaced serialtid == 0global memory loops overinverse[pen[i].id]andlg[ids[i]]with parallel evaluation acrosstid < count.
- Fast-exit when
C. metal/kernels/router.metal & metal/tests/route_parity.cpp #
route_bodyinrouter.metal(-0.2% to -2.1%):- Unrolled the per-rank argmax and butterfly reduction loops and replaced
if ((ex & 31) == lane) v[ex / 32] = -INFINITY;(dynamic register-array indexing that forcedv[16]to spill to stack memory) with#pragma clang loop unroll(full) for (int i = 0; i < 16; ++i) v[i] = (ex == lane + 32 * i) ? -INFINITY : v[i];sov[0..15]remains in scalar registers while keeping the initial threadgroup barrier that synchronizesX<F>::loadacross simdgroups.
- Unrolled the per-rank argmax and butterfly reduction loops and replaced
route_parity.cpp:- Added
--mtp-ab BASE_MTP_METALfor paired bitwise parity and timing ofmtp_rms_xprep(n=2560andn=10240),mtp_combine_gnorm, andmtp_xprep, and aligned buffer placement during paired timing in--router-ab.
- Added
2. Quiet-GPU Benchmarks & Bitwise Parity Verification (Apple M5) #
A. route_parity --mtp-ab (mtp.metal) #
| Kernel | Shape | Baseline (us) | Candidate (us) | Ratio | Delta | Parity |
|---|---|---|---|---|---|---|
mtp_rms_xprep |
n=2560, nt=1 |
2.895 | 2.223 | 0.7676 | -23.24% | BITWISE PASS |
mtp_rms_xprep |
n=2560, nt=4 |
2.932 | 2.338 | 0.7945 | -20.55% | BITWISE PASS |
mtp_rms_xprep |
n=2560, nt=8 |
2.959 | 2.370 | 0.7981 | -20.19% | BITWISE PASS |
mtp_rms_xprep |
n=10240, nt=1 |
7.893 | 5.060 | 0.6390 | -36.10% | BITWISE PASS |
mtp_rms_xprep |
n=10240, nt=4 |
8.352 | 5.626 | 0.6745 | -32.55% | BITWISE PASS |
mtp_rms_xprep |
n=10240, nt=8 |
8.398 | 5.801 | 0.6879 | -31.21% | BITWISE PASS |
mtp_combine_gnorm |
hc=4, nt=1 |
2.901 | 2.888 | 0.9962 | -0.38% | BITWISE PASS |
mtp_combine_gnorm |
hc=4, nt=4 |
3.097 | 3.067 | 0.9896 | -1.04% | BITWISE PASS |
mtp_combine_gnorm |
hc=4, nt=8 |
3.804 | 3.798 | 0.9995 | -0.05% | BITWISE PASS |
mtp_xprep |
rows=8, n=2560 |
2.154 | 2.133 | 0.9899 | -1.01% | BITWISE PASS |
In comb_fold_parity 0 mtp bench (cold 16-copy DRAM walk of the full hidden branch mtp_rms_xprep + fc_hidden GEMV + mtp_combine_gnorm):
- All
n = 1..8passALL PASS. - Cold batched hidden branch time improved from
94.1 us -> 65.5 us(n=1),102.0 us -> 96.3 us(n=2),156.3 us -> 150.7 us(n=3),190.4 us -> 184.9 us(n=4),366.6 us -> 361.2 us(n=8).
B. sample_parity --mtp (mtp_chain.metal) #
| Kernel | Config | Baseline (us) | Candidate (us) | Ratio | Delta | Parity |
|---|---|---|---|---|---|---|
mtp_pick |
nv=40525 |
3.501 | 3.508 | 0.9820 | -1.80% | BITWISE PASS |
mtp_pick |
nv=248320 |
15.084 | 15.092 | 0.9996 | -0.04% | BITWISE PASS |
mtp_slop_pen |
nv=40525, mode=0 |
24.895 | 0.876 | 0.0351 | -96.49% | BITWISE PASS |
mtp_slop_pen |
nv=40525, mode=1 |
34.803 | 10.254 | 0.2944 | -70.56% | BITWISE PASS |
mtp_slop_pen |
nv=40525, mode=2 |
41.072 | 14.822 | 0.3607 | -63.93% | BITWISE PASS |
mtp_slop_pen |
nv=248320, mode=0 |
24.904 | 0.814 | 0.0327 | -96.73% | BITWISE PASS |
mtp_slop_pen |
nv=248320, mode=1 |
57.137 | 31.043 | 0.5435 | -45.65% | BITWISE PASS |
mtp_slop_pen |
nv=248320, mode=2 |
87.128 | 60.116 | 0.6903 | -30.97% | BITWISE PASS |
mtp_embed |
format=30 (BF16) |
1.618 | 1.112 | 0.6842 | -31.58% | BITWISE PASS |
mtp_embed |
format=8 (Q8_0) |
1.616 | 1.169 | 0.7225 | -27.75% | BITWISE PASS |
mtp_embed |
format=2 (Q4_0) |
1.592 | 1.177 | 0.7409 | -25.91% | BITWISE PASS |
mtp_embed |
format=6 (Q5_0) |
1.614 | 1.264 | 0.7822 | -21.78% | BITWISE PASS |
mtp_embed |
format=20 (IQ4_NL) |
1.714 | 1.289 | 0.7530 | -24.70% | BITWISE PASS |
mtp_embed |
format=23 (IQ4_XS) |
1.599 | 1.300 | 0.8144 | -18.56% | BITWISE PASS |
mtp_embed |
format=42 (Q2_0) |
1.639 | 1.182 | 0.7229 | -27.71% | BITWISE PASS |
mtp_embed |
format=21 (IQ3_S) |
1.730 | 1.451 | 0.8361 | -16.39% | BITWISE PASS |
mtp_embed |
format=11 (Q3_K) |
1.971 | 1.437 | 0.7294 | -27.06% | BITWISE PASS |
mtp_embed |
format=12 (Q4_K) |
1.923 | 1.440 | 0.7505 | -24.95% | BITWISE PASS |
mtp_embed |
format=13 (Q5_K) |
1.941 | 1.529 | 0.7855 | -21.45% | BITWISE PASS |
mtp_embed |
format=14 (Q6_K) |
1.719 | 1.346 | 0.7830 | -21.70% | BITWISE PASS |
mtp_embed |
format=18 (IQ3_XXS) |
1.622 | 1.366 | 0.8423 | -15.77% | BITWISE PASS |
C. route_parity --router-ab (router.metal) #
type=30 (BF16):nt=1..8, split=0/1allPASSwith median ratios0.9874 .. 0.9977(-0.2% to -1.3%).type=8 (Q8_0):nt=1..8, split=0/1allPASSwith median ratios0.9787 .. 0.9981(-0.2% to -2.1%).- Standalone
route_parity(400 windows, K=512 + K=256 MoCE) andprompt_parity:ALL PASS.