Round 35 blitz: Wr/Wr2 router dq reuse (-47 dispatches/chunk), gemm_f16_16_96_2 Walbe (-13.6%), M%128<=64 64_128_4 (1.14x @ M=306), hc_up_tile row-tail trim (-2.5% @ T=289), mtp_rms_xprep (-37.5%) & mtp_embed (-21.8%) & mtp_slop_pen (-96.5%) -> 629.05 tok/s GPU prefill, 25.26 tok/s @10.2K decode, mtp_ms 192.17/355.15ms
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293# 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 with `git -C vendor/strata apply --check`)- **Owned files**: - `metal/src/engine/msl/mtp.metal` - `metal/src/engine/msl/mtp_chain.metal` - `metal/kernels/router.metal` - `metal/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`)1. **`mtp_rms_xprep`**: - **Pre-offset + unrolled reduction (`a.n == 2560` and `a.n == 10240`)**: Pre-offset `xrp = xr + tid` and specialized the two MTP widths (`2560` for `mtp.pre_fc_norm_embedding.weight`, 10 steps of 256; `10240` for `mtp.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 thread `tid < 160` executes 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 == 160` and `a.n / a.xp_n == 4` are compile-time constants (`q = i / 160u, k = i - q * 160u`), replacing expensive 32-bit runtime `udiv`/`urem` sequences on AGX with a single constant magic-multiply.2. **`mtp_combine_gnorm`**: - Pre-offset `rp = r + tid`, `o += tid`, and `w += tid` in the `n == 2560` fast path so all 40 unrolled loads/stores (`hp`, `ep`, `rp`, `w`, `o`) use constant immediate offsets `[256 * i]`.3. **`mtp_xprep`**: - Specialized `per == 160u ? gid / 160u : gid / per` and `i = gid - t * per` to avoid runtime integer division/modulo for `n == 2560`.
### B. `metal/src/engine/msl/mtp_chain.metal` (`mtp_embed`, `mtp_slop_pen`, `mtp_pick`)1. **`mtp_embed<F>` (-15.8% to -31.6% across all 13 GGUF formats)**: - Previously, `mtp_embed<F>` called `X<F>::load(posv, posv, u, ps, xsum)` on every unit to read 16 or 32 `float` indices from global buffer `posv` (`0.0f .. 2559.0f`), computed unused `xsum[0..1]`, converted `int(ps[j])` back to `int`, and performed 16 or 32 dynamic-index scalar scatter stores `y[pos] = ...` plus runtime-indexed `kind_scale<F>(pos)`. - Because unit base offset `p0` (and `p1` for `UNIT == 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 offset `p0`/`p1`, compile-time constant `kind_scale<F>(j)`, and coalesced `float4` stores to `y + p0` (and `y + p1`), plus `if (U <= 256) { if (int(tid) < U) ... }` loop elimination for `n == 2560`.2. **`mtp_slop_pen` (-31.0% to -96.7%)**: - **Fast-exit when `a.j == 0 && !any_knob` (`mode=0`)**: Immediately writes `state[0] = a.state` on `tid == 0` and returns without barriers or table scans (`24.90 us -> 0.81 us`, **-96.7%**). - **Parallel token classification (`kNewline` / `kBlank`)**: Both `slop4::kNewline` (200 tokens) and `slop4::kBlank` (222 tokens) fit within the 256-thread threadgroup (`tid < 200`, `tid < 222`), replacing 6 serial 422-element constant-memory linear scans on `tid == 0` with a single parallel compare across the threadgroup. - **Zero `thread` stack frame (`mtp_slop_penalties`)**: Emits penalties directly into `threadgroup int ids[80]` and `threadgroup float vals[80]` using file-scope `constant int32_t` tables and `#pragma clang loop unroll(full)` instead of allocating `slop4::Pen pen[80]` (640B) + 20 local arrays on the per-thread DRAM stack. - **Parallel `inverse[]` lookup & `lg[ids[tid]]` hold/removed evaluation**: Replaced serial `tid == 0` global memory loops over `inverse[pen[i].id]` and `lg[ids[i]]` with parallel evaluation across `tid < count`.
### C. `metal/kernels/router.metal` & `metal/tests/route_parity.cpp`1. **`route_body` in `router.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 forced `v[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];` so `v[0..15]` remains in scalar registers while keeping the initial threadgroup barrier that synchronizes `X<F>::load` across simdgroups.2. **`route_parity.cpp`**: - Added `--mtp-ab BASE_MTP_METAL` for paired bitwise parity and timing of `mtp_rms_xprep` (`n=2560` and `n=10240`), `mtp_combine_gnorm`, and `mtp_xprep`, and aligned buffer placement during paired timing in `--router-ab`.
---
## 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..8` pass `ALL 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/1` all `PASS` with median ratios `0.9874 .. 0.9977` (**-0.2% to -1.3%**).- `type=8 (Q8_0)`: `nt=1..8, split=0/1` all `PASS` with median ratios `0.9787 .. 0.9981` (**-0.2% to -2.1%**).- Standalone `route_parity` (400 windows, K=512 + K=256 MoCE) and `prompt_parity`: `ALL PASS`.12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364# opus r35 track A: router dq reuse + gemm_f16 tiles (TN=96, M%128 padding), SLC swizzle (rejected)
patch: `/tmp/opus_r35_dense/opus_r35_dense.patch` (`git -C vendor/strata apply --check` = OK)scratch tree: `/tmp/opus_r35_dense/strata`; test binary `/tmp/opus_r35_dense/bin/dense_mm_parity` (linked against`vendor/strata/build-metal/libmtl_rt.a`, run with `STRATA_METAL_KERNELS=/tmp/opus_r35_dense/strata/metal/kernels`).Files touched: `metal/kernels/dense_mm.metal` (+1 instantiation), `window.cpp` (gemm selector 3458-3470, router 3668-3687only), `metal/tests/dense_mm_parity.cpp`. No full-model runs, :8080 untouched.
## 1. router dequant reuse (window.cpp prompt_chunk)Under `stream_ && pred_on && !p.ids3.ptr` the router of layer l+1 that layer l dequantizes for the read-ahead is reused aslayer l+1's own router: `w_r` / `w_r2` ping-pong between `p.Wr` and `p.Wr2` by layer parity, `dq(tr, w_r)` only at l == 0(or when reuse is off). PRED2 (`p.ids3`) overwrites Wr2 with l+2, so it keeps the old path. Same tensor, same `dq`call, same perm → bitwise-identical weights. Hazards: the dq into `w_r2` at layer l targets the buffer layer l-1'srouter GEMM read (several barriers back, in hc_read); `w_r` / `w_r2` are always distinct within a layer. `run_graph_seg`runs the segments in order with no replay, so layer l+1 always sees layer l's dq.Saves 47 `dq_f16` dispatches (512x2560) per streamed chunk; measured dq cost per call (`--dq-ab`, 2560x512): 17-20 us(quantized types), 30 us BF16, 57 us F32 → 0.8-2.7 ms of dq work per chunk (they overlap other dqs, so wall saving islower). Compiles (`clang++ -fsyntax-only` with the build's flags); not measured end-to-end (no full-model runs allowed).**Needs a prompt_parity / streamed-prompt logits check before merging** (I couldn't run the model).
## 2. gemm_f16 tiles (dense_mm_parity --tiles / --ab, all memcmp == 0 vs 128_64_4)Methodology: the GPU was shared (other agents/server: Device Utilization swinging 0-84%), so medians of 5 rounds wereuseless (2-4x swings). `--tiles` now reports the min over interleaved rounds (`DENSE_ROUNDS`, default 5/9) and can berestricted with `DENSE_TILES=a,b`; repeated focused runs agreed within ~1-3%.
### Walbe (N=96, K=2560, 36 calls): new `gemm_f16_16_96_2`, used at M >= 640| M | 16_64_2 | 64_32_2 | 16_96_2 | 32_96_2 | 64_96_4 ||---|---|---|---|---|---|| 512 | 29.6 | 31.5 | 30.2 | 39.7 | 40.1 || 640 | 33.0 | 34.3 | **30.9** | 41.0 | 40.5 || 768 | 39.8 | 37.4 (cur) | **33.0** | 41.4 | 46.4 || 1024 | 55.4 | 42.9 (cur) | **41.5** | 47.5 | 48.0 || 1983 | 116.8 | 103.8 (cur) | **91.6** | 92.0 | 94.1 || 2048 | 122.8 | 98.3 (cur) | **94.3** | 96.0 | 95.2 |64_96_2 and 128_96_4 are 2-3x slower (not shipped). -3..-12% on the call, but only ~0.3-0.4 ms/chunk total.
### big-N GEMMs (Wqkv, Wz, Wout, Wq, Wpv, Wshd; the `else` branch), M < 1536128_64_4 loses whenever M % 128 is 1..64 (a 128-row tail tile that is up to half padding); 64_128_4 ties it elsewhere.New rule: `M < 1536 && (M % 128 == 0 || M % 128 > 64) ? 128_64_4 : 64_128_4`. Focused runs (us/call, 128_64_4 → 64_128_4):- M=192: Wqkv 920 → 787, Wz 708 → 552, Wout 683 → 563, Wq 1135 → 942, Wshd 64 → 51- M=306: Wqkv 1686 → 1412, Wz 970 → 847, Wout 1013 → 888, Wq 2044 → 1710, Wshd 92 → 80- M=448: Wqkv 2250 → 1990, Wout 1353 → 1233, Wq 3240 → 2862; M=576/700: -7..-9%; M=1300/1450: -4..-6%- ties (rule keeps 128_64_4): M=256, 384, 640, 896, 1000, 1100`--ab` chunk totals (sum of isolated GEMMs x calls, r34 → new): M=150 1.17x, 192 1.14x, 306 1.13-1.14x, 448 1.09x,520 1.07x, 700/800 1.05x, 1050 1.03-1.05x, 1200 1.04x; 200/330/640/1000/1400/1600/1983/2048 1.00x (unchanged tiles barWalbe). So the win is on the tail / short chunks (a 306-token prompt: ~22 ms of dense GEMM per chunk), not 2K chunks.
### W512 (N=512, 95 calls), Wshgu (640), Wk (1152/1664): no changeThe current picks (32_64_2 <384, 32_128_2 above; Nn<=1664 rule) are within ~1-3% of the best everywhere, e.g. W512M=1983: 32_128_2 402-404, 64_128_4 403-404, 64_64_2 405, 128_64_4 416; M=1024: 32_128_2 197, 64_64_2 197, 64_128_4 210.Only W512 M=768 (128_64_4 -4%) and Wshgu 64_64_2 at M~1024 (-2.5%) showed anything, inside run-to-run noise. Not shipped.
### SLC swizzle (rejected, not in the patch)Tried `gemm_f16_sw<TM,TN,SG,G>`: threadgroup ids remapped so G column tiles of B run down all row tiles of A (same tiles,same bits, memcmp 0). 128_64_4_sw{2,4,8} and 64_128_4_sw{2,4,8} vs plain on Wqkv/Wz/Wout/Wq at M = 256..2048: never areproducible win; 128_64_4_sw2 ≈ plain (±1%), sw4/sw8 up to +10-60% slower, 64_128_4_sw* often 2-3x slower (e.g. WqkvM=1983: 7827 plain vs 28523 sw2 / 23062 sw4). The default dispatch order already keeps B cached well enough; the swizzlekills the row-tile reuse of A between concurrently running threadgroups.
## verification- `dense_mm_parity` (parity): ALL PASS- `--tiles M` (all 12 tiles incl. 16_96_2 x 13 shapes) at M = 100, 256, 306, 640, 1000, 1983, 2048: ALL BITWISE- `--ab M` at 150..2048: ALL BITWISE- window.cpp: compiles (-fsyntax-only, build flags). Router reuse not run end-to-end (forbidden here).12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091# round 35 track b — experiment contract
owner: prompt.metal owns fused tile execution; hc_read owns dispatch selection. no shared interfaces or layouts change. all edits and builds stay in /tmp/sol_r35_prompt/strata.
question: do sg=2 hc-up shapes and single-descriptor row tails improve m5 latency without changing a single output bit?baseline: current vendor/strata prompt.metal, archived under /tmp/sol_r35_prompt/baseline.evaluator: comb_fold_parity, production mixed-unbound hc-up, short standalone invocations only. initial sweep selects shapes; interleaved confirmation must show a repeatable improvement before any dispatch change. row-tail comparisons use baseline-vs-candidate ab mode, including full tiles. any bit mismatch rejects the variant; noisy or regressed ranges keep the baseline. no full model runs, no :8080 access, no subagents. per invocation <10s, cold compile timeout 20s.
planned evidence: hc-up parity at 256,1983,2048; shape timings at 192,306,384,512,1024,1983,2048; epi sweep, aggregate 1024 and ab 289,306,512,1024,1983,2048. gpu tests serialized to avoid self-contention.
## decision and integration status
**candidate for parent's quiet-gpu confirmation**, not an unconditional performance sign-off. patch: `/tmp/sol_r35_prompt/sol_r35_prompt.patch`; `git -C vendor/strata apply --check` and scratch `git diff --check` both exit 0. no tracked vendor file was edited.
retain only hc-up register row trimming: one inline tile helper, original grid stride retained, tail descriptor TM=16 when <=16 rows remain, TM=32 for larger register tiles when <=32 rows remain. non-register fallback retains original descriptors. arithmetic and output guards stay unchanged. the extra shared-memory pointer is only used by the existing non-register path. no window.cpp changes: existing 32_128_4 wins every tested t range.
rejected sg=2 shapes remain **test-only** instantiations, avoiding unused production pipelines. evaluator now checks/times all 10 register shapes, both mixed bindings, using the correct sg*32 threads. its a/b hc-down selector is repaired to match production at t<=384 (old evaluator wrongly used 32_128_4 there). this repair is retained even though hc-down optimization is rejected.
hc-down/sw-up row trims are not in the patch: they passed parity, but repeated quiet production-tile gains were not established before GPU sharing resumed. full prototype is archived as `candidate-all-rows-final.metal`.
## sg=2 sweep (negative result)
all shapes pass 100% bitwise at t256 and every benchmark t, including 1983/2048. exploratory metric is minimum of 3 x 48 dispatches after warmup. values below are ms/48 with **mixed unbound**:
| t | baseline 32_128_4 | 32_128_2 | 64_128_2 | 32_64_2 | 64_64_2 | 16_64_2 ||---:|---:|---:|---:|---:|---:|---:|| 192 | 5.459 | 6.628 | 12.043 | 5.930 | 7.507 | 7.310 || 306 | 8.877 | 10.000 | 20.914 | 9.584 | 11.920 | 11.509 || 384 | 10.698 | 12.103 | 25.028 | 11.746 | 14.062 | 14.411 || 512 | 14.124 | 15.819 | 33.152 | 15.557 | 18.292 | 19.058 || 1024 | 27.476 | 30.596 | 66.110 | 30.807 | 35.487 | 38.060 || 1983 | 52.899 | 59.167 | 127.859 | 59.914 | 69.661 | 75.671 || 2048 | 57.123 | 62.854 | 131.707 | 62.581 | 74.387 | 80.271 |
all other sg4 register tiles also lose to 32_128_4; complete tables are in `sg2-sweep-T.log`. no confirmation runs spent on losing candidates.
## row-tail evidence
initial clean paired a/b (`rows-ab-T.log`), 12 alternating samples x 8 dispatches, same inputs and swapped output buffer placement:
| t | hc-up candidate/baseline ratio ||---:|---:||289|0.9761 [0.9739,0.9780]||306|1.0010||512|0.9970||1024|0.9914||1983|1.0006||2048|1.0017|
independent t289 confirmations `rows-prod0-ab-289.log`, `rows-prod1-ab-289.log`: ratios **0.9747**, **0.9725**, medians 0.1804/0.1850 and 0.1822/0.1874 ms. these support ~2.5% hc-up savings at a one-row tail; t306 has 18 rows and does not take the branch, so no expected win there. t297 first confirmation 0.9677, later results contaminated. full-tile results near neutral, not a claimed win.
the initial full prototype had identical hc-up register execution to the final patch; final patch removes unrelated down/sw trims and disables fallback-path row trimming. all evidence snapshots retained. parent's requested exclusive final source confirmation remains necessary.
noise: `rows-prod1-*`, `rows-prod2-*` and final logs show changed shared GPU conditions: t306 hc-up ~0.19ms -> ~1ms, ratios with very wide intervals. parent confirmed tracks a/c were concurrently running gpu sweeps and explicitly took ownership of exclusive confirmation. do not interpret these as performance evidence. no attempts to control or access the live server.
## correctness and execution record
final evaluator compiled using:
```textclang++ -std=c++20 -O2 -I/tmp/sol_r35_prompt/strata/metal/src -I/tmp/sol_r35_prompt/strata/third_party/ggml /tmp/sol_r35_prompt/strata/metal/tests/comb_fold_parity.cpp vendor/strata/build-metal/libmtl_rt.a -framework Metal -framework Foundation -o /tmp/sol_r35_prompt/comb_fold_parity```
all executions set `STRATA_METAL_KERNELS=/tmp/sol_r35_prompt/strata/metal/kernels`, no math overrides. source remains untouched throughout each invocation. final logs:
- `final-main1024.log`: exit 0, 7.880s (all output comparisons PASS).- `final-epi.log`: exit 0, 1.970s (all output comparisons PASS).- `final-hcup256.log`: exit 0, 2.284s (all output comparisons PASS).- `final-hcup1983.log`: exit 0, 0.371s (all output comparisons PASS).- `final-hcup2048.log`: exit 0, 0.418s (all output comparisons PASS).- `final-ab289.log`: exit 0, 1.462s (all output comparisons PASS).- `final-ab306.log`: exit 0, 1.107s (all output comparisons PASS).- `final-ab512.log`: exit 0, 2.588s (all output comparisons PASS).- `final-ab1024.log`: exit 0, 9.289s (all output comparisons PASS).- `final-ab1983.log`: exit -15, 10.027s (timeout under shared GPU load; incomplete, not a pass).- `final-ab2048.log`: exit 0, 9.369s (all output comparisons PASS).
final 1024, 0 epi, hcup256/1983/2048 all PASS, as do final ab289/306/512/1024/2048. **final ab1983 timed out at the 10s deadline** before GEMM comparisons; prior same hc-up register candidate `rows-ab-1983.log` passed all comparisons in 3.10s. parent must finish this final-source check on the quiet GPU. this is not hidden as a pass. all earlier invocations completed under 8s; final loaded run ab1024 took 9.29s, ab2048 9.37s. no further GPU retries are running.
first baseline attempt used an incomplete baseline kernel directory and failed before comparisons; repaired baseline-hcup256-repaired.log passes. first row prototype compile failed because threadgroup memory must be declared in a kernel, not a non-qualified inline helper; moved allocation to wrapper and fixed one stale tg reference. `rows-epi-repaired.log` passes. original failed snapshots/logs preserved.
## reproducibility and review
- vendor head: `296b59d2f73c8f04a805df29c2fa97655adace78`; actual working-tree baseline archived and locally committed in scratch, not assumed identical to head.- `/tmp/sol_r35_prompt/baseline/metal/kernels/prompt.metal` sha256 `97a3391e86fc1fa0ba5089263e4d5bdb15e6ef4b4c2b5c375f3da5e859013921`- `/tmp/sol_r35_prompt/candidate-final.metal` sha256 `e91f0d0dc9679de031cf7aabdbc022e3cc7dcfdf514270fc39fcb3fe4aefc2a5`- `/tmp/sol_r35_prompt/strata/metal/tests/comb_fold_parity.cpp` sha256 `41d817810837181155bac683728f7ff04abfe7e8166099831e5e47e85537038b`- `/tmp/sol_r35_prompt/comb_fold_parity` sha256 `ce858fe0158b810b1196d8eb16d872241f72a22fde40bf652f04ae59f4211664`- `/tmp/sol_r35_prompt/sol_r35_prompt.patch` sha256 `24ef87c89fb094ef045f445b01975bf77536439297547c3e62657cfc32d6cc35`
visual final review used view_diff over every final change and view_code over evaluator context; searched existing tile/row helpers before keeping extraction. no unrelated file/line changes; final patch contains only prompt.metal and comb_fold_parity.cpp. no subagents, no full-model run, no :8080 access. parent owns integration and exclusive performance acceptance.