松田研究室
FindingID: p2-bench-harness-calibration
Status: stable / task splat bench harness (2026-08-16 GPU 専有タスク)
← Findings Index
P2 · bench harness · calibration

splat bench harness 実装 + calibration — σ_replay 0.83%、full run 比 ~6x ノイズ削減

`train_step()` (refine 抜き 1 iteration) を N 回 replay する fixed-workload bench harness を実装。同一 config n=10 replay で <strong>σ_replay = 0.234 ms/iter (mean比 0.83%)</strong> を実測し、full run で観測されていた ~10% 分散から大幅に切り離した。3-regime (iter 1000 / 5000 / final checkpoint、各 n=5) でも 1.2-4.6% の低分散を確認、有意判定閾値を <code>max(2σ, 1%)</code> と規約化。parity mode (`--parity`) と checkpoint 保存 (`[output] save_ply_at`) も同梱。

positiverank: highaccepted第 1 軸第 3 軸bench-harnesscalibrationvariancereproducibilityc7fixed-workloadgate-infralego
Δ Wallclock
σ_replay 0.83% (n=10, final-scale workload) vs full run 実測 ~10% — 約 6x ノイズ削減
Impact
`splat bench --config bench.toml` を実装し、同一 config n=10 replay で σ_replay = 0.234 ms/iter (mean 28.339 ms 比 0.83%) を実測。full training run で観測されていた ~10% 分散に対し 約 6x のノイズ削減。3-regime (iter 1000 / 5000 / final checkpoint、各 n=5) でも σ 1.2-4.6% の範囲に収まり、splat 数が少ない早期 regime ほど相対分散が大きい傾向を確認。有意判定閾値を max(2σ, 1%) と規約化し、以後の gate 判定をこの harness の計測に載せ替える。付随して `--parity` (kernel variant 間の勾配 / binning 一致検証) と `[output] save_ply_at` (checkpoint 保存) を実装、parity mode の実測で production-scale シーンの `sorted_idx` tie-order 非決定性 (~0.2%、真の非決定性であって variant 差ではない) を発見し gate から除外した。
01

本文

full training run の wallclock は run 間 ~10% の分散が観測されており、以後の全 gate 判定 (variant 採否) をこの分散から切り離すのが目的。`train_step()` (`crates/splat-train-v1/src/train_loop.rs:39`) は refine/MCMC tick を含まない — これを warmup + N 回呼ぶだけで「forward+backward+adam の fixed-workload replay」になる。`splat bench --config bench.toml` を実装し、Lego 12.5k checkpoint で n=10 / n=5 の calibration を実施した。

Headline (σ_replay 0.83%、full run 比 ~6x ノイズ削減)

同一 config (Lego final checkpoint、407,267 splats) で n=10 replay (warmup=20, iters=200) を実測: median 28.312 ms/iter、mean 28.339 ms/iter、σ = 0.234 ms/iter (0.83%)。full run で観測されていた ~10% 分散に対し概ね 6x のノイズ削減。3-regime (iter 1000 / 5000 / final の checkpoint、各 n=5) でも σ 1.2-4.6% の範囲に収まり harness の低分散性を再確認。有意判定閾値は max(2σ, 1%) と規約化し (n=10 headline では 1.65%)、以後この閾値を下回る wallclock delta は「ノイズ圏」として gate 判定に使わない。

1. σ_replay 実測 (n=10、同一 config)

metric値
plyruns/lego-gap-baseline-12500/final.ply (407,267 splats)
view0 (train split、固定)
warmup / iters / repeats20 / 200 / 10
median ms/iter28.312
mean ms/iter28.339
σ (sample stddev)0.234 ms/iter
σ_pct**0.827%**
有意判定閾値 max(2σ, 1%)**1.65%** (= 2σ、1% 下限より大)
参考: full run 実測分散~10% (docs/strategy 記載、run 間 dataset load / refine cadence 交絡)
n=10 同一 config replay の σ_replay。full run の ~10% 分散に対し約 6x のノイズ削減。

2. 3-regime calibration (iter 1000 / 5000 / final、各 n=5)

既存 baseline recipe (`configs/2026-08-16-lego-gap-baseline-12500.toml`) をそのまま流用し、新 `[output] save_ply_at = [1000, 5000]` で 1 本の training run (`lego-bench-calib-checkpoints`、final PSNR 36.226 dB / 360.96s / 409,660 splats) から 3 checkpoint を生成、各 regime を n=5 で計測した。

regimeiternum_splatsmedian ms/itermeanσ (ms)σ_pct閾値 max(2σ,1%)
早期 (iter 1000)10008,22413.46213.3770.6214.65%9.29%
中期 (iter 5000)5000373,93330.03830.0390.3731.24%2.49%
後期 (final = 12500)12500409,66031.51531.4150.9012.87%5.74%
regime が進むほど splat 数と per-iter GPU work が増え、OS scheduling jitter 等の絶対誤差が相対的に薄まって σ_pct が下がる傾向 (早期 regime ほど n を増やす運用を推奨、下記 §4)。final regime の n=5 σ (2.87%) が headline の n=10 σ (0.83%) より大きいのは同じ傾向のサンプルサイズ効果 — n=5 では σ 推定自体のノイズが大きい。

3. 実装内容

項目内容
`splat bench --config bench.toml``crates/splat-cli/src/cmd/bench.rs` 新規。`[bench]` (ply/dataset/view/warmup/iters/repeats/output) + `[backend]` (既存 `BackendConfig`) を読み、repeat 毎に `Param` を ply から reload → `train_step()` を warmup+iters 回 replay。loss recipe (`l1_ssim`, λ=0.20, ssim window=7 σ=1.0, `AdamConfig`/`McmcConfig` default) は現行 production 勝者と一致させ config で選べない固定値にした (workload の再現性を最優先、詳細は bench.rs 冒頭コメント)
`bench-result.toml` 出力ms/iter (median/mean/σ/σ_pct/per-repeat)、per-kernel `{total_ms, calls, avg_us}` (`timing::snapshot()` 経由、repeat 間で集計)、num_splats、commit hash、os_build、config snapshot (raw TOML 埋め込み)
`timing::snapshot()``crates/splat-train-v1/src/timing.rs` に `pub fn snapshot() -> Vec<(String, Duration, u64)>` を追加 (~10 行)。既存 `print_summary()` の stderr 出力と独立に、呼び出し側で好きな形式に整形できる
`--parity candidate.toml`同一 ply から baseline (`bench.toml [backend]`) と candidate (`--parity` file `[backend]`) で 1 iteration を実行し比較。grad buffer (`means_grad` 等 5 種) は既存 C7 tolerance 体系 (abs/rel 1e-4) で比較、`tile_offsets` は byte equality。判定は stdout の `PARITY ...` 行 (機械可読) + exit code (`Ok`→0 / `Err`→1、main.rs の既存変換をそのまま利用)。GPU loss path (`loss_path="gpu"`) 前提、host path は明示エラー
`sorted_idx` の扱い (発見)**gate に使わない**: production-scale シーンで同一 config でも tie 内相対順序が emit_pairs の atomic 書き込み順に依存し ~0.2% 入れ替わる (`tile_bin.rs` の既存テスト註釈が示す通り、synthetic fixture でのみ byte-exact)。variant 差ではなく既知の非決定性なので informational 表示に変更 (実測で判明、当初 spec は byte equality gate を想定していた)
`[output] save_ply_at``crates/splat-cli/src/config.rs` に `Vec` field 追加 (default 空、既存互換)。`train_loop()` に `checkpoint_iters: &[u32]` + `impl FnMut(u32, &Param)` callback を追加 (hot loop は `dyn` 禁止規約に従い generic で受ける、空 slice の線形 scan のみで per-iter alloc なし)。`train.rs` が `/ckpt_.ply` を保存するクロージャを渡す
`apply_backend_config` 抽出`train.rs` 内 private fn だったものを `crates/splat-cli/src/util.rs` に `pub fn apply_backend_config(trainer: &Trainer, backend: &BackendConfig)` として抽出、`train` / `bench` 両 subcommand が共有 (`git_short_commit` / `macos_build_version` も同様に共有化)

4. 運用ガイドライン

  • gate 判定は n>=10 repeats を推奨: n=5 の σ 推定自体がノイズを持つ (final regime で n=5→2.87% vs n=10→0.83%、同程度の workload でも 3x 以上ブレる)。variant 採否のような重要判定は n=10 以上で σ を再確定してから閾値と比較すること。
  • 早期 regime (splat 数が少ない) は相対分散が大きい: iter 1000 regime は σ_pct 4.65% と他 regime の 2-4x。絶対 ms/iter が小さい workload は OS scheduling jitter 等の固定誤差が相対的に効くため、早期 regime を gate に使う場合は repeats を増やすか閾値を緩めること。
  • 有意判定閾値 = max(2σ, 1%): σ はそのベンチ実行自身の `sigma_pct` (bench-result.toml に出力済み) を使う。固定の全体閾値ではなく、計測毎に自己算出する運用とする (workload / regime で σ が変わるため)。
  • parity mode は GPU loss path 限定: `[backend] loss_path = "gpu"` を両側で明示すること (host path は SSIM を host 側で計算し `rendered_buf` を経由しないため比較対象にならない)。

5. 使い方

[bench]
ply = "runs/lego-gap-baseline-12500/final.ply"
dataset = "/Users/otkrickey/dev/3dgs-workspace/datasets/nerf_synthetic/lego"
view = 0
warmup = 20
iters = 200
repeats = 10

[backend]
loss_path = "gpu"
splat bench --config bench.toml
splat bench --config bench.toml --parity candidate.toml   # candidate.toml は [backend] のみ

6. 関連

  • harness 実装: splat/crates/splat-cli/src/cmd/bench.rs、共有 util: splat/crates/splat-cli/src/util.rs
  • checkpoint hook: splat/crates/splat-train-v1/src/train_loop.rs::train_loop (`checkpoint_iters` / `on_checkpoint`)
  • timing snapshot accessor: splat/crates/splat-train-v1/src/timing.rs::snapshot
  • BackendConfig 規約: docs/architecture/backend-and-compiler-conventions.md
  • 3-regime checkpoint 生成 run: splat/runs/lego-bench-calib-checkpoints/ (result.toml / config.toml のみ commit)
  • raw bench 出力: splat/runs/bench-calib/{sigma-final,regime-1000,regime-5000,regime-final}.toml (gitignore 対象、再生成可能)
02

関連する run

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

03

関連 finding

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