rasterize forward の per-pixel RGB accumulator を half3 化 (中間計算は f32 維持) した試行。env SPLAT_F16_FORWARD=1 で kernel 切替。30k bench で PSNR 14.873 dB (-10.0 dB) / wallclock 40m40s (+75%)、二重 negative finding。
render_splats_f16 kernel (per-pixel half3 pix_out accumulator + 中間計算 f32 維持) を shaders/forward/rasterize.metal に追加し、env SPLAT_F16_FORWARD=1 で切替可能にした。30k bench (lego sh=3, seed=42, capacity=1M) で baseline 24.879 dB / 23m13s に対し PSNR 14.873 dB (-10.0 dB) + wallclock 40m40s (+75%)、精度と速度の二重悪化が確定。Negative finding。
shaders/forward/rasterize.metal に render_splats_f16 を新規追加、render_splats_f32 をコピーして float3 pix_out → half3 pix_out に変更pix_out += half3(rgb * (alpha * T))float4 は f32 維持 (loss 計算 / backward path は変更なし)、出力時に float3(pix_out) で castrasterize.rs に pso_forward_f16 field 追加、forward() 内で env 確認して pso 切替 (Trainer API は不変)half3 pix_out = half3(0.0h);
float T = 1.0f;
// 中間計算は f32 維持
float alpha = min(0.999f, splat.color_a * exp(-sigma));
float next_T = T * (1.0f - alpha);
float3 rgb = float3(splat.color_r, splat.color_g, splat.color_b);
// term は f32 で計算、half に cast して accumulate
pix_out += half3(rgb * (alpha * T));
T = next_T;
| metric | baseline (f32 forward) | A.9 (f16 forward) | Δ |
|---|---|---|---|
| PSNR (val 100 views) | 24.879 dB | 14.873 dB | -10.006 dB |
| wallclock (30k iter) | 23m13s (1393s) | 40m40s (2439.73s) | +1046.73s (+75.1%) |
| ms/iter (avg) | 46.4 ms | 81.3 ms | +34.9 ms (+75.2%) |
| ms/iter (early, iter 1-2k) | ~46 ms | ~50 ms | +4 ms (+8.7%) |
| ms/iter (late, iter 24k+) | ~46 ms | ~99 ms | +53 ms (+115%) |
| final splats | 83,734 | 94,900 | +11,166 (+13.3%) |
front-to-back α 合成では、後段 splat ほど T (transmittance) が小さくなる。具体的に T < 1e-3 領域では alpha * T * rgb が ~1e-5 以下になりやすく、これは half (IEEE 754 binary16) の normal range 下限 6.1e-5 を下回り denormal もしくは zero に丸められる。背景に近い半透明 splat の寄与が消失し、画像の low-frequency 成分が削れて PSNR が大幅低下。
Apple Silicon GPU の SIMD ALU は half と float の演算 throughput が同じ (4-wide vector で per-cycle 同数 op)。memory bandwidth bound でない workload (rasterize は threadgroup memory に 256 splat load して再利用、bandwidth ではなく compute bound) では f16 化のメリットゼロ。さらに add 1 回ごとに half3(rgb * alpha * T) の cast 命令 (3 命令) が追加され、純粋 overhead として残る。
underflow で寄与が消えた splat に対し gradient が薄まる → refine が「効果のない splat を split」する誤判定を起こし、不要な splat が増加 (final 94,900 vs baseline 83,734、+13.3%)。これがさらに per-pixel 寄与 splat 数を増やし、ms/iter を悪化させるフィードバック loop。
第 3 軸 (Apple Silicon 固有最適化) の Negative finding。「Apple Silicon は half と float が同 throughput、bandwidth bound でない rasterize の f16 化は purely loss」を実測で示した。これは brush (Vulkan / Metal) や gsplat (CUDA) も同じ理由で f16 forward を採用していないことの傍証になる。Negative finding 章 (D.3) の 4 つ目のストーリーとして組み込み可能。
GPU の f16 化は memory bandwidth bound な workload にのみ適用すべき。Rasterize forward は threadgroup memory 内再利用で compute bound、しかも IEEE 754 binary16 の normal range は alpha 合成の low-T 領域と相性が悪い。Apple Silicon に限った話ではなく一般則として記録。
render_splats_f16 kernel は shaders/forward/rasterize.metal に残置 (Negative finding として参照可能、削除しない)rasterize.rs::forward の env 切替 (SPLAT_F16_FORWARD) も残置、default OFF で baseline 同一動作cargo test 23/23 pass (env OFF 時)a-10-kahan-negative — Metal compiler の挙動という共通主題a-6-feat-g-packed-investigation — A.9 の二重 negative を考えると、A.6 の packed も trainer 統合しても同様の risk あり、ROI ~1% の追求は妥当a-7-icb-batching-results — 第 3 軸の対比として positive 1 例 / negative 2 例 (A.9 + A.10) の構造にfinal-ablation-tableこの finding が観測された / 言及している実験 run。