per-splat backward (brush #401 型、M3/M3b) が 2 版とも regression で打ち切られたあと、<code>rasterize_backwards.metal</code> の per-pixel 構造を保ったまま <code>backward_raster</code> (kernel 内最大 share 40.07%) をさらに削れる余地を audit した。既存の勝者施策 (SIMD reduction / W1a warp sparse skip / last_contributor) と禁忌リスト (Tg staging -79% busy、f16、SIMD prefix、reduction) を踏まえると、<strong>bit-exact-class の候補で harness の分解能 (~1%、M2 Tier1 / M5a で確認済みの床) を超えるものは見つからなかった</strong> (bit-inexact な reciprocal division 候補 1 件は AIR レベルで headroom があると確認できたが実測は持ち越し)、というのが本 audit の主結論。最有力候補 (<code>rasterize_backwards_simd</code> の 9 atomic emit を lane 0 だけでなく lane 0..8 の 9 lane に分散する M8) を実装し <code>BackendConfig.raster_backward_atomic</code> (default <code>lane_zero</code>) で flag-gate した。正しさは新規 unit test 2 本 (bit-exact) と実 checkpoint (408,469 splats) の <code>splat bench --parity</code> で検証 — 後者は既存 backward が持つ (原因未確定の) self-noise と <strong>候補の diff が統計的に見分けがつかない</strong>ことを確認し、spread が新しい誤差源を持ち込んでいないことを示した。<strong>性能は whole-iteration・backward kernel 単体 gpu_ms のどちらでも一貫して遅い側</strong> (whole-iteration median ms/iter: off 13.586 → on 13.765、<strong>+1.32%</strong>、GPU busy +2.02%;<code>ts_backward_raster</code> gpu_ms/call: off 4.2016 → on 4.3670ms、<strong>+3.94%</strong>) — <strong>NO-GO</strong>、default は <code>lane_zero</code> のまま。12.5k full gate 用 config 6 本 (<code>2026-08-30-bwd1-12500-{off,on}-r{1,2,3}.toml</code>) は作成のみ (`splat check` で deserialize 確認済み)、明確な regression のため実行はしなかった。
per-splat backward (brush PR #401 型) の 2 版 (M3/M3b) が per-kernel share GO 判定 (backward_raster ≥25%) を受けて実装されたにもかかわらず、いずれも default (per_pixel: Simd+WarpAny+LastContributor) 比 +33〜39% の regression で打ち切られた (finding: p3-per-splat-backward / -tg256)。post-M4/A7 の per-kernel share v2 でも backward_raster は 40.07% まで share を伸ばしており (分母の radix 縮小による相対効果が主、絶対 gpu_ms 自体はほぼ不変)、依然として単独最大ブロックである。本 finding は「transpose (per-splat 化) 抜きで、per-pixel 構造を保ったまま backward_raster を削れる余地はまだあるか」を監査し (段 1)、最有力候補 1 件を実装して gate screen まで行った (段 2) 記録である。
| 候補 | 観点 | 期待 Δ (backward 単体) | bit-exact 性 | 工数 | 既知の敗因との衝突 |
|---|---|---|---|---|---|
| **M8: atomic emit lane 分散** (実装・gate screen 済み) | (a) | 不明瞭 — lane 0 の 9 atomic は既に HW がパイプライン化している可能性 (advisor 指摘)。実測: **+1.3%〜+2.0% regression** | 弱い意味で bit-exact (同一 (addr,value) 集合、発行 lane と順序のみ変化、既存の atomic 順序非決定性の範疇) | 低 (shader 1 file + Rust 配線) | 衝突なし (Tg staging/reduction とは別物) だが実測 NO-GO |
| `one_m_alpha` 除算 2 回 → reciprocal 1 回 + 乗算 | (e) ALU throughput | **headroom 確認済み、gain は未実測**。function constant を default 値 (lc=true, sparse=true, spread=false) に固定した AIR (`xcrun -sdk macosx metal -O3 -std=metal3.1 -ffast-math -S`) を確認したところ、`rasterize_backwards_simd` 本体に `fdiv fast <3 x float>` (= `after_me / one_m_alpha`) と `fdiv fast float` (= `dL_dA * T_final / one_m_alpha`) の **2 命令が残っており、同一 denominator への CSE (reciprocal 共有) は起きていない** — 「fast-math が既にやっているので no-op」という当初の懸念は LLVM IR レベルでは反証された。ただし最終 GPU ISA への lowering は on-device compiler 依存でこの環境では観測不能なため、**理論的な headroom はあるが実測 gain は未確認** | bit-inexact (rounding が変わる、rel ~1e-5 オーダー想定) | 低 | **p3-ssim-float4 と同型の衝突**: `float4_rcp` は ssim 単体 -20.8% でも 12.5k PSNR guard に抵触 (3/3 分離、rel 8e-5 が効いた) して不採用。backward は Adam に直結するので同種リスクがより重い |
| splat read の software prefetch (次 splat を先読みしつつ現 splat を計算) | (e) memory latency hiding | 不明瞭 — 256-thread/tile で occupancy は既に高く (busy 79〜91%)、TLP がレイテンシを隠せている可能性が高い | bit-exact (loop-carried な T/pix_acc の演算順序は不変、読み出しの順序だけ変更) | 低〜中(loop 構造の手動 unroll) | 衝突なし、ただし priori に効果を見積もれず prototyping が要る |
| `Splat2D` AoS→SoA 分割 (hot/cold field 分離) | (d) | 低 (§1(d) の broadcast-read 論証参照 — L1 は既に broadcast 済みで削減余地が薄い) | bit-exact | **高** (producer `project.rs` + forward kernel consumer の両方に触る) | 衝突なし、ただし工数/期待 Δ 比が悪い |
| last_contributor の粒度を sub-warp まで細分化 | (c) | 無し — 現行 (simd_max + warp_any) が既に理論上の天井に近い。細分化には SIMD lockstep 放棄 = atomic-per-small-group への回帰が必要 | N/A (実装しても Original 系への逆行) | N/A | **#5.33 (Original 敗退) と同型の衝突** — 追加候補として立てず |
| (参考) threadgroup 全体 reduction (`_tg` variant の再挑戦) | (a) | 既知: +63.6% (S2 後再判定) | bit-exact | — | **#5.33c 禁忌、再挑戦せず** |
| (参考) backward への Tg staging (splat を threadgroup memory に事前ロード) | (d) | 既知: busy -79% | bit-exact | — | **禁忌リスト明記 (bwd-tg)、再挑戦せず** |
| (参考) per-splat transpose (brush #401 型、M3/M3b) | 構造転換 | 既知: +34.1% / +32.8% | PASS (abs/rel 1e-4) | — | **M3/M3b で 2 回敗退、探索打ち切り済み** |
M2 Tier1 (A1-A4 まとめ) は 12.5k n=5 で Δ -1.11% を「wallclock 効果量 ≤1% で分解能未満、判定不能」として棄却し、M5a (SSIM float4_rcp) は 12.5k で Δ -0.055% を「null」と判定した。この 2 つの実測が、本 harness (12.5k Lego、n=3〜5) が確実に検出できる下限が概ね 1% 前後であることを示している。段 1 の 6 候補のうち、(b)(c)(f) は候補として成立せず、(d) は broadcast-read 論証により低 payoff、(a) (M8) は実測で harness の床未満どころか **逆方向 (+1.3% whole-iteration / +3.94% backward kernel 単体)** だった。**(e) reciprocal division は唯一「headroom が無いこと」を実測で反証できなかった候補** — AIR レベルで 2 fdiv が残っていることを確認したが (未 CSE)、bit-inexact な変更であり p3-ssim-float4 と同型の PSNR guard 抵触リスクを持つため、実装・gate までは本 session のスコープ外とした。**正確には「per-pixel 構造を保ったまま bit-exact-class で大きく効く候補はもう残っていない」が本 audit の実測的な結論** (bit-inexact な候補は未走査のまま 1 件残る)。次に有意な Δ を狙うなら per-pixel 構造そのものを離れる (= per-splat 系、既に 2 回敗退) か、reciprocal division の実測 (bit-inexact、PSNR guard リスクあり) か、禁忌リストの改訂 (8-bit radix 再挑戦の J1 判断待ちと同型の意思決定) のいずれかが必要になる。
| test / 手法 | 何を pin するか | 結果 |
|---|---|---|
| `backward_atomic_spread_byte_equality_single_pixel` | dldr を 1 pixel だけ非ゼロにして atomic 順序の影響を消した条件で `LaneZero` vs `SpreadLanes` が bit 一致 | **PASS** |
| `backward_atomic_spread_parity_simd` | 24 splat 重畳 fixture (multi-pixel) で `abs/rel 1e-4` dual-threshold (既存 W1a parity test と同一形式) | **PASS** |
| `rasterize_psos_compile` | MSL コンパイル + 全 PSO 構築 (新規 `pso_backward_simd_lc_sparse_spread` 含む) | **PASS** |
| workspace 全体 (`cargo test --workspace --release`) | splat-core 7 + splat-io 1 + splat-metal 93 (新規 2 本含む) + splat-train-v1 76、default 挙動 (LaneZero) が無変更であること | **PASS / failure 0 (2 件 ignore は既存の baseline 専用 test で本変更と無関係)** |
| `splat bench --parity` (実 checkpoint 408,469 splats、`configs/2026-08-30-bwd1-bench-off.toml` vs `-parity-on-candidate.toml`) | means/log_scales/rot_quats/raw_opacities/sh_coeffs の全 grad tensor、tolerance は既存 backward variant 比較と同一の `abs=1e-4 rtol=1e-4` | **intermittent FAIL (means_grad, 1/1,225,407 要素)** — 下記 callout 参照、既存 self-noise と同型と判明 |
実 checkpoint (408,469 splats) での `--parity` は複数回実行すると PASS することも FAIL することもある (`means_grad` の max_abs_diff が通常時 5.245e-5 で PASS、稀に厳密に 1.469e-4 まで跳ねて `abs=1e-4` を超え FAIL)。原因を切り分けるため configs/2026-08-30-bwd1-parity-off-selfcheck.toml (baseline = lane_zero と全く同一の `[backend]`) を candidate に指定し、同一 config を自分自身と比較する self-check を実施したところ、M8 (spread vs lane_zero) と全く同じ頻度・全く同じ max_abs_diff (通常時 5.245e-5 / 稀に 1.469e-4) で FAIL/PASS した — これは M8 固有の誤差ではないことの直接証拠になる (同一 kernel を自分自身と比較しても同じ現象が起きるため)。ただし発生機序は 2 通り考えられ、切り分けられていない: 6 回の試行 (self-check 3 回、spread-check 3 回) は {PASS: 5.245e-5} か {FAIL: 1.469e-4} という 離散的な 2 値しか観測せず、連続的な summation-order noise (atomic 加算の実行順序が run ごとに変わることによる浮動小数点非結合性、file 冒頭 C7 注記) が単独の原因なら本来は連続的な分布になるはずで、ちょうど 2 値に固定されるのは不自然。一方、`--parity` の出力は毎回 `sorted_idx` が 2086〜2321 要素 (0.21〜0.24%) で INFO (not gated) の tie-order 非決定性を報告しており、これは既知の radix sort tie-break の非決定性 (p3-per-splat-backward 等で既出) で、atomic 加算順序より大きな摂動 (ある splat の compositing 順序そのものが変わりうる) になり得る。tie flip で特定の splat が `raw_alpha < 0.999` や `alpha >= 1/255` の分岐境界を跨げば、離散的な 2 値(「境界を跨いだ run」と「跨がなかった run」)が生まれても不思議ではない。**本 finding は発生機序を atomic 順序 self-noise と sorted_idx tie-order のどちらか (あるいは両方) に確定できていない** — どちらであっても self-check が同じ現象を再現している以上 M8 の correctness には影響しないという結論は変わらないが、「atomic 順序 self-noise の tail が 1e-4 を超える」という主張は unit-level の bit-exact test (developer が完全に制御できる fixture、こちらは正しさのより強い直接証拠) ほど確立していない点に注意 (§7 に次段の切り分け手順を記載)。
| pair | off (lane_zero) median ms/iter | on (spread_lanes) median ms/iter | Δ wallclock | off busy ms/iter | on busy ms/iter | Δ busy |
|---|---|---|---|---|---|---|
| 1 | 13.564 | 13.731 | +1.23% | 10.736 | 10.953 | +2.02% |
| 2 | 13.600 | 13.812 | +1.56% | 10.756 | 10.991 | +2.18% |
| 3 | 13.595 | 13.753 | +1.16% | 10.765 | 10.963 | +1.84% |
| **平均** | **13.586** | **13.765** | **+1.32%** | **10.752** | **10.969** | **+2.02%** |
| 計測 (backward kernel 単体、`ts_backward_raster`) | off (lane_zero) | on (spread_lanes) | Δ |
|---|---|---|---|
| gpu_ms/call (150 call = 3 repeats × 50 iters) | 4.2016 | 4.3670 | **+3.94%** |
advisor は実装前に「lane 0 の 9 atomic は既に個別に (依存関係の無い independent instruction として) パイプライン化されている可能性があり、spread の win は nil かもしれない」と明示的に caveat していた。実測はさらに一歩進み、nil どころか 3/3 pair で一貫した小さい regression (whole-iteration wallclock +1.32%、GPU busy +2.02%) になった。これが「backward 以外の何かが遅くなった」ことによる見かけの regression でないことは、ts_backward_raster 単体の gpu_ms を per_kernel/per_stage 計測 regime で直接測ることで確認した (+3.94%、上表) — whole-iteration の delta は backward_raster share (40.07%) からの推論ではなく、kernel 自体の直接計測に帰属できる。推測される原因: `simd_lane_id < 9u` の select chain (8 段の三項演算子) を 32 lane 全員が (23 lane は結果を使わないマスク済み実行として) 評価する必要があり、この命令列コストが「lane 0 だけが 9 回 atomic を発行する」ことの低占有コストを上回った可能性がある (未検証の仮説、§7 参照)。`RasterBackwardAtomicKind` の default は `LaneZero` のまま変更しない。