1× 工程表 v2 完了 (S1→W1→W2→S2→S3) 後の最終スタックを msplat wheel v1.1.3 と <strong>同日・同一 M4 Max・同一 init 点群・同一 evaluator</strong> で interleave 比較した。<strong>msplat 30K n=4 = 165.42s / native val 36.563 dB</strong>、本実装 Lego 12.5k n=2 = <strong>152.68s (train elapsed) / 155s (process)</strong> — <strong>wallclock gap は 2026-08-16 の 2.10× から 0.927× へ反転</strong> (gap の分母は interleave pair である block 1 の msplat n=2 mean <strong>164.69s</strong>。msplat 全 4 run の n=4 mean 165.42s を分母にすると 0.923×)し、<strong>per-splat·iter throughput の劣位も 2.62× → 1.135× (ほぼ parity)</strong> に縮んだ。<strong>GPU energy 0.906× / package energy 0.896×</strong> で、counter session が「package では 1.062× で負け」と記録したエネルギー断面も反転した (ただし機構は入れ替わっており、W 比は 0.704× → <strong>0.978×</strong> = ほぼ同電力で短時間、が新しい説明)。PSNR の matched 断面は前回同様 convention で割れる: <strong>brush convention では 6,000 iter (51.60s = 0.313×) で msplat 30K の best (34.243 dB) を +0.67 dB 上回る</strong>一方、<strong>white convention では 30,000 iter (479.91s = 2.888×) まで延ばしても -0.81 dB で matched 点が存在しない</strong>。roadmap v2 が留保していた「12.5k で native 比 -0.34 dB」は <strong>iter 不足ではなく convention 差</strong>であることが 30k の天井 (self 36.455 dB) で確定した。
2026-08-16 の parity finding (p2-msplat-parity-lego) と同一 protocol — 同日・同一 M4 Max・同一 dataset・同一 culled init.ply (5207 点)・train 100 view 全量・同一 evaluator・interleave 直列実行 — を、1× 工程表 v2 完了後の最終スタックで再実施した。最終スタック = default 全採用 (binning=resident / raster_backward_range=last_contributor / loss_path=gpu / forward_submission=chained / radix_scan=gpu_hierarchical / pipelining=depth2、profile=false) で、backend block は p2-s3-iteration-pipelining の depth2 arm と完全一致。全 run を同一 config family (refine.stop_iter = max_steps) で回し、6.0k / 12.5k / 13.3k / 30k が単一の Pareto curve として読めるようにしてある。
convention 非依存で勝った: wallclock (12.5k で 0.927×)、matched-wallclock 断面 (13.3k で 1.007× / brush +1.94 dB)、GPU energy (0.906×) / package energy (0.896×)。near-parity まで詰めた: per-splat·iter throughput (msplat 1.135× — 2026-08-16 の 2.62× から)。勝ったと言えない: white convention の PSNR (12.5k -1.10 dB、30k まで延ばしても -0.81 dB で matched 点なし)、matched-iteration 断面 (30K vs 30K で 2.888× 遅い)、per-splat·iter throughput (まだ 13.5% 負け)。撤回する主張: 「12.5k の 36.202 dB は msplat native 36.539 dB に 0.34 dB 足りない」— これは premultiplied self-eval と white native の異 convention 比較であり、30k の天井 (36.455 dB) がその差を iter 予算で埋められないことを示している。
| run (実行順) | block | iter | train elapsed | process | final splats | 自己 eval | brush cross-eval q8 | white cross-eval | 窓内 load1 avg/max |
|---|---|---|---|---|---|---|---|---|---|
| msplat r1 | 1 | 30,000 | 165.65s | 169s | 194,370 | 36.554 (white, native) | 33.857 | 36.271 | 3.60 / 4.07 |
| splat 12.5k r1 | 1 | 12,500 | 153.03s | 155s | 407,501 | 36.215 (brush, trainer) | 36.252 | 35.154 | 3.82 / 5.28 |
| msplat r2 | 1 | 30,000 | 163.73s | 168s | 191,209 | 36.559 (white, native) | 33.038 | 36.252 | 3.09 / 4.42 |
| splat 12.5k r2 | 1 | 12,500 | 152.33s | 155s | 408,111 | 36.138 (brush, trainer) | 36.174 | — | 2.67 / 3.13 |
| splat 13.3k (matched-wall) | 2 | 13,300 | 165.85s | 168s | 414,205 | 36.154 | 36.182 | 35.082 | 3.22 / 3.94 |
| splat 6.0k (matched-PSNR brush) | 2 | 6,000 | 51.60s | 54s | 319,452 | 34.890 | 34.913 | 33.756 | 2.63 / 2.98 |
| splat 30k (**破棄**: 窓内汚染) | 2 | 30,000 | (498.27s) | (502s) | 468,527 | 36.440 | 36.501 | — | **5.61 / 8.81** |
| splat 12.5k r3 (bracket) | 2 | 12,500 | 164.94s | 168s | 411,192 | 36.190 | 36.219 | — | 3.33 / 3.77 |
| splat 12.5k r4 (bracket) | 2 | 12,500 | 168.05s | 171s | 408,521 | 36.215 | 36.255 | — | ~4.5 |
| splat 12.5k r5 (bracket、厳格 quiesce) | 2 | 12,500 | 162.78s | 165s | 405,173 | 36.127 | 36.158 | — | 2.67 (gate 値) |
| msplat r3 (co-located anchor) | 3 | 30,000 | 166.33s | 170s | 195,984 | 36.565 (white, native) | 34.118 | 36.294 | 3.22 (gate 値) |
| splat 30k r2 (**採用**) | 3 | 30,000 | 479.91s | 483s | 467,752 | 36.455 | 36.498 | 35.440 | 2.40 (gate 値) |
| msplat r4 (co-located anchor) | 3 | 30,000 | 165.97s | 170s | 196,404 | 36.575 (white, native) | 34.243 | — | 2.77 (gate 値) |
splat eval --convention brush --quantize-8bit (val 100 view) に通した同一 evaluator 値。「white cross-eval」= 本実装 renderer で描画 → α 合成 → white GT (前回と同一の妥協点)。本実装の brush no-quant 値は trainer 自己 eval と 4 桁一致 (例 36.2147 vs 36.215) で evaluator 整合性を確認済み。| 断面 | 比較内容 | 比 (指標は行ごと) | PSNR (同一 evaluator) | 判定 |
|---|---|---|---|---|
| **公式 12.5k** | splat 12.5k (152.68s、n=2 mean) vs msplat 30K (164.69s、block 1 n=2 mean) | **0.927×** | brush **+1.97 dB** (36.21 vs 34.24) / white **-1.10 dB** (35.15 vs 36.25) | **勝ち (wallclock)** / PSNR は convention 依存 |
| **matched-wallclock** | splat 13,300 iter = 165.85s ≒ msplat 164.69s | **1.007×** (+0.70%) | brush **+1.94 dB** (36.18 vs 34.24) / white **-1.17 dB** (35.08 vs 36.25) | 同時間・brush では勝ち / white では負け |
| **matched-PSNR (brush)** | splat 6,000 iter (34.913 dB) > msplat 30K best (34.243 dB) | **0.313×** (51.60s vs 164.69s) | **+0.67 dB** | **勝ち — 3.2 倍速で同等以上** |
| **matched-PSNR (white)** | msplat 30K 最小 36.252 dB に届く本実装 Pareto 点 | **存在しない** | 30k (479.91s) でも 35.440 = **-0.81 dB** | **負け — 断面が取れない** |
| **matched-iteration (30K vs 30K)** | splat 30k (479.91s) vs msplat 30K (166.15s、block 3 co-located anchor) | **2.888×** | brush **+2.26 dB** / white **-0.81 dB** | **負け (wallclock)** |
| per-splat·iter throughput | ms/iter ÷ iteration 平均 splats (splat 322k / msplat 165k) | **[throughput 比]** msplat **1.135×** 高速 | 37.9 vs 33.4 ns/splat·iter | near-parity、まだ 13.5% 負け |
| GPU energy (参考) | steady GPU W × train elapsed | **[energy 比]** **0.906×** | 29.38 W × 152.68s = 4,486 J vs 30.05 W × 164.69s = 4,949 J | **勝ち** |
| package energy (参考) | 同上 (CPU 込み、SoC-wide のため参考値) | **[energy 比]** **0.896×** | 4,748 J vs 5,298 J | **勝ち** (counter session の 1.062× から反転) |
| 指標 | 2026-08-16 (Phase 2 default スタック) | 2026-08-17 (最終スタック) | 差 |
|---|---|---|---|
| **wallclock gap @ 12.5k** | 2.10× (352.3s vs 167.7s) | **0.927×** (152.68s vs 164.69s) | **2.27 倍改善 / 1.0× を跨いだ** |
| 本実装 12.5k train elapsed | 352.27s | **152.68s** (n=2 mean) | **-56.7%** |
| **per-splat·iter throughput** | msplat 2.62× (88.9 vs 33.9 ns) | msplat **1.135×** (37.9 vs 33.4 ns) | **劣位 2.62× → 1.135×** |
| matched-wallclock 点 | 7,500 iter (181.4s = 1.08×) | **13,300 iter (165.85s = 1.007×)** | 同時間で回せる iter が **1.77 倍** |
| matched-PSNR (brush) 点 | 7,500 iter = 1.08× | **6,000 iter = 0.313×** | **3.4 倍速い断面へ** |
| matched-PSNR (white) 点 | 存在しない (12.5k で -1.09 dB) | 存在しない (**30k で -0.81 dB**) | 30k まで検証して不在を確定 |
| msplat 30K wallclock | 167.7s (n=2) | 165.42s (n=4) | -1.4% (日内再現、同一 wheel) |
| msplat 30K native PSNR | 36.53 dB (n=2) | 36.563 dB (n=4) | +0.03 dB (再現) |
| GPU 平均 W (splat / msplat) | 18.95 / 26.92 = 0.704× (counter session) | 29.38 / 30.05 = **0.978×** | **低 W の優位は消滅** (busy 77.5→91.3% の帰結) |
| GPU energy 比 | 0.897× (counter session) | **0.906×** | ほぼ同値だが**機構が入れ替わった** |
| package energy 比 | 1.062× (**負け**) | **0.896×** (勝ち) | **反転** |
p2-s3-iteration-pipelining と roadmap v2 は「等価 gap 0.907× は matched 断面ではない — 本実装 12.5k の 36.202 dB に対し msplat 30K native は 36.539 dB で 0.34 dB 低い」と留保していた。本 finding はこれを 2 方向から潰した。(a) その 0.34 dB は異 convention 比較である — 36.202 は premultiplied (brush) 学習/評価、36.539 は white-bg 学習/評価であり、同一 evaluator に揃えると brush では本実装 +1.97 dB、white では -1.10 dB と符号が反転する。(b) iter 予算では埋まらない — 同一 config family で 30,000 iter まで延ばした本実装の self-eval は 36.455 dB で天井 (Phase H の 36.384 dB と整合)、white cross-eval でも 35.440 dB にしか届かず msplat の 36.252 dB に -0.81 dB 残る。12.5k → 30k は wallclock 3.14 倍 (152.68 → 479.91s) を払って white で +0.29 dB しか買えないので、外挿しても matched 点には到達しない。よって roadmap v2 の caveat box は convention 別の 2 断面 (brush: 0.313× で勝ち / white: 断面不在) に置き換える。
| iter | train elapsed | self-eval (brush) | brush cross-eval q8 | white cross-eval | white で msplat 36.252 との差 |
|---|---|---|---|---|---|
| 6,000 | 51.60s | 34.890 | 34.913 | 33.756 | -2.50 dB |
| 12,500 | 152.68s (n=2 mean) | 36.177 (n=2 mean) | 36.213 (n=2 mean) | 35.154 | -1.10 dB |
| 13,300 | 165.85s | 36.154 | 36.182 | 35.082 | -1.17 dB |
| 30,000 | 479.91s | 36.455 | 36.498 | 35.440 | **-0.81 dB** |
refine.stop_iter = max_steps)。brush 側は 12.5k 以降ほぼ飽和 (12.5k → 30k で +0.28 dB に対し wallclock 3.14 倍)、white 側も 30k で -0.81 dB 残る。13.3k が 12.5k をわずかに下回るのは run 間分散の範囲 (12.5k は 5 run で 36.127〜36.215)。docs/findings/data/gpu-util-histogram-20260817/) では、msplat 側に深い dip が 8 本 (block 幅の 40.5%)、本実装側は 3 本 (2.8%) と出ており、本実装のほうが GPU を連続的に埋めていることを定性的に裏づける。ただしこれは screenshot の pixel 解析なので数値としては引用せず、傾向の傍証に留める。| run | steady GPU W | ±sd | steady CPU W (参考) | package W (参考) | train elapsed | GPU J | package J | P15 residency |
|---|---|---|---|---|---|---|---|---|
| msplat r1 | 30.42 | 2.78 | 2.06 | 32.48 | 165.65s | 5,040 | 5,381 | 74.0% |
| splat 12.5k r1 | 28.67 | 1.73 | 1.86 | 30.53 | 153.03s | 4,388 | 4,672 | 94.6% |
| msplat r2 | 29.68 | 2.03 | 2.18 | 31.87 | 163.73s | 4,859 | 5,218 | 65.7% |
| splat 12.5k r2 | 30.09 | 2.05 | 1.58 | 31.67 | 152.33s | 4,583 | 4,824 | 94.4% |
| **mean 比 (splat/msplat)** | **0.978×** | — | 0.811× | 0.967× | **0.927×** | **0.906×** | **0.896×** | — |
splat power-sample --interval 5 を workload と併走させ (sampler duration = splat 135s / msplat 150s)、GPU W が peak の 50% を超えた最初のサンプル以降を steady 区間として時間加重平均 (counter session と同一の判定)。測定の限界 (要開示): sampler は run の先頭側しか覆っておらず coverage は splat 88.4% / msplat 91.1% と非対称、J は steady W を train elapsed 全長に外挿して算出している (n=2)。両実装とも W は run 後半で上昇する (末尾 3 サンプル: splat 29.5/32.5 W、msplat 30.8/31.5 W) ためこの外挿は両者の W を過小評価し、coverage の非対称ぶんだけ splat 側を余計に過小評価する = 比を良く見せる向きのバイアス。末尾 3 サンプルで未覆区間を補正し直すと GPU energy 0.909× (補正前 0.906×) で、結論は動かない。なお prologue 側の懸念 (Python import で msplat の W が下がる) は該当せず — 両者とも第 1 サンプルから 20〜28 W に乗っており、むしろ最も低い第 1 サンプル (20.72 W) は splat 側にある。idle 床は GPU 0.03〜0.04 W で clean。CPU / package 列は IOReport が SoC-wide なので参考値。P-state residency は本実装が P15 に 94.4〜94.6% 居座り、msplat は 65.7〜74.0% (残りは P14 = 電力上限による降格) — counter session の H3 判定 (DVFS で損をしているのは msplat 側) は最終スタックでも維持。counter session は「W 比 0.704× でも wallclock が 1.273× 長いので GPU energy 0.897× / package energy 1.062× に留まり、卒論 §5 で電力効率を主張軸にはできない」と結論していた。最終スタックでは W 比が 0.978× (ほぼ同電力) に上がった代わりに wallclock が 0.927× になり、GPU energy 0.906× / package energy 0.896× と両方とも勝ちに転じた。数値だけ見ると GPU energy は 0.897× → 0.906× でほぼ動いていないが、成立機構は「GPU を遊ばせているぶん低電力」から「同じ電力で早く終わる」へ完全に入れ替わっている。S3 が busy を 77.5 → 91.3% に上げた以上、低 W 側の説明は今後も復活しない。卒論では 「W が低いから省エネ」ではなく「同電力枠で早く終わるから省エネ」と書く。なお energy は n=2 の参考値であり、gate 値ではない。
# msplat 環境 (wheel v1.1.3、torch 不要)
uv venv msplat-venv --python 3.12
uv pip install --python msplat-venv/bin/python "msplat==1.1.3" numpy pillow
# dataset 変換 (blender lego → nerfstudio 形式、white-bg composite + culled init.ply 5207 点)
LEGO=/Users/otkrickey/dev/3dgs-workspace/datasets/nerf_synthetic/lego
python docs/strategy/msplat_parity_lego.py convert --lego $LEGO --out /tmp/lego-ns
# block 1: interleave ABAB (各 run の前に load quiesce、窓内 load1 を 5s 間隔で記録)
python docs/strategy/msplat_parity_lego.py train --dataset /tmp/lego-ns --lego $LEGO \
--iters 30000 --out-ply msplat-30k-r1.ply # → 165.65s / 36.554 dB
( cd splat && ./target/release/splat train \
--config configs/2026-08-17-final-parity-12500-r1.toml ) # → 153.03s / 36.215 dB
# (msplat r2, splat 12500-r2 を同様に)
# block 2/3: matched 断面 (13300 / 6000) と Pareto 天井 (30000-r2、厳格 quiesce)
# 電力は workload と併走: ./target/release/splat power-sample --duration 135 --interval 5 --output pw.toml
# cross-eval (同一 evaluator)
python docs/strategy/msplat_parity_lego.py convert-ply --inp msplat-30k-r1.ply --out msplat-30k-r1-brush.ply
splat/target/release/splat eval --ply msplat-30k-r1-brush.ply --dataset $LEGO \
--split val --convention brush --quantize-8bit
splat/target/release/splat eval --ply splat/runs/final-parity-12500-r1/final.ply \
--dataset $LEGO --split val --convention brush
# white-bg cross-eval は 3dgs-workspace/msplat-parity-20260817/white_eval.py
docs/strategy/make_msplat_pareto.py の入力を本 finding の 4 点 (6.0k / 12.5k / 13.3k / 30k) + msplat n=4 mean に更新する (未実施)。この finding が観測された / 言及している実験 run。