Lego brushcompat + opacity decay 1500 iter (mean 24 ms/iter) で host Instant proxy による per-kernel wallclock 計測。 **仮説棄却**: refine 占有率は 2.6% に過ぎず、Phase E GPU 化 ROI は ~3% (見積もり -25% に対し 1/8)。実 dominant は (1) **radix_sort 27.0%** (forward 内 tile binning、6.45 ms/call)、(2) **ssim_fwd_grad 15.6%** (11×11 SSIM)、(3) **backward_raster 13.5%**。優先順位を反転: **target_upload キャッシュ (5.6% 即時回収) > radix_sort 改善 (10-15% 期待) > A.7 ICB tail batching > Phase E は卒論後**。
Phase D 30k で wallclock 42m vs brush 9m (-4.6x gap) の原因を kernel ごとに数値化。当初仮説は 「splat process CPU 63.4% = 1 thread bound、refine の host RMW (prune/split/clone) が dominant」 だった。これは誤りで、refine は 2.6% に過ぎない。 実 dominant は forward 内の radix_sort 27.0%、ssim 15.6%、backward 13.5%。 CPU 50% = main thread が GPU commit/wait_until_completed で blocking している 症状であり、CPU parallelization で解決しない (15 idle core は無関係)。優先順位を 低 hanging fruit → radix_sort → Phase E は最後 に反転すべき。
仮説 "refine host RMW が bottleneck" は棄却。refine 占有率は smoke で 2.6% (1500 iter で 11 回呼ばれて合計 946ms)、30k 推定でも ~7-8% 帯で頭打ち。 Phase E GPU 化の ROI は -25% 期待だったが実態は -3〜-5%。 実 bottleneck は (1) radix_sort 27.0% (M4 Max GPU radix で 6.45 ms/call、Apple Metal Performance Shaders sort で改善余地大)、 (2) ssim_fwd_grad 15.6% (11×11 Gaussian blur 4 回 + grad)、 (3) backward_raster 13.5% (per-pixel atomic accumulation)。 優先順位反転: target_upload キャッシュ → radix_sort 改善 → A.7 batching → Phase E (defer)。
| 項目 | 値 | 備考 |
|---|---|---|
| scene | nerf_synthetic/lego | brush convention (premultiplied) |
| max_steps | 1500 | refine start 500 / stop 1500 で 1 サイクル + Phase D opacity_decay 同 cadence |
| initial splats | 5,207 | init.ply cull radius=1.5 |
| final splats | 75,867 | 1500 iter 時点 (Phase D 30k では最終 375k) |
| wallclock | 35.80 s | 23.9 ms/iter mean |
| measurement | host `std::time::Instant` | Metal HW counter 非使用 (sudo 不要、proxy として十分) |
| overhead per record | ~100 ns (HashMap lookup + Instant) | per-iter 15 record × 100ns = 1.5 µs / 24 ms = 0.006% 影響 |
| config flag | `[backend] profile = true` | 新規追加、default false で既存挙動不変 |
| env / runtime | M4 Max 36GB + 並行 splat trainer 3 本 | GPU contention あり (absolute ms は 1.3-1.5x 膨張、相対順序は安定) |
| kernel | total | calls | avg/call | % wallclock | 属性 |
|---|---|---|---|---|---|
| **ts_fwd_radix_sort** | 9.679 s | 1500 | 6.453 ms | **27.0%** | GPU + 16-pass scan (host CPU 介在) |
| **ts_ssim_fwd_grad** | 5.584 s | 1500 | 3.722 ms | **15.6%** | GPU (11×11 SSIM + grad) |
| **ts_backward_raster** | 4.849 s | 1500 | 3.233 ms | **13.5%** | GPU (atomic grad accum) + readback |
| ts_fwd_rasterize | 3.378 s | 1500 | 2.252 ms | 9.4% | GPU forward kernel |
| ts_fwd_emit_pairs | 2.326 s | 1500 | 1.551 ms | 6.5% | GPU (per-pair atomic counter) |
| ts_adam | 1.995 s | 1500 | 1.330 ms | 5.6% | GPU dispatch (5 buffers × 1) |
| **ts_target_upload** | 1.994 s | 1500 | 1.329 ms | **5.6%** | Host→GPU buffer copy (毎 iter) |
| ts_l1_combine_ssim | 1.147 s | 1500 | 765 µs | 3.2% | GPU |
| **ts_refine** (clone/split/prune) | 0.946 s | 11 | 85.99 ms | **2.6%** | Host RMW (prune+split のみ) |
| ts_project_back | 0.758 s | 1500 | 506 µs | 2.1% | GPU |
| ts_fwd_extract_off | 0.639 s | 1500 | 426 µs | 1.8% | GPU |
| ts_fwd_project | 0.632 s | 1500 | 421 µs | 1.8% | GPU |
| ts_refine_accumulate | 29.5 ms | 1500 | 20 µs | 0.08% | Host loop (|v_xy| L2) |
| ts_opacity_decay (Phase D) | 0.44 ms | 11 | 40 µs | 0.001% | Host RMW |
| **SUM (instrumented)** | **33.96 s** | — | — | **94.8%** | 残 5.2% = loop overhead / scheduling |
| 観察 | 値 | 解釈 |
|---|---|---|
| splat 親 process CPU | 40-55% (M4 Max 16 core) | 1 dispatcher thread が GPU commit + wait で blocking |
| active thread (>1% CPU) | **1 個** | Metal driver helper 2 threads (~7-8% 各) は dispatch handler |
| dominant kernel type | **GPU** (radix_sort + ssim + raster + backward = 65% wallclock) | host CPU が忙しいのは GPU 待ちの間 commit/poll、純 host 計算ではない |
| host RMW kernel 合計 | 2.7% (refine + accumulate + opacity_decay) | **host CPU parallelization は意味なし**、15 idle core は GPU 待ちの副作用 |
| bottleneck 種別 | **GPU-bound (not CPU-bound)** | 解決方向は GPU kernel 効率化、CPU thread pool ではない |
MPSRadixSort や cooperative kernel 化で 2-3x 高速化の余地大。| 施策 | 対象 kernel | 現占有率 | GPU 化/最適化 後想定 | **wallclock ROI** | 実装工数 | 優先度 |
|---|---|---|---|---|---|---|
| **target_upload cache** | ts_target_upload | 5.6% | 0.1% (1 回 upload) | **-5.5%** | 小 (BTreeMap | **★最優先** (即時 ROI) |
| **radix_sort MPS / fused 化** | ts_fwd_radix_sort | 27.0% | 10-13% (2-2.5x) | **-14〜17%** | 中 (Metal kernel + scan 統合) | **★高** (最大 ROI) |
| A.7 ICB batching 全面 (forward+backward+loss) | ts_forward 子 + loss + project_back | 26.0% (commit barrier 込) | 20% (commit 4→1) | -6% | 中-大 (cmd buffer 設計) | 中 (既存 PoC あり) |
| SSIM kernel fusion / window 縮小 | ts_ssim_fwd_grad | 15.6% | 8-10% (4 blur→2 fused) | -5〜-7% | 中 (Metal kernel 再設計) | 中 |
| backward_raster atomic 削減 | ts_backward_raster | 13.5% | 8-9% (reduction 化) | -4〜-5% | 大 (cooperative kernel) | 中-低 |
| **Phase E (refine GPU 化)** | ts_refine + accumulate | **2.7%** | **0.5%** | **-2%** | **大 (Metal kernel 群)** | **低 (defer 卒論後)** |
| A.6 f16 packed 全面 | 全 GPU kernel | (memory bandwidth) | — | ~1% (既存 PoC で実証) | 大 (kernel 全更新) | 低 (実 ROI 小、memo `feat_g_f16_packed_roi.md` 参照) |
| opacity_decay GPU 化 (Phase D) | ts_opacity_decay | 0.001% | 0% | 0% | 中 | **no-op** (host で十分) |
splat/crates/splat-train-v1/src/timing.rs: 既存 thread-local `HashMapsplat/crates/splat-train-v1/src/config.rs: `BackendConfig` に `profile: bool` を追加 (default false)。splat/crates/splat-train-v1/src/train_loop.rs: 既存の `ts_forward / ts_loss_gpu / ts_adam / ts_project_back` に加え、`ts_ssim_fwd_grad / ts_l1_combine_ssim / ts_backward_raster / ts_refine_accumulate / ts_refine / ts_opacity_decay / ts_opacity_reset / ts_scale_reg / ts_mcmc_*` を追加。 ts_loss_gpu parent は double-count 回避のため除去。splat/crates/splat-train-v1/src/trainer.rs: `forward_with_state` 内に `ts_fwd_project / ts_fwd_emit_pairs / ts_fwd_radix_sort / ts_fwd_extract_off / ts_fwd_rasterize` を追加 (forward sub-breakdown)。splat/crates/splat-cli/src/cmd/train.rs: `cfg.backend.profile == true` で timing::reset() + enable()、train_loop 終了時 `print_summary()` を log_every の max_steps tick で stderr 出力。splat/configs/2026-05-25-1200-lego-profile-smoke.toml: smoke config (1500 iter、profile=true)、parent: 2026-05-24-2000-lego-brushcompat-opacdecay-5k.toml。1. target_upload cache (即 -5.5%、~30 分実装): train_loop entry で全 cameras の target を VecMPSMatrixDecompositionLU 系には sort はないため、scan の host 同期を Metal kernel 内に統合する fused approach、または Metal Performance Counters で radix の真の bottleneck を 4-bit 単位で見る。 3. A.7 ICB batching tail 完全化 (-5%、既存 PoC 拡張): forward + loss + backward + project_back の 4 commit を 1 cmd buffer 化、host wait 回数を 4→1 に。 4. Phase E は卒論後: axis 1 主張 (native Metal kernel 群) として必要だが、wallclock 改善は -2% で labour intense (subagent ~1-2 日)、卒論評価表の主役にはならない。
# 1. Build (P1 profiling instrumentation 含む binary)
cd splat
cargo build --release -p splat-cli
# 2. profile smoke 実行 (1500 iter、~35-50 s + GPU contention で 1.3-1.5x 増)
./target/release/splat train --config configs/2026-05-25-1200-lego-profile-smoke.toml
# 3. 期待出力: stderr に
# [profile] kernel timing ON ([backend] profile=true)
# ... training progress ...
# === Kernel timing summary ===
# kernel | total | calls | avg/call | %total
# ts_fwd_radix_sort | 9.679s | 1500 | 6.453ms | 27.0%
# ...
#
# 4. 任意の config で計測したければ [backend] に profile = true を追加するだけ。
# 既存 config は profile flag を持たず default false なので互換。
p1-d-stage2-30k-resultsa-7-icb-batching-resultsfeat_g_f16_packed_roi.mdautonomous_plan_brush_parity.md (本 finding により優先度反転、卒論後 defer)