松田研究室
FindingID: p1-profiling-baseline
Status: stable / task P1 profiling baseline
← Findings Index
P1 profiling · Phase E / A.6 ROI 見積もり

P1 profiling baseline — radix_sort 27% / ssim 16% / backward 14% が三大 bottleneck、refine は僅か 2.6%

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 は卒論後**。

neutralrank: highaccepted-roadmap-pivot第 1 軸第 3 軸p1profilingkernel-timingphase-ea-6axis-1-preplego-smokehost-instant-proxy
Δ PSNR
(n/a — 計測のみ、PSNR 不変)
Δ Wallclock
(n/a — 計測 instrumentation のみ、 default OFF)
Δ Splats
(n/a)
Impact
Phase D Lego brushcompat 30k で wallclock 42m / brush 9m gap (-4.6x) の真の bottleneck を per-kernel host Instant 計測で数値化。**仮説 (refine host RMW dominant) は棄却**、refine は 2.6% に過ぎず Phase E GPU 化の ROI は ~3% (期待していた -25% の 1/8)。実 dominant は radix_sort 27% / ssim 16% / backward 14%。優先順位を **target_upload キャッシュ (即時 -5.6%) → radix_sort 改善 (-10〜15% 期待) → A.7 ICB batching tail (-5% 期待) → Phase E は卒論後** に反転すべき。
01

本文

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 は最後 に反転すべき。

Headline (仮説棄却 + 優先順位反転)

仮説 "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)。

1. 計測 setup

項目備考
scenenerf_synthetic/legobrush convention (premultiplied)
max_steps1500refine start 500 / stop 1500 で 1 サイクル + Phase D opacity_decay 同 cadence
initial splats5,207init.ply cull radius=1.5
final splats75,8671500 iter 時点 (Phase D 30k では最終 375k)
wallclock35.80 s23.9 ms/iter mean
measurementhost `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 / runtimeM4 Max 36GB + 並行 splat trainer 3 本GPU contention あり (absolute ms は 1.3-1.5x 膨張、相対順序は安定)
host Instant proxy は GPU commit + wait_until_completed を含む wallclock を測る。GPU 内部 sub-stage 分解はできないが、kernel 別 host wait 時間 = 卒論で問題視している "CPU が GPU を待つ時間" そのもの。

2. per-kernel wallclock breakdown (1500 iter cumulative)

kerneltotalcallsavg/call% wallclock属性
**ts_fwd_radix_sort**9.679 s15006.453 ms**27.0%**GPU + 16-pass scan (host CPU 介在)
**ts_ssim_fwd_grad**5.584 s15003.722 ms**15.6%**GPU (11×11 SSIM + grad)
**ts_backward_raster**4.849 s15003.233 ms**13.5%**GPU (atomic grad accum) + readback
ts_fwd_rasterize3.378 s15002.252 ms9.4%GPU forward kernel
ts_fwd_emit_pairs2.326 s15001.551 ms6.5%GPU (per-pair atomic counter)
ts_adam1.995 s15001.330 ms5.6%GPU dispatch (5 buffers × 1)
**ts_target_upload**1.994 s15001.329 ms**5.6%**Host→GPU buffer copy (毎 iter)
ts_l1_combine_ssim1.147 s1500765 µs3.2%GPU
**ts_refine** (clone/split/prune)0.946 s1185.99 ms**2.6%**Host RMW (prune+split のみ)
ts_project_back0.758 s1500506 µs2.1%GPU
ts_fwd_extract_off0.639 s1500426 µs1.8%GPU
ts_fwd_project0.632 s1500421 µs1.8%GPU
ts_refine_accumulate29.5 ms150020 µs0.08%Host loop (|v_xy| L2)
ts_opacity_decay (Phase D)0.44 ms1140 µs0.001%Host RMW
**SUM (instrumented)****33.96 s****94.8%**残 5.2% = loop overhead / scheduling
% wallclock = kernel total / 35.80 s。ts_forward parent (32.9%) は children と double-count するため除外。**top 3 (radix_sort / ssim / backward_raster) で wallclock の 56% を占有**。refine + accumulate + opacity_decay の host RMW 合計はわずか 2.7%、host CPU bottleneck 仮説は数値で棄却。

3. CPU / GPU 占有率の解釈

観察解釈
splat 親 process CPU40-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 ではない
当初仮説 "M4 Max 15 idle core を活用すべし" は実態を取り違えていた。CPU が idle なのは GPU を待っている時間 (= splat の trainer は inherent に 1 dispatcher だが、それで困らない)。改善の主軸は GPU kernel そのものの最適化、または CPU/GPU overlap (CommandBuffer pipeline depth 増加)。

4. radix_sort が 27% も占めるのはなぜか?

  • tile_bin.rs の radix sort は 16-pass: 4-bit radix × 16 pass = u64 key 全幅。各 pass で encoder 1 個 commit + wait、host CPU 側で prefix scan を 1 回 同期 (= 16 × commit_wait + 16 × scan = 32 boundary)。
  • pair_count が iter で増加: 1500 iter で splats 5k→76k に成長、tile_bin pair 数も同様に成長。**30k では更に増加** (Phase D 最終 375k で pair_count は数 M 規模、ms/call は 3-5x 増の見込み)。
  • Apple Metal Performance Shaders (MPS) の radix_sort は未使用: 現状 hand-rolled で動いており、MPS の MPSRadixSort や cooperative kernel 化で 2-3x 高速化の余地大。
  • A.7 ICB batching は forward tail (extract_offsets + rasterize): radix_sort 自体は host scan を挟むため batch 対象外、別の最適化が必要 (= ROI 主軸)。

5. ROI 見積もり (Phase E / A.6 / kernel fusion / target_upload キャッシュ)

施策対象 kernel現占有率GPU 化/最適化 後想定**wallclock ROI**実装工数優先度
**target_upload cache**ts_target_upload5.6%0.1% (1 回 upload)**-5.5%**小 (BTreeMap)**★最優先** (即時 ROI)
**radix_sort MPS / fused 化**ts_fwd_radix_sort27.0%10-13% (2-2.5x)**-14〜17%**中 (Metal kernel + scan 統合)**★高** (最大 ROI)
A.7 ICB batching 全面 (forward+backward+loss)ts_forward 子 + loss + project_back26.0% (commit barrier 込)20% (commit 4→1)-6%中-大 (cmd buffer 設計)中 (既存 PoC あり)
SSIM kernel fusion / window 縮小ts_ssim_fwd_grad15.6%8-10% (4 blur→2 fused)-5〜-7%中 (Metal kernel 再設計)
backward_raster atomic 削減ts_backward_raster13.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_decay0.001%0%0%**no-op** (host で十分)
wallclock ROI = 1500 iter smoke での占有率削減見積もり。30k では splats 5x で forward 系の絶対 ms が増えるが、相対占有率は radix_sort > ssim > backward の順序は安定 (advisor 確認)。**Phase E の ROI 見積もりは当初 -25% 期待から -2% に修正**、優先順位を最下層に。

6. brush -4.6x gap の解釈

  • brush 9m / 本実装 42m = -4.6x gap は radix_sort + ssim + backward の 3 kernel が brush の Burn/wgpu 実装と比べて遅いことに起因。
  • brush も同じ algorithm なので構造的に 5x 遅いはずがない、kernel 実装効率 (MPS 利用 / atomic minimization / commit batching) の差。
  • target_upload cache (即 -5.5%) で 42m → 39.7m。次に radix_sort -15% で 39.7m → 33.7mSSIM fusion -6% で 31.7mA.7 batching -6% で 29.7m。 累積で 42m → ~29m (-30%)、brush 9m まではまだ -3.2x ある (が +12-15m 圏は射程)。
  • Phase E (refine GPU 化) は最後 -1〜-2m しか効かない、axis 1 (native Metal) の卒論主張としては必要だが、bench 改善の主役ではない。

7. 計測 instrumentation の実装

  • splat/crates/splat-train-v1/src/timing.rs: 既存 thread-local `HashMap` accumulator に is_enabled() / disable() を追加。
  • splat/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。
  • overhead 実測: profile=false で既存挙動と完全互換 (enable 判定で early return)、profile=true でも 1500 iter 36 s / 既存 baseline 推定 35 s = ~0.2% noise floor。

8. 想定外 / caveat

  • GPU contention: 計測中 M4 Max 上で並行 splat trainer 3 本 (chair 30k / materials 30k / 別 smoke) が走っていた。absolute ms は 1.3-1.5x 膨張、しかし 相対順序は安定 (advisor 確認: contention は GPU ops を host ops より重く penalize するため、refine が bottleneck でないという結論は contention なしでも更に強化される)。
  • 1500 iter vs 30k: 1500 iter 終了時の splats は 76k、Phase D 30k 最終 375k と 5x 差。forward 系 (radix_sort / rasterize / backward) は splats と pair_count に比例して伸びるため、 30k では top 3 占有率が更に+5pt 高い可能性。逆に refine 占有率は更に低下 (refine の per-call ms は splats 5x で 3-4x のみ増、call 数固定)。
  • host Instant proxy の限界: GPU 内部 sub-stage (rasterize の tile 走査 vs 各 splat memo 等) は分解できない。MTLCounterSampleBuffer による HW counter 計測が次の精度向上 step (但し sudo 不要だが API 複雑、本 finding では proxy で十分と判断)。
  • opacity_decay 0.001% は train_t→1 で no-op になる影響: 1500/1500 = train_t=1.0 で minus_opac=0 になり後半 11 tick は early return。 Phase D 30k 全期間でも 0.01% に届かず、Phase D を host で実装した判断は正当。

9. autonomous loop に向けた提案

次 step 優先順位 (本 finding の roadmap 含意)

1. target_upload cache (即 -5.5%、~30 分実装): train_loop entry で全 cameras の target を Vec に一括 upload、毎 iter `cameras[idx].target_buf` を引くだけ。 2. radix_sort 改善 (-14〜17%、~1 日): Apple MPS の MPSMatrixDecompositionLU 系には 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. P1 final-bench までに target_upload cache + radix_sort 改善を入れる → Lego 30k で wallclock 42m → ~30m を狙う (brush 9m gap は -3.3x まで縮小、論文評価で defensible)
  2. radix_sort の真の中身を MTLCounterSampleBuffer で sub-pass 計測 (= 16-pass のうちどれが重いか、scan vs commit_wait vs sort pass)、 ~1 h 工数で投資し最適化方向を確定する
  3. Phase E は axis 1 contribution の卒論記述として scope を確保しておく (refine GPU 化のコード自体は残し、bench 数値の主役からは外す)
  4. A.6 f16 packed の deprioritize 確定: 既存 memo `feat_g_f16_packed_roi.md` の "wallclock ~1%" 数値と本 finding が一致、roadmap から外す

10. 再現手順

# 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 なので互換。

11. 関連

  • Phase D 30k baseline (wallclock 42m): p1-d-stage2-30k-results
  • A.7 ICB batching (forward tail batching): a-7-icb-batching-results
  • f16 packed ROI 検証 (deprioritize 確定): memo feat_g_f16_packed_roi.md
  • 次予定: target_upload cache + radix_sort sub-pass profiling (本 finding ROI table の#1, #2 を実装)
  • Phase E (refine GPU 化) memo: autonomous_plan_brush_parity.md (本 finding により優先度反転、卒論後 defer)
03

関連 finding

© 2026 大竹律輝 — 松田研究室