# Round 16 Track C: `ple_moe_gate_norm_c` SIMD Slot Compaction + Stream-Loop Unrolling & `ple_conv` Vectorized `half4` / Branchless `t >= 9` Fast Path

## 1. Summary of Changes

### Target 1: `metal/src/engine/msl/ple.metal`
1. **`ple_moe_gate_norm_c` SIMD Slot Compaction (`roff[0..cnt-1]`) + `n == 2560` Unrolling (lines 176–263):**
   - Replaced the `threadgroup int rk[16]` + per-iteration `if (rk[k] >= 0)` branch + 64-bit `ulong(rk[k]) * n` multiply inside the `d` loop with SIMD prefix compaction (`simd_prefix_exclusive_sum(ushort(valid))`, `simd_sum(ushort(valid))`) into `threadgroup uint roff[17]; threadgroup float wk[16];` where `roff[prefix] = uint(r) * n`.
   - Reduced the inner expert accumulation loop to branchless 32-bit `for (uint k = 0; k < cnt; ++k) s = fma(wk[k], YE[roff[k] + d], s);` while preserving exact `k` accumulation order (`memcmp == 0` under dropped slots `pos == -1`).
2. **Fixed `NPT = 10` (`n == 2560`) Loop Unrolling + Pointer-Offset Hoisting & `tg_sum` Unrolling (lines 23–263):**
   - Verified `tg_sum` retains its trailing `threadgroup_barrier(mem_flags::mem_threadgroup)` (required because `gnorm_regs(k)`, `gnorm_regs(q)`, and `gate_norm_regs` reuse `threadgroup float part[8]` across sequential reductions) and added `#pragma clang loop unroll(full)` to the 8-element reduction loop in `tg_sum`.
   - Added `n == 2560` fast paths with `#pragma clang loop unroll(full)` and base-pointer hoisting (`+ tid` outside the loop, constant `i * 256` displacements inside) across `gnorm_regs`, `gate_norm_regs`, `ple_query_gate_norm`, `moe_gate_norm_row`, `ple_moe_gate_norm`, and `ple_moe_gate_norm_c`.
3. **`ple_conv` Vectorized `half4` Weight Load + Branchless `t >= 9` Fast Path (lines 275–295):**
   - Replaced the 4 scalar `half` tap loads (`kw[k + 4 * ch]`) with a single 8-byte aligned vectorized load `const half4 w = *(device const half4*)(kw + 4 * ch);`.
   - Added a branchless `if (t >= 9)` fast path (taken by `2,039 / 2,048 = 99.56%` of rows in a 2048-token prompt chunk) that hoists `device const float* nw = norm_w + (t - 9) * hcd + ch; const uint s3 = 3 * hcd;` and accumulates `nw[0]`, `nw[s3]`, `nw[2 * s3]`, `nw[3 * s3]` in identical sequential `acc += ...` order (`memcmp == 0`), eliminating all 4 per-tap `p >= 0` ternary branches and `hist` address calculations.

### Target 2: `metal/kernels/gdn_prompt.metal` (`gdn_post`, lines 433–460)
- Hoisted base row pointers (`o_row = o + base`, `z_row = z + base`, `g_row = gamma + lane`, `yh_row = yh + base`, `y_row = y + base`) so both the 4-channel RMSNorm preload (`o_row[0]`, `o_row[32]`, `o_row[64]`, `o_row[96]`) and the unrolled `k = 0..3` output loop use constant immediate offsets `off = k * 32`.
- Verified `100%` bitwise parity (`memcmp == 0` on both `yh` and `y` for `f32 = 0, 1` and `1-sg` vs legacy grid) and up to `1.8%` faster execution at `T = 1024, 2048` (`f32 = 1`).

### Target 3: `metal/tests/ple_moe_parity.cpp`
- Added `--bench` and `--ab baseline.metal [T]` modes testing bitwise parity (`memcmp == 0`) across `T = 1, 3, 7, 9, 10, 64, 512, 1024, 2048` and interleaved 5-pair A/B timing at `T = 512, 1024, 2048`.

---

## 2. Bitwise Parity & Benchmark Results (Apple M5)

### `ple_moe_parity` Bitwise Verification (`T = 1, 3, 7, 8, 9, 10, 64, 512, 1024, 2048`)
- `ple_moe_gate_norm + ple_conv_res`: **100% bitwise PASS (`memcmp == 0`)** across all tested `T`.
- `ple_moe_gate_norm_c + ple_conv_res` (with dropped slots `pos == -1`): **100% bitwise PASS (`memcmp == 0`)** across all tested `T`.
- `ple_query_gate_norm + ple_conv_res`: **100% bitwise PASS (`memcmp == 0`)** across all tested `T`.
- `ple_conv_res + ple_hist_adv`: **100% bitwise PASS (`memcmp == 0`)** across `T = 1, 3, 7, 8, 9, 10, 64, 2048`.

### Paired A/B Timing (`ple_moe_parity --ab`, median of 5 interleaved pairs x 8 reps)

| Kernel / Stage | `T = 512` Base -> Cand (Ratio) | `T = 1024` Base -> Cand (Ratio) | `T = 2048` Base -> Cand (Ratio) |
|---|---:|---:|---:|
| `ple_moe_gate_norm_c` | `1.264 -> 1.133 ms` (`0.8966x`, **-10.3%**) | `2.455 -> 2.265 ms` (`0.9224x`, **-7.8%**) | `4.820 -> 4.496 ms` (`0.9321x`, **-6.8%**) |
| `ple_query_gate_norm` | `0.796 -> 0.777 ms` (`0.9832x`, **-1.7%**) | `1.614 -> 1.614 ms` (`1.0002x`, **+0.0%**) | `3.210 -> 3.237 ms` (`1.0079x`, **+0.8%**) |
| `ple_conv_res` | `0.477 -> 0.482 ms` (`1.0106x`, **+1.1%**) | `0.956 -> 0.957 ms` (`0.9994x`, **-0.1%**) | `1.907 -> 1.914 ms` (`1.0029x`, **+0.3%**) |
| `ple_moe_gate_norm_c + ple_conv_res` | `1.709 -> 1.608 ms` (`0.9426x`, **-5.7%**) | `3.391 -> 3.196 ms` (`0.9422x`, **-5.8%**) | `6.714 -> 6.404 ms` (`0.9533x`, **-4.7%**) |
| `ple_query_gate_norm + ple_conv_res` | `1.277 -> 1.273 ms` (`0.9993x`, **-0.1%**) | `2.567 -> 2.584 ms` (`1.0041x`, **+0.4%**) | `5.108 -> 5.139 ms` (`1.0060x`, **+0.6%**) |

### `gdn_prompt_parity --ab` (`gdn_post` Stage 2, 36 layers, median of 3 interleaved pairs)
- `T = 512, f32 = 1`: `12.164 -> 12.162 ms` (`ratio 0.9998`, `yh PASS`, `y PASS`)
- `T = 1024, f32 = 0`: `16.071 -> 16.071 ms` (`ratio 1.0002`, `yh PASS`, `y PASS`)
- `T = 1024, f32 = 1`: `24.928 -> 24.410 ms` (`ratio 0.9823`, **-1.8%**, `yh PASS`, `y PASS`)
- `T = 2048, f32 = 1`: `50.341 -> 49.725 ms` (`ratio 0.9915`, **-0.9%**, `yh PASS`, `y PASS`)

## 3. Artifacts
- Unified patch: `/tmp/argon_r16_ple/argon_r16_ple.patch` (verified clean with `git -C vendor/strata apply --check`).
- Modified files in patch:
  - `metal/src/engine/msl/ple.metal`
  - `metal/kernels/gdn_prompt.metal` (lines 433–460 only)
  - `metal/tests/ple_moe_parity.cpp`
