[{"data":1,"prerenderedAt":388},["ShallowReactive",2],{"finding:p3-per-splat-backward":3,"finding-runs:p3-per-splat-backward":255,"finding-related:p3-per-splat-backward":256},{"meta":4,"impact":50,"sections":56},{"id":5,"title":6,"subtitle":7,"eyebrow":8,"date":9,"status":10,"category":11,"polarity":12,"axes":13,"tags":16,"task_code":28,"commit":29,"branch":30,"config_paths":31,"related_runs":42,"related_findings":43},"p3-per-splat-backward","M3 per-splat backward (brush PR #401 型) — 正しさ PASS (full-scale parity) だが wallclock +34.1% regression","既存 backward (per-pixel loop + \u003Ccode>atomic&lt;float&gt;\u003C\u002Fcode> 加算、kernel 内最大 share 33.6%) に対し、brush PR #401 型の \u003Cstrong>1 threadgroup\u002Ftile、1 thread = 1 splat が tile 内 256 pixel を diagonal scheduling で横断\u003C\u002Fstrong> する別 entry (\u003Ccode>rasterize_backwards_per_splat\u003C\u002Fcode>) を追加した。\u003Ccode>BackendConfig.backward_scheme\u003C\u002Fcode> (default \u003Ccode>per_pixel\u003C\u002Fcode>) で切替。\u003Cstrong>正しさは 2 段で確認\u003C\u002Fstrong>: unit fixture (24 splat 合成 tile) の単一 pixel で bit 一致、\u003Ccode>splat bench --parity\u003C\u002Fcode> による \u003Cstrong>実 checkpoint 409,660 splats・全 grad tensor\u003C\u002Fstrong> の比較で既存 backward variant と同じ \u003Ccode>abs\u002Frel 1e-4\u003C\u002Fcode> tolerance を PASS。\u003Cstrong>しかし性能は brush の報告と逆方向\u003C\u002Fstrong> — 同一 checkpoint の短時間 replay bench を \u003Cstrong>3-way\u003C\u002Fstrong> で取ったところ、per_splat は現行 default (per_pixel = simd+warp_any+last_contributor) 比 \u003Cstrong>+34.1%\u003C\u002Fstrong> 遅いだけでなく、\u003Cstrong>最適化を一切持たない per_pixel (original+full_range+off) 比でも +16.5% 遅い\u003C\u002Fstrong> — 「比較が不公平」という懸念を 3rd arm で解消したうえでの構造そのものの敗北。副次効果 (atomic 順序依存の消失) は実証できた (self-noise が atomic 版の 1\u002F9 以下)。この regression で \u003Cstrong>12.5k trainer 統合 gate は見送った\u003C\u002Fstrong> — 実装・parity は資産として残すが、default 採用は推奨しない。","2026-09 seminar05 milestones · M3 · per-splat backward","2026-08-30","stable","experiment","mixed",[14,15],1,3,[17,18,19,20,21,22,23,24,25,26,27],"seminar05-milestones","m3","per-splat-backward","brush-pr-401","diagonal-scheduling","rasterize-backward","atomic-float","parity","backend-config","regression","lego","2026-09-seminar05-milestones.md — M3 per-splat backward (brush PR #401 型)","f6c7cdb95dba","worktree-agent-a68a654f076a906ea",[32,33,34,35,36,37,38,39,40,41],"splat\u002Fconfigs\u002F2026-08-30-m3-bench-perpixel-final.toml","splat\u002Fconfigs\u002F2026-08-30-m3-bench-perpixel-unopt-final.toml","splat\u002Fconfigs\u002F2026-08-30-m3-bench-persplat-final.toml","splat\u002Fconfigs\u002F2026-08-30-m3-parity-persplat-candidate.toml","splat\u002Fconfigs\u002F2026-08-30-m3-12500-perpixel-r1.toml","splat\u002Fconfigs\u002F2026-08-30-m3-12500-perpixel-r2.toml","splat\u002Fconfigs\u002F2026-08-30-m3-12500-perpixel-r3.toml","splat\u002Fconfigs\u002F2026-08-30-m3-12500-persplat-r1.toml","splat\u002Fconfigs\u002F2026-08-30-m3-12500-persplat-r2.toml","splat\u002Fconfigs\u002F2026-08-30-m3-12500-persplat-r3.toml",[],[44,45,46,47,48,49],"p2-w1-warp-sparse-skip","p2-last-contributor","p2-s3-iteration-pipelining","p2-flag-revisit-sweep","p2-bench-harness-calibration","p3-per-splat-backward-tg256",{"summary":51,"rank":52,"verdict":53,"delta_psnr":54,"delta_wallclock":55},"M3 の実装フェーズ (spec: docs\u002Fstrategy\u002F2026-09-seminar05-milestones.md) を完了した。**新 kernel `rasterize_backwards_per_splat` を既存 3 kernel (Original\u002FSimd\u002FTg) と並存する別 entry として追加**し、`BackendConfig.backward_scheme` (`per_pixel` default \u002F `per_splat`) で配線した。**正しさの証拠は 2 層で強い**: (1) unit-level — 単一 pixel fixture で `Original`(FullRange,Off) と bit 一致、multi-pixel fixture (32×32\u002F24×24) で `abs\u002Frel 1e-4` dual-threshold PASS (milestone spec の 1e-5 は `Original` 自身の run-to-run self-noise 4.54e-5 より厳しく、達成不能と判明したため 1e-4 に補正)。(2) **trainer 統合レベル** — `splat bench --parity` で実 checkpoint (Lego final、409,660 splats、sh_degree=3) の 1 iteration を実行し、`means\u002Flog_scales\u002Frot_quats\u002Fraw_opacities\u002Fsh_coeffs` の全 grad tensor が **既存 backward variant (Original vs Simd\u002FTg) と同じ `abs=1e-4 rtol=1e-4` tolerance で PASS** (max_abs_diff は 5.8e-8 〜 2.8e-5 の範囲、既存 Tg の文書化済み値 2.811e-5 と同オーダー)。**性能は逆方向の結果、しかも「比較が不公平」では説明できない** — bench replay を **3-way** で取った: per_splat (20.594 ms\u002Fiter) は現行 default (per_pixel = Simd+WarpAny+LastContributor、15.354 ms\u002Fiter) 比 **+34.1%** 遅いだけでなく、**W1a\u002FLastContributor 等の最適化を一切持たない per_pixel (Original+FullRange+Off、17.681 ms\u002Fiter) 比でも +16.5% 遅い**。GPU busy ms\u002Fiter は per_splat 17.753 に対し per_pixel(unopt) 14.914 で、sync-tax はどの arm も ~2.8〜2.9ms\u002Fiter で揃っている — つまり待ち時間の付け替えではなく **transposed loop nest 構造そのものが Apple GPU 上で不利**という決定的な結果になった。brush が報告する backward -25% とは逆方向。**副次効果 (atomic 順序依存の消失) は実証できた** — per_splat 自身の self-noise は max_rel 5.1e-6 (32×32) \u002F 完全 bit-exact (24×24) で、atomic 版 `Original` の self-noise (4.5e-5) の 1\u002F9 以下。**この regression で 12.5k trainer 統合 gate (wallclock Δ \u002F PSNR guard) を見送る判断をした** — gate 用 config 6 本は作成済み (`splat check` で deserialize 確認済み) だが、bench 3-way の時点で明確な regression が出ているため、main が別途 12.5k で確認を望む場合のみ実行すればよい状態で残す。`cargo build --release` + `cargo test --release` (全 workspace 80+70+8 test) は全 pass、per_pixel default (既存挙動) は無変更。","high","m3-per-splat-backward-correct-but-slower-not-recommended-for-default","N\u002FA (bench replay は 1 checkpoint に対する forward+backward+adam の反復であり学習軌道ではない。12.5k trainer gate は regression 判明のため未実施)","**bench replay 3-way (Lego final checkpoint 409,660 splats、800×800、iters=50×repeats=3、pipelining=off 固定)**: per_pixel(default: simd+warp_any+lc) **15.354** ms\u002Fiter → per_pixel(unopt: original+full_range+off) **17.681** ms\u002Fiter → per_splat **20.594** ms\u002Fiter。**per_splat vs default = +34.1%、per_splat vs unopt = +16.5%**。GPU busy ms\u002Fiter: 12.499 → 14.914 → 17.753 (per_splat が unopt 比でも +19.0%)。sync-tax ms\u002Fiter は 3 arm とも 2.79〜2.87 でほぼ不変。σ: default 1.08% \u002F unopt 0.31% \u002F per_splat 0.17% (n=3、いずれも安定)。**per_splat 自身の self-noise: max_rel 5.1e-6 (32×32 unit fixture) \u002F 0 (24×24、bit-exact) — atomic 版 Original の self-noise 4.5e-5 の 1\u002F9 以下**",[57,60,63,81,83,107,112,114,156,160,163,165,209,212,214,222,224,230,232],{"type":58,"text":59},"lead","M1 (C1 per-kernel 再計測) の完了報告を待たずに実装へ着手した — milestone 一覧の推奨 fan-out 順は「M2 gate → \u003Cstrong>M3 実装起動 (M1 の share を見て)\u003C\u002Fstrong>」であり、既存 doc (`docs\u002Farchitecture\u002Fbackend-and-compiler-conventions.md` C7 注記、`rasterize_backwards.metal` file 冒頭) はすでに backward_raster を \u003Cstrong>kernel 内最大 share (33.6%)\u003C\u002Fstrong> と記録していたため、GO 条件 (share ≥ 25%) を満たす前提で着手した。実装・parity は unit level と trainer-integration level (`splat bench --parity`) の両方で完走し、最後に短時間 bench で性能を確認したところ \u003Cstrong>明確な regression\u003C\u002Fstrong> が出た。以下、実装の設計判断・正しさの証拠・性能 regression の実測・次段への引き継ぎの順に記録する。",{"type":61,"text":62},"heading","1. 実装 — brush PR #401 の構造を本実装の data layout に合わせて書き直す",{"type":64,"items":65},"kv",[66,69,72,75,78],{"key":67,"value":68},"参照元","https:\u002F\u002Fgithub.com\u002FArthurBrussee\u002Fbrush、worktree に clone 済みの `~\u002Fdev\u002Fbrush` (commit `7640a811cdc17ff0cd7b55cf3c7b6873f9f83d78`、PR #401 “Per-splat backward + fused L1+SSIM loss”)。実装対象は `crates\u002Fbrush-render-bwd\u002Fsrc\u002Fkernels\u002Frasterize_backwards.rs` (cubecl DSL)",{"key":70,"value":71},"brush の実際の構造 (実装前に読んで前提を合わせた点)","workgroup = 32 thread (= `SPLAT_BATCH`、1 SIMD group)、TILE_SIZE = 256 pixel。1 thread が 1 splat を担当し、`total_iters = num_splats_this_batch + TILE_SIZE - 1` の loop を diagonal scheduling (`pixel_rank = i - UNIT_POS`) で回す。pixel 側 state (`pix_state`: remaining RGB + T) は shared memory、read-only input (`output`\u002F`v_output`) は毎 iteration global buffer から読み直す (L1 cache 任せ)。**brush はここで forward-order の「remaining を引き算していく」定式化を使っており、reverse-order の un-blend ではない** — 既存 per-pixel kernel の `pix_acc` 累積 + `after_me = C_final - pix_acc - my` と代数的に同一 (`state_new = state_old - my = C_final - pix_acc_new`)",{"key":73,"value":74},"本実装での差分 (写経ではない理由)","brush 固有の (a) 背景色 pre-roll 減算 (`t_final * bg`)、(b) rgb clamp (`max(color, 0)`)、(c) densification 用 `refine` 勾配 (screen-space grad norm) は持たない — 本実装にその概念が無いため。数式は brush ではなく **既存 per-pixel kernel と完全に同一のものを使用** (v_rgb \u002F v_alpha \u002F v_color_a \u002F v_conic \u002F v_xy の式、`raw_alpha \u003C 0.999` の分岐条件含め全て一致)。`splats_grad` layout も brush の 10-component (refine 込み) ではなく既存の 9-component (`[N*9]`) のまま",{"key":76,"value":77},"last_contributor を使わない設計判断","forward が保存する `last_contributor` は「1 thread = 1 pixel」構造の loop 打ち切りに使う値で、per-splat 構造 (全 thread が同じ `total_iters` を回す必要がある barrier 制約) では pixel ごとに独立した loop 境界を持てない。代わりに pixel の running T が `next_T \u003C= 1e-4` で sentinel 0.0 に落ち、以降 `T > 1e-4` 判定で自動的に非活性化する — forward の早期終了条件を replay 中に再現するだけなので数学的に同値 (2-BWD 節の論証と同型)。**milestone spec の「last_contributor はそのまま利用」からの逸脱**だが機能的に等価 (brush 自身も同じ理由で使っていない)。**下記 §4 の regression 分析ではこれが疑わしい一因** — per-pixel 系は last_contributor で loop bound 自体を縮められるが、per-splat 系は全 thread 共通の `total_iters` (= num_splats_this_batch + 255) を縮められず、密な重畳領域で無駄な iteration が積み上がる",{"key":79,"value":80},"occupancy","32-thread\u002Fthreadgroup dispatch のため `Device::pso_min256` (256-thread floor、コンパイラ規約 C6) の対象外と判断し、floor なしの `pso_with_constants` を使用。理由をコード doc に明記済み。**この 32-thread dispatch 自体も regression の一因の可能性** — 他 kernel の 256-thread dispatch に比べて 1 threadgroup あたりの並列度が低く、GPU core あたりの occupancy が下がっている可能性がある (未検証、下記 §5 参照)",{"type":61,"text":82},"2. 正しさの証拠 (1) — unit-level parity",{"type":84,"columns":85,"align":89,"rows":91},"table",[86,87,88],"test","何を pin するか","結果",[90,90,90],"left",[92,96,100,103],[93,94,95],"`backward_per_splat_byte_equality_single_pixel`","dldr を 1 pixel だけ非ゼロにして summation 順序の影響を消した条件で per_splat vs `Original`(FullRange,Off) が bit 一致","**PASS**",[97,98,99],"`backward_per_splat_matches_per_pixel_original`","32×32 (OOB pixel なし) \u002F 24×24 (tile 境界からはみ出す OOB pixel あり) の 2 fixture、multi-pixel (24 splat 重畳) で `abs_tol\u002Frel_tol = 1e-4` dual-threshold","**PASS** (max_rel 2.47e-5 @ 32×32 \u002F 7.4e-6 @ 24×24)",[101,102,95],"`rasterize_psos_compile`","MSL コンパイル + 全 PSO 構築 (`rasterize_backwards_per_splat` 含む)",[104,105,106],"workspace 全体 (`cargo test --release`)","splat-metal 80 + splat-train-v1 70 + splat-cli 8、per_pixel default (既存挙動) が無変更であること","**PASS \u002F failure 0**",{"type":108,"label":109,"variant":110,"text":111},"callout","milestone spec の閾値「相対誤差 ≤ 1e-5」は達成不能と判明 — reference 自体が 4.54e-5 の self-noise を持つ","warning","実装直後の multi-pixel parity test は `rel_tol = 1e-5` で **FAILED** した (32×32 fixture、`splats_grad[91]`: ref=0.028393194, per_splat=0.028392494, diff=7.0e-7, \u003Cstrong>rel=2.47e-5\u003C\u002Fstrong>)。原因を切り分けるため `Original`(FullRange,Off) kernel を \u003Cstrong>同一 input・同一 dispatch で 20 回連続実行\u003C\u002Fstrong>し run-to-run の自己差を測定したところ、\u003Cstrong>max_rel 4.54e-5 (32×32) \u002F 3.95e-6 (24×24)\u003C\u002Fstrong> という self-noise が確認できた — `atomic_fetch_add_explicit` の実行順序が dispatch ごとに変わるため、`Original` 自身が「決定的な真値」ではなく毎回わずかに違う値を返す乱数的な reference だったことになる。per_splat vs `Original`(1 サンプル) の実測差 (2.47e-5 \u002F 7.4e-6) は\u003Cstrong>この self-noise 床より小さい\u003C\u002Fstrong>ので、per_splat の状態機械・数式は正しく (単一 pixel fixture の bit 一致がこれを別途直接証明する)、観測された差は純粋に summation 順序に由来する。\u003Cstrong>`shader_fast_math=false` にしても改善しない\u003C\u002Fstrong> (32×32 で逆に max_rel 7.4e-5 に悪化、24×24 では 7.6e-6 に改善 — 一貫した方向性がなく、summation 順序そのものの非結合性に起因することを裏づける)。以上から \u003Cstrong>1e-5 の閾値を file 内の既存 backward parity test 群と同じ `abs_tol\u002Frel_tol = 1e-4` dual-threshold に変更\u003C\u002Fstrong>し、実測値に十分なマージンを残して pin した。",{"type":61,"text":113},"3. 正しさの証拠 (2) — trainer 統合レベル (`splat bench --parity`、実 checkpoint 409,660 splats)",{"type":84,"columns":115,"align":120,"rows":122},[116,117,118,119,88],"grad tensor","tolerance","max_abs_diff","n (要素数)",[90,90,121,121,90],"right",[123,129,134,137,141,145,149,154],[124,125,126,127,128],"`loss_value`","—","1.006e-7","1","参考値 (diff のみ)",[130,131,132,133,95],"`means_grad`","atol=1e-4 rtol=1e-4","2.811e-5","1,228,980",[135,131,136,133,95],"`log_scales_grad`","5.808e-8",[138,131,139,140,95],"`rot_quats_grad`","2.127e-7","1,638,640",[142,131,143,144,95],"`raw_opacities_grad`","9.005e-8","409,660",[146,131,147,148,95],"`sh_coeffs_grad`","1.558e-7","19,663,680",[150,151,152,153,95],"`tile_offsets`","byte equality","0","2,500",[155,125,125,125,95],"**PARITY OVERALL**",{"type":108,"label":157,"variant":158,"text":159},"full-scale parity は unit test より強い証拠 — 実 checkpoint・real dataset・全 downstream grad tensor","success","`splat bench --config configs\u002F2026-08-30-m3-bench-perpixel-final.toml --parity configs\u002F2026-08-30-m3-parity-persplat-candidate.toml` で、Lego \u003Cstrong>final checkpoint (409,660 splats、sh_degree=3、800×800)\u003C\u002Fstrong> の 1 iteration を \u003Ccode>backward_scheme = per_pixel\u003C\u002Fcode> (baseline) と \u003Ccode>per_splat\u003C\u002Fcode> (candidate) の両方で実行し、project_backwards を経由した \u003Cstrong>末端の全 param grad\u003C\u002Fstrong> (means \u002F log_scales \u002F rot_quats \u002F raw_opacities \u002F sh_coeffs、合計 2300 万要素超) を比較した。**全項目 PASS**、tolerance は既存 backward variant 比較 (`BackwardKind::Tg` の文書化済み parity 記録 \u003Ccode>max_abs_diff 2.811e-5\u003C\u002Fcode>) と同一の `abs=1e-4 rtol=1e-4`。\u003Cstrong>means_grad の max_abs_diff (2.811e-5) が `Tg` の記録値と偶然一致\u003C\u002Fstrong>している点は、同じ synthetic な summation-order noise の量が同じ checkpoint\u002Fview に対して同じオーダーで出ることの傍証。`sorted_idx` のみ「INFO」(tie-order 非決定性、0.225% の要素が入れ替わる — これは既存の radix sort tie-break の非決定性であり per_splat 固有ではない、gated 対象外)。",{"type":108,"label":161,"variant":158,"text":162},"副次効果 (milestone 記載): atomic 順序依存の消失を self-noise 測定で確認","milestone spec は per_splat の副次効果として「atomic 順序依存が消え backward が決定的になる」ことを挙げていた。`Original` の self-noise 測定 (§2 callout、20 回連続 backward、max_rel 4.54e-5 @ 32×32) と同じ手法を per_splat 自身に適用したところ、\u003Cstrong>max_rel 5.14e-6 (32×32) \u002F 完全 bit-exact (24×24、20 回とも 1 回目と全要素一致)\u003C\u002Fstrong> という結果になった。32×32 では 0 にならない (splat が複数 tile に跨る synthetic fixture 構造上、tile 間の atomic 加算が 4 回残るため) が、\u003Cstrong>atomic 版 `Original` の 1\u002F9 以下\u003C\u002Fstrong>まで縮小している。24×24 (OOB pixel あり、実効的な重畳が少ない fixture) では完全に決定的になった。**この副次効果は実証されたが、性能 regression と相殺してもなお default 採用を正当化しない**という判断に変わりはない。",{"type":61,"text":164},"4. 性能 — bench replay 3-way、per_splat は「不公平な比較」を除いても +16.5% 遅い",{"type":84,"columns":166,"align":171,"rows":172},[167,168,169,170],"指標 (Lego final checkpoint 409,660 splats、800×800、warmup=10 iters=50 repeats=3、pipelining=off)","per_pixel default (simd+warp_any+lc)","per_pixel unopt (original+full_range+off)","per_splat",[90,121,121,121],[173,178,183,188,193,198,203,206],[174,175,176,177],"**ms\u002Fiter (median)**","**15.354**","17.681","**20.594**",[179,180,181,182],"ms\u002Fiter (mean)","15.432","17.706","20.602",[184,185,186,187],"σ (n=3)","1.08%","0.31%","0.17% (3 arm とも安定)",[189,190,191,192],"**GPU busy (ms\u002Fiter)**","**12.499**","14.914","**17.753**",[194,195,196,197],"GPU busy%","81.3%","84.2%","86.1%",[199,200,201,202],"sync-tax (ms\u002Fiter)","2.871","2.794","2.869 (3 arm ともほぼ不変)",[204,125,125,205],"**Δ per_splat vs default**","**+34.1% wallclock \u002F +42.0% busy**",[207,125,125,208],"**Δ per_splat vs unopt**","**+16.5% wallclock \u002F +19.0% busy**",{"type":108,"label":210,"variant":110,"text":211},"regression の性質 — 「待ち時間の付け替え」でも「比較が不公平」でもなく、kernel 実行そのものが遅い","3 arm とも sync-tax (host が GPU 完了を待つオーバーヘッド) は 2.79〜2.87 ms\u002Fiter で \u003Cstrong>ほぼ完全に一致\u003C\u002Fstrong>している。一方 GPU busy ms\u002Fiter は 12.499 → 14.914 → 17.753 と単調に増え、\u003Cstrong>per_splat は「最適化を一切持たない unopt per_pixel」と比べても純粋な GPU 実行時間の増加\u003C\u002Fstrong>として現れる。当初の懸念 (「default は Simd+WarpAny+LastContributor という 3 つの勝者施策を積んでいるので比較が不公平ではないか」) は unopt arm の追加で解消した — **どちらの baseline に対しても per_splat は遅い**。つまりこれは encode\u002Fsync 構造の劣化でも「不利な baseline 選択」でもなく、\u003Cstrong>`rasterize_backwards_per_splat` の transposed loop nest (1 threadgroup=1 tile、1 thread=1 splat、32-thread dispatch) という構造そのものが Apple GPU 上で `rasterize_backwards`\u002F`_simd` の構造 (1 thread=1 pixel、256-thread dispatch) より遅い\u003C\u002Fstrong>ことを示す。brush が自身の実測 (WebGPU\u002Fcubecl 経由、非 Apple 環境を含む) で backward -25% を報告しているのとは逆方向の結果になった。",{"type":61,"text":213},"5. regression の原因 (仮説、未検証 — 次段の調査候補)",{"type":215,"items":216},"list",[217,218,219,220,221],"**32-thread\u002Fthreadgroup の低 occupancy (最有力仮説)**: 他 kernel (forward\u002FSimd\u002FTg\u002Funopt Original 含む) はすべて 256-thread\u002Fthreadgroup で dispatch し `pso_min256` の occupancy floor を満たす。per_splat は 1 SIMD group (32 thread) だけを 1 threadgroup に割り当てるため、同じ tile grid (`tile_bounds_x × tile_bounds_y` 個の threadgroup) に対して**1 threadgroup あたりの並列度が 1\u002F8**、GPU core あたりの in-flight thread 数もそれに応じて減り、メモリレイテンシを隠す余地が構造的に小さい。unopt per_pixel (256-thread) 比でも +16.5%〜19.0% 遅いという結果は、この occupancy 差が W1a\u002FLastContributor の有無とは独立に効いていることを示唆する","**diagonal scheduling の `threadgroup_barrier` オーバーヘッド**: brush のコメントは「32 = 1 SIMD group なので barrier は lockstep no-op に近い」と主張するが、Apple GPU の `threadgroup_barrier` が本当に無視できるコストかは本実装では未検証。1 round あたり最大 287 回の barrier が発生し、tile あたりの round 数 (= splat 数 \u002F 32) が多い密な tile ではこれが積み上がる。brush の実測環境 (WebGPU\u002Fcubecl、必ずしも Apple Silicon 単体ではない) と本実装の直接 Metal 実装で barrier コストの実態が異なる可能性がある","**last_contributor による loop bound 短縮を失っている**: `raster_backward_range = last_contributor` は pixel ごとに loop 打ち切りができる (12.5k gate で -14.98% の実績、`p2-last-contributor`) が、per_splat は全 thread 共通の `total_iters = num_splats_this_batch + 255` を縮められない (barrier の uniform 実行制約)。ただし **unopt per_pixel (last_contributor 無し) 比でも per_splat は +16.5% 遅い**ので、これだけでは regression の全量を説明できない — 上位 2 仮説 (occupancy \u002F barrier) の寄与のほうが大きいと考えられる","**W1a warp sparse skip (exp-skip + `simd_any`) を失っている**: 12.5k gate で backward -35.2% (`p2-w1-warp-sparse-skip`) の実績を持つが、これも unopt per_pixel には無いので上と同じ理由で「regression の主因ではない」候補","**切り分けの次段**: (a) `[backend] profile = true` の 1k smoke で `staticThreadgroupMemory` \u002F occupancy ログを比較する、(b) Xcode GPU capture で `rasterize_backwards_per_splat` の実行時間・occupancy% を直接見る、(c) threadgroup あたりの thread 数を 32 から 64\u002F128\u002F256 (複数 SIMD group、`SPLAT_BATCH` を増やす) に変えて occupancy 仮説を直接検証する。**注意 (c への caveat)**: `SPLAT_BATCH` を 32 より大きくすると diagonal scheduling の `threadgroup_barrier` が「1 SIMD group 内の lockstep no-op」で済まなくなり (複数 SIMD group 間の実 barrier になる)、occupancy 仮説と barrier オーバーヘッド仮説が同時に動いてしまう — (c) は「occupancy 単体の検証」にはならず「occupancy を上げた代わりに barrier を実物化した場合の net 効果」を見る実験になる点に注意 (それでも net で速くなれば十分に有用な結果ではある)。**本 draft のスコープでは打ち切り** (3-way bench で regression の実在と大まかな帰属先 (occupancy 濃厚) までは確定させたが、確定的な原因特定は次段に委ねる)",{"type":61,"text":223},"6. 未解決点・次段への引き継ぎ",{"type":215,"items":225},[226,227,228,229],"**12.5k trainer 統合 gate は見送った** — bench 3-way (default 比 +34.1%、unopt 比 +16.5%) の時点で明確かつ「比較の不公平さ」では説明できない regression が出ているため、12.5k full run (数分〜十分オーダーの GPU 専有) を追加で消費する前に main の判断を仰ぐべきと考えた。gate 用 config 6 本 (`2026-08-30-m3-12500-{perpixel,persplat}-r{1,2,3}.toml`) は `splat check` で deserialize 確認済みのまま残してある — regression の原因究明 (上記 §5) 後に再実装してから使うか、現状のまま「regression の再確認」として走らせるかは main の判断","**regression の確定的な原因は未特定** (§5 の仮説、最有力候補は 32-thread dispatch の低 occupancy — last_contributor \u002F W1a の有無だけでは unopt per_pixel 比 +16.5% を説明できないため)。次に per-splat backward に再挑戦する場合は、まず `SPLAT_BATCH` (現状 32) を増やして occupancy を上げる実験から始めるのが筋が良さそうに見える","**2026-08-30 追記**: 上記の実験は `p3-per-splat-backward-tg256.toml` (M3b) として実装した (`rasterize_backwards_per_splat_tg256`、`PER_SPLAT_BATCH` 32→256)。**ただし GPU 検証 (parity・bench) は同一 session 中に走っていた 12.5k gate (M2 Tier1) との衝突回避のため未実行のまま持ち越し** — GO\u002FNO-GO 判定は次段の GPU session 待ち。当該 finding は「barrier 総数 4.5 分の 1」「thread-iteration 効率 1.78 分の 1 に悪化」「barrier 単価上昇」の 3 効果が 1 本の実験に同居することも指摘しており、tg256 が負けても『occupancy 仮説の反証』にはならない点に注意","**M1 (C1 per-kernel 再計測) との整合確認が必要** — 本 finding 作成時点で M1 の完了報告をまだ受け取っていない。M1 の share 実測にかかわらず、本 finding の regression 結果は独立に成立する (share が高いほど「遅い kernel に置き換える」ことの機会損失も大きくなるため、むしろ M1 の share が高いほど本 regression の重要度は上がる)",{"type":61,"text":231},"7. ファイル一覧 (このセッションの変更)",{"type":64,"items":233},[234,237,240,243,246,249,252],{"key":235,"value":236},"kernel","`splat\u002Fshaders\u002Fbackward\u002Frasterize_backwards.metal` — `rasterize_backwards_per_splat` を追加 (既存 3 kernel は無変更)",{"key":238,"value":239},"Rust dispatch","`splat\u002Fcrates\u002Fsplat-metal\u002Fsrc\u002Fkernels\u002Frasterize.rs` — `BackwardSchemeKind` enum、`pso_backward_per_splat`、`backward_per_splat_from_buf`、parity test 2 本、`synthetic_overlap_fixture_sized`",{"key":241,"value":242},"config","`splat\u002Fcrates\u002Fsplat-train-v1\u002Fsrc\u002Fconfig.rs` — `BackendConfig.backward_scheme` field、`BackwardSchemeKind` enum、`validate()` warning",{"key":244,"value":245},"wiring","`splat\u002Fcrates\u002Fsplat-cli\u002Fsrc\u002Futil.rs::apply_backend_config`、`splat\u002Fcrates\u002Fsplat-train-v1\u002Fsrc\u002Ftrainer.rs` (Trainer field + 2 dispatch call site)",{"key":247,"value":248},"bench config","`splat\u002Fconfigs\u002F2026-08-30-m3-bench-{perpixel,perpixel-unopt,persplat}-final.toml` + `2026-08-30-m3-parity-persplat-candidate.toml` (実行済み、結果は `splat\u002Fruns\u002Fbench-m3\u002F*.toml`、gitignore 対象なので repo には残らない — 数値は本 finding に転記済み)",{"key":250,"value":251},"gate config (12.5k、run 未実施)","`splat\u002Fconfigs\u002F2026-08-30-m3-12500-{perpixel,persplat}-r{1,2,3}.toml` (6 本)",{"key":253,"value":254},"commit","`f08a7c8` (kernel skeleton + wiring, GPU 未検証) → `f6c7cdb` (parity test、GPU 実行確認済み) → 本 finding + bench 実行 (次 commit) — worktree `worktree-agent-a68a654f076a906ea`",[],[257,272,304,324,347,368],{"id":49,"title":258,"date":9,"status":10,"polarity":12,"category":11,"axes":259,"tags":260,"task_code":264,"related_runs":265,"delta_psnr":266,"delta_wallclock":267,"rank":268,"verdict":269,"impact_summary":270,"detail_path":271},"M3b per-splat backward 第 2 版 (256-thread threadgroup) — NO-GO 確定 (occupancy 仮説は方向的に正しいが不十分)",[14,15],[17,261,19,79,262,21,22,263],"m3b","threadgroup-size","gpu-pending","2026-09-seminar05-milestones.md — M3 追補 (per-splat backward 第 2 版、occupancy 仮説検証)",[],"N\u002FA (bench のみ、12.5k gate 未実施。trainer 統合 parity は5 grad tensor 全て tol 内で PASS — correctness は確認済み)","tg256 vs default per_pixel: **+32.8%** (劣後、bench median 17.994ms vs 13.548ms, n=3)。tg256 vs per_splat v1: -4.22% (occupancy 仮説の方向は正しい)。12.5k gate 未実施 (bench 時点で NO-GO 確定のため)","mid","no-go-confirmed","第 1 版 finding (`p3-per-splat-backward.toml` §5) の occupancy 仮説を検証するため、`rasterize_backwards_per_splat_tg256` (`BackwardSchemeKind::PerSplatTg256`、flag `per_splat_tg256`) を追加した。**アルゴリズム面では第 1 版と一字一句同一** (diagonal scheduling、pixel 側 state 復元、last_contributor を使わない理由、gradient 式) で、変えたのは `PER_SPLAT_BATCH` (= threadgroup thread 数) の 1 パラメータのみ (32→256)。GPU 検証: (1) `rasterize_psos_compile` PASS — `pso_min256` の 256-thread occupancy floor をレジスタ圧迫なしに達成。(2) parity 3 本 (`backward_per_splat_tg256_matches_per_pixel_original` [32×32\u002F24×24]、`_multi_round` [n=600, rounds=3]、`_byte_equality_single_pixel`) 全 PASS。(3) trainer 統合レベル `splat bench --parity` (実 checkpoint 409,660 splats) — 5 grad tensor 全て tol (atol\u002Frtol=1e-4) 内で PASS、`sorted_idx` は既知の非決定性 (0.222% 差、非 gate)。(4) 4-way bench (median ms\u002Fiter, n=3, warmup=10\u002Fiters=50\u002Frepeats=3): per_pixel (default, σ0.59%) **13.548** \u002F per_pixel unopt (σ0.34%) 15.701 \u002F per_splat v1 (σ0.12%) 18.787 \u002F **per_splat_tg256 (σ0.35%) 17.994**。tg256 は v1 比 **-4.22%** — §3 が予告した 3 効果 (barrier 総数 4.5 分の 1 \u002F thread-iteration 効率 1.78 分の 1 悪化 \u002F barrier 単価上昇) の net が僅かに改善方向に振れたことを示し、occupancy 仮説は**方向としては裏付けられた**。しかし default per_pixel 比では **+32.8%** で依然として大幅に劣後しており、M3 系列 (per-splat backward、atomic なし構造) はこの bench harness 上で **NO-GO 確定**。12.5k gate は実施していない (bench の時点で `max(2σ,1%)` の閾値を大幅に超えて劣後することが明白なため、instructions の「遅ければ gate せず終了」に従った)。`BackwardSchemeKind` の default は `PerPixel` のまま、`.rs` は無変更。","\u002Ffindings\u002Fp3-per-splat-backward-tg256\u002F",{"id":46,"title":273,"date":274,"status":10,"polarity":275,"category":11,"axes":276,"tags":277,"task_code":287,"related_runs":288,"delta_psnr":299,"delta_wallclock":300,"rank":52,"verdict":301,"impact_summary":302,"detail_path":303},"S3 iteration pipelining depth 2 — 12.5k -18.1% \u002F busy 78 → 91% (default flip)","2026-08-17","positive",[14,15],[278,279,280,281,282,283,284,285,27,286],"1x-roadmap-v2","s3","iteration-pipelining","command-buffer","gpu-busy","sync-tax","double-buffering","scratch-generation","unified-memory","2026-08 1× 工程表 v2 — AC 復帰後 計測 queue #2' (S3 gate)",[289,290,291,292,293,294,295,296,297,298],"s3-smoke-1000-off","s3-smoke-1000-depth2","s3-12500-off-prof","s3-12500-depth2-prof","s3-12500-off-r1","s3-12500-off-r2","s3-12500-off-r3","s3-12500-depth2-r1","s3-12500-depth2-r2","s3-12500-depth2-r3","12.5k **同一 session 交互 A\u002FB** n=3 mean: depth2 **36.202 dB** (36.195 \u002F 36.179 \u002F 36.231) vs off **36.185 dB** (36.178 \u002F 36.192 \u002F 36.186) = **+0.016 dB** (guard 36.167 ± 0.1 の内側、n=3 mean 判定 — n=1 判定なし)。final splats は depth2 406,439 \u002F off 408,874 で **depth2 のほうが 0.6% 少ない** (前段の profile pair では逆に +0.6% で、方向が一致しない = 既存 atomic 非決定性の chaos 増幅の範囲)","12.5k full run **同一 session 交互 A\u002FB n=3 mean: 185.21 → 151.70s = -18.1%** (train elapsed)。process wallclock では **188.33 → 154.00s = -18.2%**。σ は off 3.33% \u002F depth2 1.18% なので有意閾値 max(2σ,1%) = 6.7%、実測 Δ は**その 2.7 倍**。同一 session の off arm は S2 gate 当日の S2 arm (train 184.61s \u002F process 187.65s) を **+0.3% で再現**しており、session の健全性が確認できている。参考: profile=true の 1 対 1 比較では 180.49 → 162.72s = -9.85% (計装自体が host 側 critical path に乗るため S3 の効果を過小評価する)","s3-adopted-default-flip-pipelining-depth2","1× 工程表 v2 が「S2 後の単独本命」に指定していた S3 を gate し、採用した。S2 までの施策が \u003Cstrong>CB の数\u003C\u002Fstrong>を減らすものだったのに対し、S3 は \u003Cstrong>CB の並び方\u003C\u002Fstrong>を変える — iteration N の loss\u002Fbackward CB (B\u003Csub>N\u003C\u002Fsub>) を commit したら wait せずに iteration N+1 の forward CB (F\u003Csub>N+1\u003C\u002Fsub>) を組んで commit し、\u003Ccode>wait(B\u003Csub>N\u003C\u002Fsub>)\u003C\u002Fcode> の後の host readback \u002F \u003Ccode>accumulate\u003C\u002Fcode> を F\u003Csub>N+1\u003C\u002Fsub> の GPU 実行の影に置く。\u003Cstrong>12.5k 同一 session 交互 A\u002FB n=3 で 185.21 → 151.70s = -18.1%\u003C\u002Fstrong> (閾値 max(2σ,1%) = 6.7% の 2.7 倍)。\u003Cstrong>GPU busy 77.5 → 91.3% \u002F sync-tax 3.244 → 1.136 ms\u002Fiter (-65%)\u003C\u002Fstrong> で counter session が置いた「busy 100% = 理論上限」への距離は 22.5 pt → 8.7 pt に縮んだ。\u003Cstrong>1k smoke では busy 65.8 → 72.0% にしか上がらず判定不能だった\u003C\u002Fstrong> — smoke の splat 数 6.8k では forward の GPU 影 (1.06 ms\u002Fiter) が image size 固定の host 仕事 (~1.7 ms\u002Fiter) を隠しきれないためで、これは \u003Cstrong>「pipelining の gate は smoke では代替できない」という harness 上の教訓\u003C\u002Fstrong>として残す。残 bubble の内訳も確定した: \u003Cstrong>refine tick の drain 6.04s (43%) &gt; encode(B) 4.66s (33%) &gt; 残差 3.28s (23%)\u003C\u002Fstrong> — 次の一手が roadmap の「即効ペア (refine capacity スコープバグ修正)」であることが実測で決まった。msplat 同日等価 gap \u003Cstrong>1.105× → 0.907×\u003C\u002Fstrong>、Phase 0 からの累計 766.07 → 154.0s = \u003Cstrong>-79.9%\u003C\u002Fstrong> (全て数学的同値の施策のみ)。","\u002Ffindings\u002Fp2-s3-iteration-pipelining\u002F",{"id":48,"title":305,"date":306,"status":10,"polarity":275,"category":307,"axes":308,"tags":309,"task_code":317,"related_runs":318,"delta_psnr":-1,"delta_wallclock":320,"rank":52,"verdict":321,"impact_summary":322,"detail_path":323},"splat bench harness 実装 + calibration — σ_replay 0.83%、full run 比 ~6x ノイズ削減","2026-08-16","tooling",[14,15],[310,311,312,313,314,315,316,27],"bench-harness","calibration","variance","reproducibility","c7","fixed-workload","gate-infra","splat bench harness (2026-08-16 GPU 専有タスク)",[319],"lego-bench-calib-checkpoints","σ_replay 0.83% (n=10, final-scale workload) vs full run 実測 ~10% — 約 6x ノイズ削減","accepted","`splat bench --config bench.toml` を実装し、同一 config n=10 replay で \u003Cstrong>σ_replay = 0.234 ms\u002Fiter (mean 28.339 ms 比 0.83%)\u003C\u002Fstrong> を実測。full training run で観測されていた ~10% 分散に対し \u003Cstrong>約 6x のノイズ削減\u003C\u002Fstrong>。3-regime (iter 1000 \u002F 5000 \u002F final checkpoint、各 n=5) でも σ 1.2-4.6% の範囲に収まり、splat 数が少ない早期 regime ほど相対分散が大きい傾向を確認。有意判定閾値を \u003Ccode>max(2σ, 1%)\u003C\u002Fcode> と規約化し、以後の gate 判定をこの harness の計測に載せ替える。付随して `--parity` (kernel variant 間の勾配 \u002F binning 一致検証) と `[output] save_ply_at` (checkpoint 保存) を実装、parity mode の実測で production-scale シーンの `sorted_idx` tie-order 非決定性 (~0.2%、真の非決定性であって variant 差ではない) を発見し gate から除外した。","\u002Ffindings\u002Fp2-bench-harness-calibration\u002F",{"id":47,"title":325,"date":306,"status":10,"polarity":275,"category":326,"axes":327,"tags":328,"task_code":336,"related_runs":337,"delta_psnr":-1,"delta_wallclock":344,"rank":52,"verdict":321,"impact_summary":345,"detail_path":346},"保持 flag 全数再 gate sweep — 8 維持 \u002F 1 flip (loss_path -26.5%) + bench validate 抜けを発見","performance",[14,15],[329,330,331,25,332,333,27,334,335],"flag-revisit","gate-sweep","loss-path","validate","regression-guard","stop-the-pop","default-flip","保持 flag 全数再 gate sweep (2026-08-16 GPU 専有タスク)",[338,339,340,341,342,343],"revisit-losspath-host-12500-r1","revisit-losspath-host-12500-r2","revisit-losspath-host-12500-r3","revisit-losspath-gpu-12500-r1","revisit-losspath-gpu-12500-r2","revisit-losspath-gpu-12500-r3","loss_path flip: replay -26.5% (final) \u002F -29.6% (iter5000)、12.5k gate -35.3% — ただし公式 config は既に gpu 明示のため公表値は不変 (hygiene flip)","\u003Ccode>BackendConfig\u003C\u002Fcode> 全 16 field を機械走査し、default 以外の variant を全数再判定。実測 9 件のうち \u003Cstrong>8 件は verdict 維持\u003C\u002Fstrong>、\u003Cstrong>1 件 (\u003Ccode>loss_path\u003C\u002Fcode>) が flip\u003C\u002Fstrong>。\u003Ccode>loss_path = \"gpu\"\u003C\u002Fcode> は replay final regime \u003Cstrong>21.210 → 15.591 ms\u002Fiter (-26.5%)\u003C\u002Fstrong> \u002F iter5000 \u003Cstrong>-29.6%\u003C\u002Fstrong>、12.5k full gate (同一 session 交互 A\u002FB、n=3) で \u003Cstrong>283.00 → 183.00s (-35.3%)\u003C\u002Fstrong>・PSNR \u003Cstrong>36.142 → 36.188 dB (+0.047、guard ±0.1 内)\u003C\u002Fstrong>。ただし \u003Cstrong>この flip は公式数値を 1 つも動かさない hygiene\u003C\u002Fstrong> — 全 237 config が \u003Ccode>loss_path\u003C\u002Fcode> を明示指定済みで 12.5k gate 系は例外なく \u003Ccode>\"gpu\"\u003C\u002Fcode>、enum default だけが 2026-04-30 以来実運用から取り残されていた。残り 7 件は validate hard error による構造的非互換 (5) \u002F 禁忌リスト (2) \u002F harness 非対応 (1) \u002F 非 perf variant (3) として理由付きで判定不能を記録。副産物として \u003Ccode>splat bench\u003C\u002Fcode> の \u003Ccode>validate()\u003C\u002Fcode> 抜け (無効組合せが exit 0 で silent 別経路実行) を発見・修正した。","\u002Ffindings\u002Fp2-flag-revisit-sweep\u002F",{"id":45,"title":348,"date":306,"status":10,"polarity":275,"category":11,"axes":349,"tags":350,"task_code":357,"related_runs":358,"delta_psnr":363,"delta_wallclock":364,"rank":52,"verdict":365,"impact_summary":366,"detail_path":367},"P2 gap sweep Phase 2 — 2-BWD last_contributor で -15.0% (数学的同値)、STP 再判定は棄却",[14],[351,352,353,354,334,355,356,27],"gap-sweep","phase-2","2-bwd-last-contributor","backward","re-eval","function-constant","2026-08 msplat gap sweep Phase 2 2-BWD \u002F STP 再判定",[359,360,361,362],"lego-lc-smoke-1k","lego-p2lc-12500","lego-stp-c0-12500","lego-stack-lc-stp-12500","LC 12.5k gate: 36.183 → 36.191 dB = +0.008 dB (数学的同値実装、atomic 非決定性 guard ±0.1 内)","LC 12.5k gate: 395.59 → 336.33s = **-14.98%** (backward_raster -40.3%)。STP: -2.51% (gate 未達)、LC+STP スタック: 343.63s (LC 単独より +2.2%)","lc-adopted-minus-15-percent-stp-rejected-final","C0 (-48.4%) に続く Phase 2 第 2 弾で、backward 31.4% の新 bottleneck を kernel 側から -40.3% 削減。\u003Cstrong>Lego 12.5k = 336.33s で msplat 30K (220s) 比 1.53× まで縮小\u003C\u002Fstrong>し、gap sweep の目標 (≤2.0×) を達成した。実装は「forward が既に知っている打ち切り位置を backward に伝えるだけ」で数学的同値 — 品質を 1 bit も動かさずに backward の replay 計算量を ~40% 削った。STP 再判定は負の結果だが価値が高い: Phase 1a の -7.45% の正体が C0 と同じ page fault 削減だったことを分離実証し、「効果の重複を疑って再 bench する」protocol の正しさを裏付けた。次の bottleneck は forward rasterize 9.8% \u002F radix 13.5% \u002F SSIM 7.5% でいずれも trigger 25% 未達 — Phase 2 はここで完了、Phase 3 (再計測 + Pareto v3) へ。","\u002Ffindings\u002Fp2-last-contributor\u002F",{"id":44,"title":369,"date":306,"status":10,"polarity":275,"category":11,"axes":370,"tags":371,"task_code":378,"related_runs":379,"delta_psnr":383,"delta_wallclock":384,"rank":52,"verdict":385,"impact_summary":386,"detail_path":387},"W1 warp sparse skip + exp-skip — backward -35% \u002F replay -11.2% (bit-exact)",[14],[278,372,373,374,354,375,376,356,377,27],"w1","warp-sparse-skip","exp-skip","forward","simd-any","bit-exact","2026-08 1× 工程表 v2 W1",[380,381,382],"lego-w1-warpany-12500","lego-w1-warpany-12500-rep2","lego-w1-warpany-12500-rep3","12.5k gate n=3: 36.209 \u002F 36.201 \u002F 36.193 dB → mean 36.201 dB = 基準 36.148 (S1 n=3 mean) 比 +0.053 dB (guard ±0.1 内、n=3 mean 判定 — S1 の教訓に従い n=1 判定なし)","replay bench final regime: 21.724 → 19.300 ms\u002Fiter = **-11.16%** (閾値 max(2σ,1%)=2.7%)。iter5000 regime: 19.984 → 17.759 = **-11.13%** (閾値 3.1%)。backward_raster: 7.183 → 4.652 ms\u002Fcall (**-35.2%**、GPU 部 5.963 → 3.437 = -42.4%)。12.5k full run n=3: 239.74 \u002F 246.93 \u002F 237.57s → mean **241.41s** (S1 基準 261.55s 比 **-7.7%**)","w1-adopted-default-flip-warp-any","1× 工程表 v2 の第 1 波。msplat \u002F Inria \u002F brush の 3 実装が持ち本実装だけ欠落していた backward の 2 つの skip を、bit-exact の等価性論証つきで移植した。backward_raster は LC (2-BWD) 後の 7.2ms\u002Fcall から 4.65ms\u002Fcall へ -35.2% (kernel share 13.1% に低下)、replay 全体で -11.2%。12.5k full run は 261.55 → 241.41s で msplat 同日等価 gap は 1.56× → 1.44× に縮小。数学的同値の施策のみの累計は 766.07 → 241.41s = -68.5% になった。次の律速は forward (23.6%) \u002F radix (15.0%) で、W2 (encoder\u002FCB 統合) と計測 (counter session) が次段。","\u002Ffindings\u002Fp2-w1-warp-sparse-skip\u002F",1790227327656]