松田研究室
FindingID: p1-axis1-phase-f3-radix-gpu-prefix-falsified
Status: stable / task P1 Phase F.3
← Findings Index
P1 axis 1 · Phase F.3 · Tier 2 falsified · 方法論 finding

Phase F.3 radix GPU prefix scan — bit-exact 実装完成だが Metal implicit fences で +7.4% wallclock / +35-41% per-call regression、audit Tier 2 仮説 falsified

Phase F.1 emit_simd / f16_fwd に続く audit Tier 2 (radix_sort GPU prefix scan、別 mechanism = CPU-GPU sync 除去) の empirical 検証。GPU 上で per-group histograms の exclusive scan を計算する 16-thread single-threadgroup kernel を実装、bit-exact (100k random keys / 500k packed keys / edge cases 6 種) 確認。**5k Lego smoke で wallclock 115.83s → 124.41s (+7.4%、sanity rerun 118.73s で再現)**、**ts_fwd_radix_sort 4.768 → 6.733 ms/call (+41%)**、PSNR は parity (31.604 → 31.635 dB、bit-exact 経路なので drift なし)。**Likely mechanism**: StorageModeShared buffer での back-to-back compute encoder 間で Metal が implicit fence を挿入 (hist→scan で buf_hist、scan→scatter で buf_offsets の read-after-write hazard)、TBDR pipeline stall。旧 CPU 経路は buf_hist→buf_offsets を host で行うため GPU 内 memory dependency が無く、「除去した wait_until_completed」は実は **CPU prefix scan と overlap していた active work** だった。kernel + tests は env `SPLAT_RADIX_GPU_SCAN=1` で opt-in、別 workload (>>100k splats 等) で再評価余地のみ残す。

negativerank: highaudit-falsified-tier-2第 1 軸p1-axis1phase-fradix-sortgpu-prefix-scantier-2falsifiednegative-findingmetal-fencestbdrab-testlego-5k
Δ PSNR
+0.03 dB (parity、bit-exact 経路、session noise 内)
Δ Wallclock
+7.4% (+8.58s @ 5k iter、sanity rerun +4.8% でも regression 確定)
Impact
audit (p1-axis1-metal-opt-audit) で Tier 2 「radix_sort GPU prefix sum、-0.5-0.8% wallclock、LOW PSNR risk」と分類した候補を empirical 検証。bit-exact 実装は完成 (16-thread single-threadgroup kernel、Apple SIMD prefix exclusive sum + per-digit serial scan、100k random / 500k packed keys / edge cases 6 種で CPU stable sort と byte-for-byte 一致)、しかし 5k Lego smoke で wallclock 115.83s → 124.41s (+7.4%)、ts_fwd_radix_sort 4.768 → 6.733 ms/call (+41%) の net regression。sanity re-run (118.73s / 6.402ms) で再現確認、run-to-run 変動の上。PSNR は parity (31.604 → 31.635 dB、bit-exact 経路で 0 drift 期待、観測 +0.03 dB は session noise)。Likely mechanism: StorageModeShared buffer での back-to-back compute encoder 間で Metal が implicit fence を挿入 (hist→scan の buf_hist、scan→scatter の buf_offsets で read-after-write hazard)、TBDR pipeline stall。旧 CPU 経路は buf_hist→buf_offsets 変換を host で実行するため GPU 内 memory dependency が無く、「除去した wait_until_completed」は実は CPU prefix scan と overlap していた active work だった。教訓: 「CPU 介在を on-GPU に置換」族の audit 予測は overlap の存在を見落とすため systematically overestimate、Tier 2 同 family (backward TBDR、tile-local accumulator) の skip 判断強化。kernel + tests は env SPLAT_RADIX_GPU_SCAN=1 で opt-in (future workload hedge)。
01

本文

audit (p1-axis1-metal-opt-audit) で Tier 2 = 「Phase E scope の構造改善、別 mechanism (CPU-GPU sync 除去)、-0.5-0.8% wallclock、LOW PSNR risk」 と分類した radix_sort GPU prefix sum を 4-5h で実装、5k Lego smoke で empirical 検証。bit-exact 実装は完成PSNR parity (drift 0 期待、観測 ±0.03 dB は session noise)、しかし wallclock +7.4% / ts_fwd_radix_sort +41% の net regression。Phase F.1 (emit_simd / f16_fwd) に続く Tier 2 別 family の audit-overestimate 観測 5 例目。

Headline (Tier 2 hypothesis falsified、別 mechanism 化でも overestimate)

audit Tier 2 "-0.5-0.8% wallclock、CPU-GPU sync 除去" 予測は empirical で大幅 falsified。5k Lego smoke で wallclock 115.83s → 124.41s (+7.4%)、ts_fwd_radix_sort 4.768 → 6.733 ms/call (+41%、sanity re-run 6.402ms で再現)、PSNR は bit-exact 経路で parity。Likely mechanism (advisor 2026-05-25): StorageModeShared buffer での back-to-back compute encoder 間で Metal が implicit fence を挿入 (hist→scan の buf_hist、scan→scatter の buf_offsets で read-after-write hazard)、TBDR pipeline stall。旧 CPU 経路は buf_hist→buf_offsets を host で行うため GPU 内 memory dependency が無く、「除去した wait_until_completed」は CPU prefix scan と overlap していた active work だった。**結論**: default 経路は legacy CPU scan 維持、GPU scan kernel + tests は env SPLAT_RADIX_GPU_SCAN=1 で opt-in (future workload hedge、>>100k splats / 高 tile_per_splat scene で dispatch overhead vs fence trade-off が逆転する可能性)。Tier 2 同 family (backward TBDR、tile-local accumulator + Kahan summation で global atomic 除去) は **同 mechanism 影響を受けるため skip 判断強化**。

1. A/B test 数値 (Lego 5k smoke、seed=42、premultiplied、3 run)

metricF.3 CPU scan baselineF.3 GPU scanGPU scan rerun (sanity)delta vs baselineaudit 予測
total wallclock**115.83s**124.41s (**+7.4%**)118.73s (+2.5%)+7.4% (first) / +2.5% (rerun)-0.5〜-0.8% (audit) / -3〜-14% (advisor optimistic)
TOTAL kernel sum174.943s191.236s (+9.3%)186.0s (est.)+9.3%
ts_fwd_radix_sort ms/call4.768**6.733 (+41.2%)****6.402 (+34.5%)****+35〜41%**-0.5〜-3.0 ms/call expected
ts_fwd_radix_sort share13.6%17.6%17.5%+4.0 pt
ts_fwd_emit_pairs ms/call5.1574.775 (-7.4%)-7.4% (noise、orthogonal)
mean val PSNR (100 view)**31.604**31.635 (**+0.031**)31.869 (+0.265)±0 (bit-exact)±0 dB
final splats84,84083,644 (-1.4%)-1.4%
F.3 baseline = legacy CPU scan 経路 (F.3 falsification 後の default、env 不要)。最初のベンチ取得時は default が GPU scan だったため `SPLAT_RADIX_CPU_SCAN=1` env で強制したが、その後の default 反転で env なしで同経路を実行可能。F.3 GPU scan = opt-in 経路、env `SPLAT_RADIX_GPU_SCAN=1` で enable (kernel + tests は worktree に残存、future workload hedge)。sanity rerun は同 seed / config / binary で同セッション内再実行。+41% per-call radix_sort regression が再現、+7.4% wallclock は rerun で +2.5% に縮小だが noise floor (~2-4%) の上、real regression 確定。PSNR parity は bit-exact unit test (100k / 500k keys、CPU stable sort と byte-for-byte 一致) で事前確証済。

2. 実装と bit-exact 検証 (correctness は問題なし、performance のみ falsified)

  1. shader (tile_bin.metal:radix_prefix_scan): 1 threadgroup、16 thread (digit 1 つ / lane)。phase 1: thread `d` が hist[g*16 + d] を全 group で sum → threadgroup memory の bucket_totals[d]。phase 2: simd_prefix_exclusive_sum を 16 lane で 1 SIMD group (Apple width 32) として実行 → bucket_starts[d]。phase 3: thread `d` が column 内 serial scan で offsets[g*16 + d] = cursor; cursor += hist[g*16 + d] を g=0..num_groups で実行。num_groups 数千でも単 threadgroup の serial scan は 8000 add 程度で trivial。
  2. host (tile_bin.rs::radix_sort): use_gpu_prefix_scan flag (env SPLAT_RADIX_GPU_SCAN=1 で ON、default OFF) で 2 path 分岐。GPU 経路は 1 command buffer / pass で hist → scan → scatter を 3 encoder で chain、intra-pass の wait_until_completed を完全除去、final pass のみ wait。CPU 経路 (legacy) は intact 保持。num_groups u32 buffer (scan kernel 引数) を pre-allocate。
  3. bit-exact 検証 (unit test、9 件全 pass): radix_sort_gpu_vs_cpu_scan_100k (100k 一様乱数 u64、num_groups ≈ 391)、radix_sort_gpu_scan_packed_keys_500k (500k packed key = tile_id[0,2500) << 32 | depth_bits[0.01,100.0)、runtime distribution mimic、num_groups ≈ 1954)、radix_sort_gpu_scan_edge_cases_match_cpu_scan (n=1, 255, 256, 257, 1024, 2048 の boundary num_groups)、すべて (a) GPU 経路 vs CPU 経路 byte-for-byte 一致 (b) 両者 vs cpu_stable_sort reference 一致。Phase F.3 で PSNR drift は理論的に 0 (sort 順序保存)、観測 ±0.03 dB は session noise。

3. Mechanism 分析 (advisor 2026-05-25): なぜ -0.5% 予測が +7.4% 実測になったか

  1. 「除去した wait_until_completed」は idle ではなく active overlap だった。旧経路: histogram.commit() → wait → CPU prefix scan (~64KB 配列の 2 pass、~100µs) → scatter.commit()。host 側で wait している間、GPU は scatter dispatch 待ちで idle だが、wait 時間そのものは CPU prefix scan と overlap していた。新経路で除去したのは idle time ではなく overlap time、純粋な improvement にならない。
  2. StorageModeShared での Metal implicit fence 挿入: 同 command buffer 内で連続 compute encoder が同一 buffer に read-after-write hazard を持つ場合、Metal hazard tracker は encoder 境界に implicit fence (memory barrier) を挿入。本 F.3 では encoder 1 (hist) が buf_hist write → encoder 2 (scan) が buf_hist read + buf_offsets write → encoder 3 (scatter) が buf_offsets read で 2 つの fence が必須。TBDR pipeline では fence が tile flush + driver-level scheduling barrier を強制、stall 発生。
  3. 旧 CPU 経路は GPU 内 memory dependency が無かった: hist → CPU → scatter で、buf_hist は GPU dispatch 後 host 読みのみ、buf_offsets は host 書き → GPU dispatch 読み。encoder 間の GPU 内 read-after-write は無く、implicit fence 不要。新経路を導入することで初めて GPU 内 dependency chain が発生、本来不要な fence overhead を増やしてしまった。
  4. per-call 4.768 → 6.733 ms (+1.97 ms) の breakdown 推定: 16 pass × ~120 µs/pass の fence overhead = ~1.92 ms、ほぼ全量を説明。GPU prefix scan 自体は 16-thread 1-threadgroup で <10µs / pass < 0.2 ms/call、無視可能。CPU scan elimination の saved time は元々 wait と overlap していたため net 0。
  5. 修正可能性 (試さない判断): (a) untracked resource + manual fence で hazard tracker を bypass → 正確性検証 cost 大、(b) hist + scan + scatter を 1 kernel に fusion → 16 digit / variable group size のため 1 dispatch で表現困難 + MED PSNR risk、(c) StorageModePrivate 化 → unified memory のメリット捨てる。いずれも 4-5h 以上の追加 cost、ROI 不明 (audit calibration 5 連続 overestimate)、棄却。

4. PSNR 安全性確認 (bit-exact 経路、drift 観測なし)

  • F.3 baseline (CPU scan) 31.604 dB → F.3 GPU scan 31.635 dB: +0.031 dB は session noise 圏内 (~0.1 dB run-to-run、cf. F.1 baseline 2 sample で 31.720 / 31.588 = 0.13 dB variance)
  • sanity rerun 31.869 dB: baseline からの drift +0.265 dB は run-to-run 上だがやはり session noise (initial population subsampling の差)
  • bit-exact unit test (100k / 500k keys、CPU stable sort byte-for-byte) で sort 順序保存を事前確証、PSNR drift は radix_sort 経路に依らない、rasterize input の splat 順序は両経路で identical
  • M5 baseline (8 scene mean 33.49 dB、Lego val 36.106 dB) は影響なし、default OFF 維持で current production unchanged

5. 教訓 / 卒論 narrative

  • 「CPU 介在を on-GPU に置換」族の audit 予測は overlap の存在を見落とす: wait 時間を idle と仮定すると net improvement に見えるが、実際は CPU work と overlap している場合 net 0 / negative。同 family の Tier 2 backward TBDR (tile-local accumulator + Kahan で global atomic 除去) も 同 mechanism 影響を受けるため skip 判断強化
  • StorageModeShared での implicit fence は invisible cost: Metal docs に明示されておらず profile でも encoder 境界として現れず、kernel time が膨らんで見える。Apple TBDR optimization では encoder 数最小化 + buffer dependency 最小化 が原則、本 F.3 は 3 encoder + 2 dependency で原則逆行
  • audit calibration 5 連続 overestimate: Phase E refine (5x → 0.2%)、target_upload (5.6% → 0.23%)、F.1 emit_simd (-0.7% → +4.7%)、F.1 f16_fwd (-0.5% → noise)、本 F.3 (-0.5% → +7.4%)。audit theoretical prediction は overlap / fence / dispatch overhead 等の hidden cost を低見積、empirical verification 必須
  • §5.4 negative findings 章に追記候補: 「audit theoretical predictions vs empirical measurements」方法論 paragraph に本 F.3 を 5 例目として、特に 「removing CPU intermediates between GPU dispatches introduces implicit fences」 という mechanism family-level の lesson
  • §6 future work から radix GPU prefix scan を除外: kernel + tests は env opt-in で残置 (large-scale workload で再評価可能な状態)、しかし "axis 1 future work" 推奨枠からは除外。同 family の backward TBDR も併せて除外を提案

6. 次のアクション (advisor 助言反映)

  1. 本 finding commit + main pickup 判断: kernel + tests + env-gate default OFF を main 取り込み (regression なし、future workload hedge として価値、5 連続 audit-overestimate の data point として卒論 narrative)
  2. Tier 2 backward TBDR: skip 強化 — 同 mechanism family (CPU 介在を on-GPU に置換) の falsification 拡大適用、6-8h + MED-HIGH PSNR risk は poor bet given today's 5 連続 calibration
  3. audit Tier 1 + Tier 2 即 actionable 候補は全消化、Tier 3 (SSIM kernel fusion / fwd_rasterize imageblock) は ROI ≲ 0.2% で投資価値低。axis 1 future work 候補は実質枯渇、卒論 §6 では「測定可能な per-kernel 上限ありだが、Apple Silicon native optimization の構造的 ROI は Phase D 達成時点で plateau」と honest reporting
  4. 並行価値タスク: §5.4 negative findings 章への F.3 + 5 連続 overestimate paragraph 追加、Phase D 完遂後 axis 1 narrative の plateau 認定として整理

7. 再現手順

# Build (worktree)
cd splat
cargo build --release

# Bit-exact validation (9 tests, ~0.1s)
cargo test --release -p splat-metal --lib tile_bin

# A/B (5k Lego smoke、~2 min each)
# baseline = default (legacy CPU scan、env 不要、本 commit の現状)
./target/release/splat train \
    --config configs/2026-05-25-2030-lego-phase-f3-baseline-5k.toml
# → ts_fwd_radix_sort ~4.77 ms/call、total ~115s、PSNR ~31.6 dB

# GPU scan opt-in (env で明示的に enable)
SPLAT_RADIX_GPU_SCAN=1 ./target/release/splat train \
    --config configs/2026-05-25-2030-lego-phase-f3-gpu-scan-5k.toml
# → ts_fwd_radix_sort ~6.4-6.7 ms/call (+35-41%)、total ~118-124s (+2.5-7.4%)、PSNR parity

8. 関連

  • audit baseline: p1-axis1-metal-opt-audit (Tier 2 radix GPU prefix scan -0.5-0.8% wallclock 予測)
  • 本 F.3 baseline profile: p1-profiling-clean (radix_sort 13.6% share)
  • Tier 1 falsification 先行例: p1-axis1-phase-f1-emit-simd-falsified (emit_simd / f16_fwd の 2 連続 falsified)
  • 同 pattern (audit theory 予測 → empirical 棄却): p1-e-refine-gpu-smoke (refine GPU 化 5x → 0.2%)、p1-axis1-target-cache (5.5% → 0.23% async overlap で 1/25)、a-6-f16-packed-rebench (-50% bandwidth → -1%)
  • Phase D baseline (M5 達成): p1-d-multi-scene-rechain
  • 卒論統合候補 chapter: chapter-5-4-negative-findings (axis 1 audit predictions section、5 連続 overestimate)
02

関連する run

この finding が観測された / 言及している実験 run。

03

関連 finding

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