C1: `submission = per_kernel` / `forward_submission = per_stage` / `pipelining = off` (radix_scan = gpu_hierarchical / binning = resident は現 default のまま) で 12.5k Lego を 1 run。leaf label の gpu_ms 合計 141.716s は `gpu_busy_stats()` の union 141.716s と<strong>誤差 0.3ms で一致</strong> (per_kernel/per_stage は commit+wait 境界が kernel 境界と一致するため、chained 由来の二重計上が原理的に起きない)。<strong>backward_raster (+project_back、bit 同値のため 1 行に統合) 33.86%</strong> > radix 26.40% > adam 12.68% > rasterize 11.64% > ssim(+l1_combine) 11.80% > project(+emit+extract_off) 3.61%、refine/zero_grads は host 実コストがありながら **GPU 側 gpu_ms が 0** に落ちる (非ブロッキング harvest の帰属漏れ、既知の限界)。<strong>M3 の GO 閾値 (≥25%) を 33.86% で超過 → per-splat backward 実装へ進む根拠が立った</strong>。C2 は M0 (並行 doc 修正 c8864c8) が発見した会計訂正後の depth2 残差 sync-tax 内訳 <strong>refine drain 6.26s (44%) / encode(B) 3.41s (24%) / 残差 4.54s (32%)</strong> を出発点に、残差 4.54s のさらなる driver-latency / CB間-gap 分解を試みたが、<strong>`gputime.rs` は `kernelStartTime`/`kernelEndTime` を bind しているだけで収集していない</strong> (Collector は `GPUStartTime`/`GPUEndTime` のペアのみ保持) ため、既存計装では分解不能と判明。file:line 付きで追加案を残す (実装はしていない)。
p2-counter-session の per-kernel share 表 (backward_raster 33.6% > radix 26.3% > adam 13.8% > rasterize 11.9% > ssim 10.0%) は commit 56519c4 — S2 (forward 1 CB 化 + hierarchical GPU scan) の実装 (414c633) より後、しかし default 採用 (3027e33) より前 — に撮られたデータで、config が `[backend] radix_scan` を明示していなかったため 当時のデフォルト (旧 flat scan) で計測されていた。post-S2/S3 (hierarchical scan + resident binning が defaultの現在) で撮り直すとどう動くかが M1 C1 の主題であり、その結果が M3 (per-splat backward) の着手可否を決める。
本 run の GPU busy は 68.9% (production depth2 default の 91.3% とは別物)、wallclock も 205.78s (production の 151.70s とは別物)。submission=per_kernel/forward_submission=per_stage/pipelining=off という 3 重に非 production な regimeであり、以下の share 表と kernel 単体の gpu_ms/iter だけが意味を持つ。counter-session はこの点を「chained 12.498 vs per_kernel 12.874 ms/iter busy (差 3%)」で検証しており、kernel 単体の gpu_ms は submission wrapping にほぼ不変という事後検証があるため、share の値自体は production の kernel 内訳の近似として読んでよい。
| kernel (leaf label) | gpu_ms 総計 (12,500 iter) | gpu_ms/iter | share |
|---|---|---|---|
| **`ts_backward_raster` (+ `ts_project_back`、bit 同値のため統合)** | 47,991.156 | **3.839** | **33.86%** |
| `ts_fwd_radix_sort` | 37,416.557 | 2.993 | 26.40% |
| `ts_adam` | 17,970.471 | 1.438 | 12.68% |
| `ts_fwd_rasterize` | 16,494.460 | 1.320 | 11.64% |
| `ts_ssim_fwd_grad` | 15,516.979 | 1.241 | 10.95% |
| `ts_fwd_project` | 3,500.682 | 0.280 | 2.47% |
| `ts_fwd_emit_pairs` | 1,473.654 | 0.118 | 1.04% |
| `ts_l1_combine_ssim` | 1,205.594 | 0.096 | 0.85% |
| `ts_fwd_extract_off` | 146.144 | 0.012 | 0.10% |
| `ts_refine` (121 tick、host 5.047s) | 0.000 | 0.000 | 0.00% |
| `ts_refine_accumulate` (host 3.969s) | 0.000 | 0.000 | 0.00% |
| `ts_opacity_decay` (121 tick、host 0.127s) | 0.000 | 0.000 | 0.00% |
| `ts_zero_grads` (host 0.140s) | 0.000 | 0.000 | 0.00% |
| **leaf 合計** | **141,715.697** | **11.337** | **100%** |
| (参照) `gpu_busy_stats()` union | 141,716.0 (141.716s) | 11.337 | — |
counter-session の per_kernel 表は leaf 合計が union を +5.4% 上回っていた (非ブロッキング harvest が wait なし commit の CB を後続 label へ重複帰属)。本 run は 141.715697s (leaf 合計) vs 141.716s (union) で 差 0.3ms (0.0002%) — 実質完全一致。`per_stage` forward は各 stage が commit+wait 境界を持つため (`trainer.rs` の per-stage forward path、S2 以前と同じ構造)、非ブロッキング harvest の帰属漏れが起きる余地がほぼ無い。「leaf 合計 ≠ union」問題は submission=per_kernel/forward_submission=per_stage では原理的に解消される — 逆に言えば chained (production) でこの表を撮ろうとすると必ず起きる帰属誤差だということでもある。
`ts_project_back` (host 9.737s、gpu_ms **47,991.156** — `ts_backward_raster` と bit 単位で同値) は counter-session でも観測された「同一 CB 集合を指す 2 つの label」現象がそのまま再現している。上表では 1 行に統合し、host 列は参考として両者の合計 (54.924+9.737=64.661s) を別途 §2 で扱う。
`ts_refine` (121 tick、host 合計 5.047s = tick あたり 41.7ms) と `ts_zero_grads` (12,500 回、host 合計 0.140s) はいずれも gpu_ms が 0.000 だが、host 時間は明確に非ゼロ (特に refine は 1 tick 41.7ms と重い)。これは 非ブロッキング harvest の帰属先が「その label の closure 内で完了した CB」に限られるため — refine tick 中に commit された GPU 側の実作業 (opacity decay 等の compute dispatch) が、tick の closure が終わる時点でまだ完了しておらず、次の label (`ts_forward` 等) の harvest 時に回収されてそちらに計上される典型例 (p2-s3 finding が `ts_refine_accumulate` の gpu 0→1,871.9 で報告したのと同じ機構)。つまり本表の refine 行 (0.00%) は「refine の GPU コストが無い」ことを意味しない — 実コストは backward_raster 等の行に混入して見えなくなっている。この limitation は counter-session の callout (「label は入れ子」「Σ label.gpu ≠ 全体 GPU busy」) の系であり、新規発見ではない。
| 指標 | 値 | 根拠 |
|---|---|---|
| backward_raster (+project_back) share | **33.86%** | 上表 (leaf 合計に対する比) |
| M3 GO 閾値 (milestones.md) | ≥ 25% | docs/strategy/2026-09-seminar05-milestones.md M3 節 |
| 判定 | **GO** | 33.86% は閾値を 8.9pt 超過 |
| ナイーブ期待 Δ = share × 0.25 | **8.47%** | brush PR #401 の -25% 主張をそのまま backward_raster share に乗せた素朴な積 (milestone 本文が要求する形式) |
| busy%-調整後の期待 wallclock Δ | **≈7.7%** | 33.86% × 91.3% (production depth2 の busy%) × 0.25。本 run の share は GPU busy に対する比率であり、production の wallclock に対する比率に変換するには busy% を掛ける必要がある (本 run 自身の busy% 68.9% は非 production regime なので使わない) |
milestone 本文は「backward_raster の share (%) と、それ × 0.25 の期待 wallclock Δ」を 1 行で求めており、素朴に計算すれば 33.86% × 0.25 = 8.47% になる。ただしこれは「GPU busy 時間のうち backward_raster が占める割合」であって「wallclock のうち backward_raster が占める割合」ではない — production (depth2 default) は busy 91.3% / sync-tax 8.7% なので、GPU busy の delta がそのまま wallclock の delta になるとしても、wallclock に対する backward_raster の実効 share は 33.86% × 91.3% ≈ 30.9% であり、期待 Δ は 30.9% × 0.25 ≈ 7.7% のほうが production の数字としては妥当。両方とも n=1 点推定・brush の -25% を無検証で借用した上限値である点は変わらない — M3 実装後の実測 gate でしか確定しない。
| kernel | counter-session (pre-S2/S3, 旧 flat scan) | 本 finding (post-S2/S3, hierarchical scan) | Δ (pt) |
|---|---|---|---|
| backward_raster (+project_back) | 33.6% | 33.86% | +0.26 |
| radix | 26.3% | 26.40% | +0.10 |
| adam | 13.8% | 12.68% | -1.12 |
| rasterize | 11.9% | 11.64% | -0.26 |
| ssim (`ts_ssim_fwd_grad`) | 10.0% | 10.95% | +0.95 |
| project + emit_pairs + extract_off | 2.7+1.0+0.1=3.8% | 2.47+1.04+0.10=3.61% | -0.19 |
radix の share は 26.3% → 26.40% と実質不変。roadmap v2 が S2 gate 時に明記していた「S2 (hierarchical scan) が狙うのは radix の CB/wait 数 であって kernel 時間ではない」という注記どおりの結果であり、実測でこの想定を裏づけた。adam が -1.12pt 下がっているのは測定誤差 (n=1) の範囲内とみるのが妥当で、有意な変化と主張するには n=3 が要る。全体として「backward_raster が最大単独ブロックで 1/3 強を占める」という律速構造は S2/S3 を経ても変わっていない — これが M3 (per-splat backward) を優先する直接的な根拠になる。
① splat/crates/splat-metal/src/gputime.rs:153-159 — Collector.intervals を Vec<(f64,f64)> (gpu start/end) から Vec<(f64,f64,f64,f64)> (kernel_start, kernel_end, gpu_start, gpu_end) に拡張。② 同ファイル collect() の let (s, e) = (cb.gpu_start_time(), cb.gpu_end_time()); (L237) の直前後に let (ks, ke) = (cb.kernel_start_time(), cb.kernel_end_time()); を足し、4 tuple を push。③ union_since() (L265) と同じ形の pub fn intervals_since(mark: usize) -> Vec<(f64,f64,f64,f64)> を追加してソート済み生 interval を label 単位で取り出せるようにする。④ splat-train-v1/src/timing.rs に、④-a 連続 GPU interval 間の gap (gpu_start[i+1] - gpu_end[i]、= CB 間 gap) と ④-b 各 CB の gpu_start - kernel_end (= driver 投入 latency の近似、kernelEndTime は driver がコマンドの GPU 側実行可能形式への変換を終えた時刻) を集計する gpu_gap_report() を追加。⑤ 呼び出し箇所は splat-train-v1/src/train_loop.rs:802-803 (既存の gpu_drain() → print_summary() の直後、最終 blocking drain 済みで全 CB が回収されている地点)。この 5 箇所を実装すれば、次回 depth2-prof run 1 本で残差 4.54s の分解が測れる見込み (追加コストはほぼゼロ — 既存 register()/collect() 経路に相乗りするだけ)。
leaf 9 label (backward_raster / ssim_fwd_grad / adam / fwd_project / fwd_emit_pairs / fwd_radix_sort / fwd_rasterize / fwd_extract_off / l1_combine_ssim、`ts_project_back` は `ts_backward_raster` と bit 同値の重複のため v1 と同様に 1 行へ統合) の gpu_ms 合計は 114,507.738ms = 114.508s で、run 末尾の [gpu] busy 114.508s と完全一致。v2 run のバイナリには v1 に無かった新規 sub-label (ts_forward / ts_raster_bwd_gpu / ts_project_back_gpu / ts_project_soa_gpu 等) が追加されているが、これらはいずれも host 計測目的の子スコープで gpu_ms は 0 に落ちるか (v1 の refine/zero_grads と同じ「非ブロッキング harvest が親スコープに帰属する」現象)、または上位 leaf の子要素の和と完全一致する集計ラベル (ts_forward の gpu_ms 34,977.613 = fwd_project + fwd_emit_pairs + fwd_radix_sort + fwd_rasterize + fwd_extract_off の和と bit 一致) であることを確認し、leaf 集合の選定から除外した (v1 の「leaf 判定は union との一致で検算する」規約をそのまま適用)。
| kernel (leaf label) | v1 gpu_ms (M1、M4/A7 前) | v2 gpu_ms (M4/A7 後) | abs Δ | v1 share | v2 share | share Δ (pt) |
|---|---|---|---|---|---|---|
| **backward_raster (+project_back)** | 47,991.156 | 45,877.989 | -4.40% | 33.86% | **40.07%** | +6.21 |
| adam | 17,970.471 | 17,502.128 | -2.61% | 12.68% | **15.28%** | +2.60 |
| rasterize (`ts_fwd_rasterize`) | 16,494.460 | 16,185.077 | -1.88% | 11.64% | **14.13%** | +2.49 |
| ssim (`ts_ssim_fwd_grad`) | 15,516.979 | 15,025.375 | -3.17% | 10.95% | **13.12%** | +2.17 |
| radix (`ts_fwd_radix_sort`) | 37,416.557 | 13,749.681 | **-63.25%** | 26.40% | **12.01%** | **-14.39** |
| project (`ts_fwd_project`) | 3,500.682 | 3,391.138 | -3.13% | 2.47% | 2.96% | +0.49 |
| emit_pairs (`ts_fwd_emit_pairs`) | 1,473.654 | 1,511.105 | +2.54% | 1.04% | 1.32% | +0.28 |
| `ts_l1_combine_ssim` | 1,205.594 | 1,124.633 | -6.72% | 0.85% | 0.98% | +0.13 |
| extract_off (`ts_fwd_extract_off`) | 146.144 | 140.612 | -3.79% | 0.10% | 0.12% | +0.02 |
| **leaf 合計** | **141,715.697** | **114,507.738** | **-19.20%** | **100%** | **100%** | — |
本 v2 run (submission=per_kernel/forward_submission=per_stage) の radix 単独 gpu_ms 減少 -63.25% は、M4 の gate finding (p3-radix-ballot §4) が別の run で計測した「forward_submission=per_stage 単独計測での radix gpu_ms/call -63.7%」と独立した run・独立した計測断面で一致する (両者とも `radix_scatter` flag 差の A/B ではなく、v1(M4前)/v2(M4後) や M4 gate の off/on という異なる比較軸から同じ数字に収束している)。leaf 合計の総額縮小 -19.20% (141.716s → 114.508s) のほぼ全部が radix 単独の絶対削減 (37,416.6 → 13,749.7ms = -23,666.9ms) で説明でき、他 8 label の削減 (backward_raster -4.40%〜l1_combine_ssim -6.72%、絶対にして合計 ~2,470ms) は寄与として一桁小さい。A7 (refine 3-pass 削減) は host 側の allocation/copy を減らす施策であり、この per-kernel gpu_ms 表 (GPU 側 kernel 実行時間) には理論上ほぼ影響しないはずという予想と、他 8 label の変化が誤差レベル (-1.9〜+2.5%、n=1 点推定のノイズ帯) に収まっていることは整合する。
上表で backward_raster (33.86%→40.07%) / adam (12.68%→15.28%) / rasterize (11.64%→14.13%) / ssim (10.95%→13.12%) はshare だけを見ると全て上昇しているが、これは分母 (leaf 合計) が radix の劇的な縮小で 19.20% 小さくなったことによる相対効果であり、これら 4 kernel 自体の絶対 gpu_ms はすべて -1.88%〜-4.40% と、v1→v2 でむしろわずかに縮んでいる (n=1 点推定のノイズ帯内で、A7/その他の間接効果か測定誤差かは切り分けられない)。「share が上がった = 遅くなった」ではない — 唯一の実質的な構造変化は radix の share が 26.40% → 12.01% (-14.39pt) に落ち、backward_raster が単独最大ブロックとしての優位をさらに拡大した (33.86%→40.07%、2 位の adam 15.28% との差が 21.2pt→24.8pt に拡大) ことのみである。
| kernel (leaf label) | v2 ms/iter (a446f2c) | v3 ms/iter (a556b78, 同 Lego scene) | v3/v2 比 | v2 share | v3 share | share Δ (pt) |
|---|---|---|---|---|---|---|
| backward_raster (+project_back) | 3.670 | 3.693 | 1.006× | 40.07% | 40.18% | +0.11 |
| adam | 1.400 | 1.416 | 1.011× | 15.28% | 15.40% | +0.12 |
| rasterize | 1.295 | 1.293 | 0.999× | 14.13% | 14.07% | -0.06 |
| ssim (`ts_ssim_fwd_grad`) | 1.202 | 1.194 | 0.993× | 13.12% | 12.99% | -0.13 |
| radix | 1.100 | 1.103 | 1.003× | 12.01% | 12.00% | -0.01 |
| project | 0.271 | 0.274 | 1.012× | 2.96% | 2.99% | +0.03 |
| l1_combine_ssim | 0.090 | 0.090 | 1.000× | 0.98% | 0.98% | 0.00 |
| emit_pairs | 0.121 | 0.116 | 0.962× | 1.32% | 1.27% | -0.05 |
| extract_off | 0.011 | 0.011 | 1.008× | 0.12% | 0.12% | 0.00 |
| **leaf 合計** | **9.161** | **9.190** | **1.003×** | 100% | 100% | — |
同じ Lego scene・同じ [backend] regime で binary だけを `a446f2c`→`a556b78` (S4b + C2 計装 + それ以降の全 commit) に進めても、9 leaf すべてが v2 比 0.962×〜1.012× (leaf 合計 1.003×) に収まった — 最大逸脱は emit_pairs の -3.80%、次点は ssim (`ts_ssim_fwd_grad`) の -0.67% で、share Δ も全 kernel ±0.13pt 以内。S4b (ssim_reduce=Gpu) と C2 計装 (全 CB kernel timestamp 収集) を含む binary vintage 差は per-kernel gpu_ms を系統的には底上げしていない — `p3-kernel-share-drums` §2/§4 が疑った「profile=on 経路の系統的な底上げ」仮説は本 run により反証された。drums で観測された 1.17〜1.93倍の増加 (ssim_fwd_grad +44.6% を含む) は binary vintage ではなくdrums scene 自体 (splat 数 1.85倍、画像内容差) に起因すると判定する — 詳細は `p3-kernel-share-drums.toml` §5 参照。
M3 は本 finding v1 の GO 判定 (33.86% ≥ 25%) を受けて実装・gate まで完了しており (`p3-per-splat-backward` / `p3-per-splat-backward-tg256`)、tg256 版が default (per_pixel) 比 +32.8% と大幅劣後で NO-GO 確定済みである。v2 の backward_raster share 40.07% は M3 実装当時 (33.86%) より さらに大きいため、GO/NO-GO の判定自体は変わらない (GO のまま) — ただし M3 の性能敗因は「256-thread TG の低 occupancy」という実装上の問題であり、share の大小とは無関係の敗因だったため、v2 の share 上昇は M3 の結論を覆さない。本節は記録のためだけに残す — M3 系列の探索は打ち切り済みで、v2 を受けた再挑戦の計画は無い。
この finding が観測された / 言及している実験 run。