`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`) も同梱。
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 から除外した。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 を実施した。
同一 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 判定に使わない。
| metric | 値 |
|---|---|
| ply | runs/lego-gap-baseline-12500/final.ply (407,267 splats) |
| view | 0 (train split、固定) |
| warmup / iters / repeats | 20 / 200 / 10 |
| median ms/iter | 28.312 |
| mean ms/iter | 28.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 交絡) |
既存 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 で計測した。
| regime | iter | num_splats | median ms/iter | mean | σ (ms) | σ_pct | 閾値 max(2σ,1%) |
|---|---|---|---|---|---|---|---|
| 早期 (iter 1000) | 1000 | 8,224 | 13.462 | 13.377 | 0.621 | 4.65% | 9.29% |
| 中期 (iter 5000) | 5000 | 373,933 | 30.038 | 30.039 | 0.373 | 1.24% | 2.49% |
| 後期 (final = 12500) | 12500 | 409,660 | 31.515 | 31.415 | 0.901 | 2.87% | 5.74% |
| 項目 | 内容 |
|---|---|
| `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 |
| `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` も同様に共有化) |
[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] のみsplat/crates/splat-cli/src/cmd/bench.rs、共有 util: splat/crates/splat-cli/src/util.rssplat/crates/splat-train-v1/src/train_loop.rs::train_loop (`checkpoint_iters` / `on_checkpoint`)splat/crates/splat-train-v1/src/timing.rs::snapshotdocs/architecture/backend-and-compiler-conventions.mdsplat/runs/lego-bench-calib-checkpoints/ (result.toml / config.toml のみ commit)splat/runs/bench-calib/{sigma-final,regime-1000,regime-5000,regime-final}.toml (gitignore 対象、再生成可能)この finding が観測された / 言及している実験 run。