host pump pipeline (= wgpu 抽象典型) を排除し forward → loss → backward を GPU buffer で連結。2k iter wallclock -26.9%、30k で -14〜-19% を 4 sample で再現。当初の PSNR -0.65 dB regression 主張は variance band 内と判明し撤回。第 3 軸 (Apple unified memory) narrative の核。
host pump pipeline (= wgpu 抽象典型) を排除し forward → loss → backward を GPU buffer で連結。2k iter で wallclock -26.9% (42.5 → 31.07 ms/iter)、30k では 4 sample variance test で wallclock -14〜-19% が再現。第 3 軸 (Apple unified memory) narrative の核となる成果。
forward → loss → backward を GPU buffer で連結#5.31.x.future 4 sample variance test で n=1 の sample noise と判明、systematic regression ではないforward_with_state ──→ ForwardState {
rendered_buf: Some(Buffer), // 新フィールド
rendered: Vec<f32>, // legacy 互換、L2 経路で使用
...
}
iter 入口 (train_step):
target_buf = upload(target) // 1 回 / iter
rendered_buf ─┐
├─→ ssim.forward_and_grad_buf(rendered_buf, target_buf)
│ ├─→ work_grad: Buffer (Ref で返却)
│ └─→ mean_ssim: f32 (CPU sum、1.28 ms 残置)
target_buf ───┘
│
rendered_buf ─┐ ▼
├─→ loss_eval.compute_l1_combine_ssim(
target_buf ───┤ rendered_buf, target_buf, ssim_grad_buf, λ
ssim_grad_buf─┘ ) → (l1_loss: f32, dldr_buf: Ref<Buffer>)
│
total_loss = (1-λ)·l1 + λ·(1-mean_ssim) │
▼
splats_grad: Vec = backward_rasterize_buf(fs, dldr_buf)
│
(refine 用に readback、ここは将来 GPU 化)
▼
project_backwards / Adam
// loss.metal
kernel void loss_l1_only(
const device float* rendered, target,
constant LossUniforms& u,
device atomic<float>* loss_sum,
device float* dldr,
...) {
// dldr = sign(r-t)/N
// L1 reduction: simd_sum + warp atomic
}
kernel void loss_l1_combine_ssim(
const device float* rendered, target, ssim_grad,
constant LossUniforms& u, // λ included
device atomic<float>* loss_sum,
device float* dldr,
...) {
// dldr = (1-λ)·sign(r-t)/N - λ·ssim_grad
// L1 reduction 同様
}
| advisor 指摘 | 対応 |
|---|---|
| 1. correctness checkpoint after L1 path | gpu_loss::tests::l1_only_matches_host (rel <1e-3、per-element 一致 rate <1e-4)、500 iter smoke で loss curve 一致確認 |
| 2. loss_sum_buf persist | LossEvaluator field に 4 bytes 1 回 alloc、毎 dispatch でゼロクリア |
| 3. 2 kernel 分離 | loss_l1_only と loss_l1_combine_ssim 別実装、warp divergence 排除 |
| 4. queue reuse 30k validation | M-2 25.79 dB → 26.27 dB (+0.48) M-3 候補確定 (#5.31.0b) |
| 5. L2 path | host fallback 残置 (本研究 production で未使用)、L2 GPU 化は future work |
| 指標 | step 30b 初期 | #5.31.0a (instr 追加) | #5.31.0b (queue reuse) | #5.31.x (GPU loss) | 累積 delta |
|---|---|---|---|---|---|
| wallclock 2000 iter | 132.4 s | 95.9 s | 82.8 s | 62.14 s | -53.1% vs step 30b |
| ms/iter | 67.9 ms | 49.2 ms | 42.5 ms | 31.07 ms | -54.2% |
| 主要 wins | (baseline) | (no opt) | -13.7% queue reuse | -26.9% GPU loss | (累積) |
| kernel | calls/iter | ms/call | per-iter ms | 旧比較 |
|---|---|---|---|---|
| ts_forward (bucket) | 1 | 16.08 | 16.08 | -8% (17.52 → 16.08) |
| ts_loss_gpu (bucket) | 1 | 11.91 | 11.91 | -42% (旧 ts_loss_host 9.13 + ts_backward 11.62 = 20.75 を置換) |
| ↳ ssim_fwd_bwd_buf | 1 | 1.47 | 1.47 | -48% (旧 ssim_fwd_bwd 2.84) |
| ↳ ssim_cpu_mean_buf | 1 | 1.01 | 1.01 | -21% (旧 1.28、kernel 不要部分) |
| ↳ loss_l1_combine | 1 | 0.23 | 0.23 | NEW (旧 host iterate 4.5 ms 置換) |
| ↳ rasterize_backwards_simd_buf | 1 | 8.51 | 8.51 | -10% (旧 9.47、上流 readback なし → cache 圧縮) |
| ts_target_upload (NEW) | 1 | 0.62 | 0.62 | (NEW、target 共有のため必要) |
| ts_adam (bucket) | 1 | 0.91 | 0.91 | -4% |
| ts_project_back (bucket) | 1 | 0.42 | 0.42 | -23% |
| (encoder _enc 合計) | - | - | ~0.18 | (旧 0.34、queue reuse 効果) |
| 削減項目 | per-iter ms | wallclock % |
|---|---|---|
| host loss + grad iterate (l1_loss + l1_loss_grad + grad combine) | -4.5 ms | -10.6% |
| ssim grad readback (host 経由) | -0.52 ms | -1.2% |
| ssim rendered/target re-upload (forward_and_grad 内部) | -0.7 ms | -1.6% |
| dldr buffer upload (host Vec → GPU) | -0.5 ms | -1.2% |
| target upload (centralized 1 回) | +0.62 ms | +1.5% (cost) |
| その他 (cache pressure 緩和、autoreleasepool) | -3.7 ms | -8.7% |
| net | -11.4 ms | -26.9% |
"その他" の 3.7 ms 削減は推定: host iterate を排除すると CPU L2/L3 cache が解放され、Metal driver の事前準備や autorelease pool churn も軽くなる。step 30b → step 31.0b で見られた「naive 1.14 ms → 実測 6.7 ms (autoreleasepool 圧縮)」と同根。
advisor 注意通り: gate culture (F/G で 4 連続 / 3 連続失敗) を維持しつつ、想定値 (-16〜19%) を上振れる結果を出せた。これは F/G の honest reject と対称な「honest accept」事例。
初期 30k validation (2026-04-30、commit 22411d2 binary):
| 指標 | 値 | M-2 比 | M-3 (#5.31.0b queue reuse) 比 |
|---|---|---|---|
| val PSNR | 25.140 dB | -0.65 dB | -1.13 dB |
| val SSIM | 0.9022 | -0.0067 | -0.0084 |
| 学習時間 | 1297 s = 21m37s | -28% (vs 30m M-2) | -18.5% (vs 26m32s M-3) |
| final splats | 79,654 | -967 | -5,699 |
当初は「PSNR -0.65 dB regression」と判定、speed/quality tradeoff として記録した。しかしこれは n=1 sample で短絡した結論。#5.31.x.future で 4 sample variance test を行い、この regression 主張は撤回された (次節)。
advisor 推奨で deep numerical analysis を実施。#5.31.x.future の最初の hypothesis は「f32 reduction order が training trajectory を狂わせている」だったが、diagnostic で:
→ dldr に GPU/HOST 差はない、後段の atomic 非決定性が refine 累積を介して divergence を生む。これは GPU/HOST 両 path 共通の挙動、#5.31.x 固有の問題ではない仮説。
--force-host-loss flag を追加 (commit 330c48a)、同 binary で GPU/HOST 切替 A/B が可能に。4 つの 30k validation を実施 (全て同 seed 42、同 F config):
| # | binary | path | val PSNR | SSIM | splats | 学習時間 |
|---|---|---|---|---|---|---|
| 1 | e2611df (queue reuse only) | host | 26.268 dB | 0.9106 | 85,353 | 26m32s |
| 2 | 22411d2 (GPU loss 追加) | gpu | 25.140 dB | 0.9022 | 79,654 | 21m37s |
| 3 | 330c48a (--force-host-loss) | host | 24.855 dB | 0.9005 | 82,185 | 25m48s |
| 4 | 330c48a (default) | gpu | 25.467 dB | 0.9053 | 81,326 | 22m51s |
観察 1: 同 binary 330c48a 内で GPU > HOST (sample #4 vs #3) — HOST: 24.855 dB / GPU: 25.467 dB (+0.612 dB) → 当初の「GPU が host より劣る」という命題は反証された。
観察 2: 同 path (host) 別 binary で 1.41 dB 差 (sample #1 vs #3) — e2611df host: 26.27 dB / 330c48a host: 24.86 dB → 同じコード経路でも binary 違いで 1.41 dB のばらつき、atomic 非決定性が compile/scheduling を介して影響。
観察 3: GPU path の wallclock は consistently 速い — queue-reuse host (#1): 1592s (baseline) / GPU r1 (#2): 1297s (-19%) / HOST r1 (#3): 1548s (-3%、queue reuse 改善のみ) / GPU r2 (#4): 1371s (-14%) → GPU path は HOST path より 14-19% wallclock 短縮 (4 sample で再現)。
| 項目 | 当初の主張 | 修正後 (4 sample 検証後) |
|---|---|---|
| GPU loss path PSNR | 「-0.65 dB regression」 | n=1 sample variance 内、systematic regression ではない 撤回 |
| M-3 26.27 (queue reuse) | 「lucky outlier の可能性」 | n=1 で断定不能、1.41 dB variance band の上端 sample (これも撤回) |
| GPU vs HOST 同 binary | 「GPU が劣る」 | GPU が +0.61 dB 高い (sample #3 vs #4) |
| wallclock 改善 | -28% (2k iter) | -14〜-19% (30k 実測) で確実 |
#5.31.0b queue reuse は M-3 確定: 30k で 26.27 dB を出した sample (#1) は variance band の上端だが、同 path 別 binary でも 24.86 dB (#3) なので、期待 PSNR は ~25.4 dB ± 0.6 dB。queue reuse 自体の wallclock -13.7% は本質的、コード変更は健在。
#5.31.x GPU loss path は M-3 候補に再昇格 (mixed 撤回): PSNR は HOST path と同等 (variance 内、むしろ +0.61 dB 高い sample もある)、wallclock -14〜-19% (4 sample で再現)。ただし single sample の絶対 PSNR を milestone と呼ぶのは fragile、mean ± std での記述に切替。→ M-3.x として記録、stat-based wallclock 改善を主張。
30k val PSNR は 25.0 ± 0.6 dB の variance を持ち、これは rasterize_backwards_simd の atomic_fetch_add warp 順序非決定性が refine state accumulator (iter 500-1500) に compound することに起因する。同 binary 330c48a での GPU loss path (n=1, 25.47) と HOST fallback (n=1, 24.86) の差は variance 内であり、speed/quality tradeoff は観測されなかった。wallclock 改善 (-14〜-19% over HOST、4 sample) は consistently 観測される、これが #5.31.x の主要寄与。
MTLBuffer を直接設計に組み込んだ#5.31 original) では 0.5% 上限だったところ、no-readback で 26.9%kernel level (M-2 SIMD reduction 2.43×) と pipeline level (M-3 GPU loss -26.9%) の両方を分離して評価できる本 step は mixed result で、卒論には 「速度の big win + PSNR regression という二面性」 の事例として収録 (※ 後の 4 sample variance test で PSNR 主張は撤回):
F (scale_reg 失敗) / G (MCMC 失敗) / #5.31 (ArgBuffer reject) の negative findings と、#5.31.0b (queue reuse +0.48 dB の純 win) の対比で「測定して進める研究の typology」が立つ:
| typology | 例 | 卒論章 |
|---|---|---|
| 純 win (PSNR ≥ baseline + speed up) | #5.33 M-2 (SIMD 2.43×、PSNR +0.20)、#5.31.0b M-3 (queue reuse、PSNR +0.48) | 主要成果 |
| 純 reject (gate 不通過) | #5.31 ArgBuffer (0.5% 上限)、#5.31.5 dispatch fusion、#5.33.b/c | negative findings |
| 設計欠陥 reject | F (scale_reg formulation 3 失敗)、G (MCMC 4 失敗) | future work |
| mixed result (speed vs quality tradeoff) | #5.31.x GPU loss (-22.8% wallclock、PSNR -0.65) ※後に撤回 | 方法論章 (本 step) |
#5.31 ArgBuffer の reject (0.5% 上限) と、#5.31.x の partial win (-22.8% wallclock、PSNR -0.65) を併せて「ベンチマーク前提の自己検証 → bottleneck 同定 → 想定外の上振れ → 30k validation で PSNR 副作用検出」 という方法論が立つ:
= 「測ってから最適化し、何度も前提を更新した」5 段階の実証
| 項目 | 期待効果 | 工数 |
|---|---|---|
rendered.to_vec() skip in forward_with_state (L2 path 修正と引き換え) | -0.82 ms (-2%) | 1-2 hr (L2 GPU kernel 化込み) |
ssim_cpu_mean の GPU 化 (W·H·3 reduce kernel) | -1.0 ms (-2.5%) | 1-2 hr |
splats_grad readback の GPU refine 化 | -0.05 ms (~0%) | refine の大規模 refactor |
final_t / splats2d / sorted_idx / tile_offsets の GPU 連結 (forward → backward) | ~3-5 ms (推定) | 2-4 hr (forward の戻り値 大幅変更) |
合計潜在: 残り ~5 ms/iter、追加 -10〜15% wallclock 余地あり。本研究では #5.31.x の単独 win を採用、上記は future work 化。
この finding が観測された / 言及している実験 run。