worktree (<code>worktree-agent-afb59d93b2c731864</code>) で実装済みの S1 (pipeline 全域 resident 化: Tier A resident work buffers / Tier B ForwardState Buffer handle + *_from_buf 直結 / Tier C zero_grads スコープ化 + AdamUniforms 統合) を main へ統合し GPU gate を完遂。C3 統合 (32070c4) との conflict は tile_bin.rs を手 merge し、C3 (rejected flag) 併用時は S1 buffer 直結を legacy 経路へフォールバックさせる regression test 込みで解決。fixed-workload replay bench で <strong>final regime 28.630→22.059 ms/iter = -22.9%</strong> / iter5000 regime -21.9% (閾値 max(2σ,1%) の 10 倍超の有意差)、attribution 計測で alloc 成分の消滅 (ts_raster_fwd_alloc 0.2 µs/call 等) を確認。12.5k full gate は rep1 PSNR 36.051 dB が guard 外に出たため n=3 escalation → rep2 36.168 / rep3 36.224 dB で tail variance と判定し PASS。wallclock は 12.5k rep mean 261.55s (rep1 272.36 / rep2 257.55 / rep3 254.73s、公式 336.33s 比 -22.2%) で replay 判定と整合。<strong>採用 — flag なしの無条件 default</strong>。msplat 同日等価 gap は 2.10× → <strong>1.56×</strong>。
C0 (finding: p2-c0-resident-binning) は binning 経路の per-iter alloc を resident arena 化して -48.4% を得たが、pipeline の残り (project / rasterize forward / rasterize backward / project_backwards / adam) には per-iter fresh-alloc (~46MB/iter) と host Vec 往復 (~57MB/iter) が残っていた。別 worktree (worktree-agent-afb59d93b2c731864、分岐元 b053e35) で先行実装されていた S1 (Tier A/B/C の 3 層 + DispatchAttribution 計測 4 点) を main へ統合し、GPU gate を完遂した。
| Tier | 内容 | 撤廃対象 |
|---|---|---|
| A (resident work buffers) | `GpuProjector` の出力 (splats2d/depths)、`Rasterize` の forward 出力 (out/final_t/last_contrib) と backward splats_grad を struct 保持の resident buffer に変更 (capacity-grow のみ、`ssim.rs::ensure_work_buffers` と同型)。memset は backward grad の active 範囲 (`n*9*4`) のみ | ~46MB/iter の fresh-alloc + first-touch page fault (C0 が binning で実証した主犯の一般化) |
| B (Buffer handle 直結) | `ForwardState` に `splats2d_buf` / `sorted_idx_buf` / `tile_offsets_buf` / `final_t_buf` の handle (arena buffer の retain) を保持し、backward 系を `backward_from_buf` / `dispatch_from_buf_active` に直結。非 batched 経路 (per_stage default) も `backward_rasterize_buf_raw` → `project_backwards_from_buf_active` で splats_grad の Vec→GPU re-upload を撤廃。BatchedTail 経路の「buffer を作っても捨てていた」未完成部も修正 | ~57MB/iter の host Vec → GPU 再 upload (splats2d 最大 ~14.7MB/枚 ほか) |
| C (小物 alloc + memset) | `zero_grads` のクリア範囲を capacity → active num_splats に縮小 (12.5k 規模で ~59% memset 削減、`ts_zero_grads` として計測化)。`adam_step` の 6 scalar buffer を `AdamUniforms` 1 struct に統合 (5 group × 6 → 5 alloc/iter、adam.metal の binding 変更) | 30 小 alloc/iter + capacity 全域 memset |
| 項目 | 内容 |
|---|---|
| worktree | `worktree-agent-afb59d93b2c731864` (分岐元 b053e35)。main は C3 統合 (32070c4) + docs (e4db3f4) の 2 commit 先行 |
| conflict | tile_bin.rs のみ (C3: dispatcher + per-tile kernels / S1: `resident_tile_offsets_buf` accessor)。他 7 ファイルは `git apply` がそのまま通った |
| C3 相互作用 (手 merge で発見・解決) | S1 の buffer 直結 forward は `resident_sorted_bufs()` を呼ぶが、C3 経路の sorted idx は arena の idx_a/idx_b ではなく別 buffer (idx_c3) にあり、C3 の panic guard に当たる (pre-S1 は per_stage + C3 が有効な組み合わせだった)。`GpuTileBinner::last_sort_used_c3()` accessor を追加し、C3 iter は buffer 直結を諦めて legacy host-slice forward にフォールバック (pre-S1 と同一挙動)。regression test `forward_state_c3_tile_counting_falls_back_to_host_slice_path` を追加 |
| MSL smoke compile | adam.metal の binding 変更 (buffer(4..9) → `AdamUniforms` struct buffer(4)) を `cargo test -p splat-metal adam` (= `Adam::new` + `device_compiles_adam_metal`) で検証、一発 PASS |
| test | S1 新規 test 9 本 (project 2 / rasterize 3 / trainer 3 + C3 fallback 1): resident byte-stability (repeat call bit-exact) / grow path (img_size・num_splats 増減) / ForwardState buffer handle と host Vec の bit-exact parity / `backward_rasterize_buf` Vec 版 vs raw Buffer 版 parity。workspace 全 test pass、`cargo build --release` warning 0 |
統合前に同一 machine/同日で baseline を取り直し (`final-pre` 28.630 ms/iter は calibration headline 28.312 ms/iter と 1.1% 差 = σ 圏内で整合)、S1 適用 build で同一 config を再計測した。S1 は flag なしの無条件変更のため、pre/post の差分は build のみ。
| regime | n | variant | median ms/iter | σ_pct | delta | 閾値 max(2σ,1%) | 判定 |
|---|---|---|---|---|---|---|---|
| final (407,267 splats) | 3 | pre (e4db3f4) | 28.630 | 1.02% | — | — | — |
| final | 3 | post (S1) | 22.059 | 0.22% | **-22.95%** | 2.05% | **有意 (採用)** |
| iter5000 (373,933 splats) | 3 | pre (e4db3f4) | 26.113 | 0.19% | — | — | — |
| iter5000 | 3 | post (S1) | 20.405 | 0.64% | **-21.86%** | 1.29% | **有意 (採用)** |
| timing label | pre avg_us/call | post avg_us/call | delta | S1 の対応 Tier |
|---|---|---|---|---|
| ts_fwd_project | 1,786.8 | 739.5 | **-58.6%** | A (out2d/depths resident 化) |
| ts_project_back | 1,908.8 | 900.6 | **-52.8%** | B (splats_grad buffer 直結、Vec 再 upload 撤廃) |
| ts_fwd_rasterize | 3,923.3 | 2,318.7 | **-40.9%** | A+B (out/final_t/last_contrib resident + splats2d/sorted_idx/tile_offsets 直結) |
| ts_backward_raster | 8,939.5 | 7,104.5 | **-20.5%** | A+B (splats_grad resident + 入力 buffer 直結) |
| ts_adam | 2,643.6 | 2,591.2 | -2.0% | C (AdamUniforms 統合、小物) |
| ts_fwd_radix_sort | 5,239.3 | 5,144.1 | -1.8% | — (S1 対象外、不変であることの sanity) |
| ts_fwd_emit_pairs | 499.4 | 494.7 | -0.9% | — (C0 で resident 済、不変) |
S1 は `{upload, alloc, gpu, readback}` の 4 成分計測 (`DispatchAttribution`、emit_pairs の C0 Day 0 計測と同型) を project_soa / raster_fwd / raster_bwd / project_back の 4 dispatch に新設した。pre 側には計測が存在しない (計測自体が S1 の一部) ため、before は per-kernel 全体値 (§4) で、after は内訳で示す。
| dispatch (post、final regime) | upload µs/call | alloc µs/call | gpu µs/call | readback µs/call |
|---|---|---|---|---|
| ts_project_soa | 5.4 | **0.1** | 520.2 | 0 |
| ts_raster_fwd | 5.9 | **0.2** | 2,306.7 | 0 |
| ts_raster_bwd | 5.0 | 63.2 (※) | 5,912.2 | 0 |
| ts_project_back | 9.1 | **0.0** | 884.4 | 0 |
| run | PSNR (mean val, 100 views) | wallclock | final splats |
|---|---|---|---|
| rep1 (lego-s1-resident-12500) | 36.051 dB | 272.36s | 407,556 |
| rep2 | 36.168 dB | 257.55s | 406,321 |
| rep3 | 36.224 dB | 254.73s | 406,304 |
| **n=3 mean** | **36.148 dB** | **261.55s** | — |
rep1 の 36.051 dB は歴代同 recipe 7 run の分布 (36.138〜36.226、mean 36.185) の下に外れ、±0.1 dB guard を単発では割った。S1 は数学的同値 (ForwardState buffer handle の bit-exact parity / resident buffer の byte-stability を test で証明済み、非決定性は既知の atomic 加算順序のみ) のため systematic な品質劣化の機構がなく、run 分散の tail を疑って n=3 に escalation → rep2 36.168 / rep3 36.224 dB と分布内に戻った。n=3 mean 36.148 dB = 基準 36.19 比 -0.04 dB で guard 内 PASS。歴代最低値の単発観測を機に「PSNR guard も wallclock 同様 n=1 判定は分散に飲まれうる」ことを記録する (phase5 の -0.65 dB 撤回事例、full-run wallclock 分散 ~10% と同根)。
wallclock は rep mean 261.55s で、S1 前の公式 336.33s (p2-last-contributor) 比 **-22.2%**、同 recipe 当日近傍の calib run 360.96s 比 -27.5% — replay bench の -22.9% と方向・規模とも整合する (full run は分散 ~10% があるため参考値、gate 判定は replay を正とする)。
同日等価比較 (p2-msplat-parity-lego、msplat 本日実測 167.7s) の gap は本実装 352.27s 基準で 2.10× だった。S1 後の 12.5k は rep mean 261.55s のため、同 protocol 換算で gap は 1.56× に縮小 (公式値どうしの比較 336.33s→261.55s)。Phase 2 完了時の残 bottleneck (backward 21.0% / radix 13.5% / forward rasterize 9.8%) のうち backward / forward の alloc+往復成分を刈り取った形で、次の主要残差は radix_sort (~5.1ms/iter、23%) と SSIM (~2.4ms/iter) になる。
この finding が観測された / 言及している実験 run。