Layer 1 (CB 区間 union) と Layer 2 (IOReport、sudo 不要) を同時に回し、replay bench n=3 ×2 regime / 12.5k full run 1 本 / msplat 交互 7 run で計測。<strong>busy% は bench 68.9% (final) / 67.5% (iter5000)、full run 補正値 68.6% の 3 系統が一致</strong> — P1 の「~30%」は<strong>同 doc 内で既に自己訂正済みの誤読</strong> (30% は CPU 側 encoding の取り分、GPU compute は ~70% と書かれていた)、P2 の「~82%」は予算式の過大評価。msplat との交互実測 (splat n=3 / msplat n=4、AC・warm) で <strong>GPU 平均電力は splat 18.95 W vs msplat 26.92 W = 0.704×</strong>、これは busy 比 (0.689) とほぼ一致するため <strong>H1 (bubble) だけで説明でき H2 (activity factor) は null</strong>。<strong>H3 は逆向きに棄却</strong>: 本実装は P15 (最高 P-state) に 98% 居座り、DVFS で落とされているのは<strong>電力上限に当たっている msplat の方</strong> (P15 residency 5.7〜89%)。wallclock が 1.273× 長いぶん W の優位は相殺され、<strong>完走までの GPU energy は 0.897×、package energy は 1.062×</strong>。
commit 26cdc92 で入れた 2 層計装 — Layer 1 (完了 command buffer の GPU 実行区間を union して busy% と同期税を出す) と Layer 2 (IOReport private framework、sudo なしで GPU energy [J] と P-state residency を読む) — を AC 電源下で初めて本格運用した。加えて本 session で splat power-sample (standalone サンプラ) を足し、msplat (別プロセス) の消費電力を本実装と同一の計測器で取れるようにした。splat bench --power は本実装の計測区間を内側から括るので他実装には使えない、という穴を塞ぐためである。
Layer 2 の active(P-state) は負荷下で常に 94〜100% を示すが、これは 「GPU が電源 OFF 状態でない時間の割合」であって稼働率ではない。CB 間の bubble 中も GPU は P15 に居座ったままなので active に数えられる。稼働率は必ず Layer 1 の busy_pct (CB 区間 union ÷ wall) を使うこと。逆に Layer 1 の busy% も CB 粒度であり、CB 内の occupancy 低下 (SIMD divergence 等) は見えない — つまり busy% が高いことは CB レベルの bubble が無いことしか意味しない。
| 計測系統 | 条件 | busy% | ms/iter | busy ms/iter | sync-tax ms/iter | 健全性 |
|---|---|---|---|---|---|---|
| replay bench (final) | 409,660 splats、chained、n=3 | **68.9%** | 18.178 | 12.498 | 5.662 | cb_dropped=0 / overlap 1.00× / σ 0.30% |
| replay bench (iter5000) | ckpt_005000、chained、n=3 | **67.5%** | 16.769 | 11.312 | 5.464 | cb_dropped=0 / overlap 1.00× / σ 0.48% |
| 12.5k full run (実測値) | profile=true、wall 236.09s | 62.8% | 18.89 | 11.85 | 7.03 | cb_dropped=0 / cb 27.0 per iter |
| 12.5k full run (**補正値**) | busy 148.169s ÷ 同日 profile=false wallclock 216.12s | **68.6%** | 17.29 | 11.85 | 5.44 | profiling の host overhead を分母から除いた推定 |
profile=true は host 側に計測 overhead を足す (分母が伸びる) だけで GPU 実行時間 (分子) は変えない。実際 busy 148.169s は profile ON/OFF で不変とみなせるので、同日の profile=false wallclock (n=3 mean 216.12s) を分母に置き直すと 68.6% となり bench の 68.9% / 67.5% と一致する。profile=false では busy 行そのものが出ないので直接測定はできない — この 68.6% は 2 つの実測値 (148.169s と 216.12s) から作った推定値である旨を明示しておく。
| 推定 | 出所 | 実際に何を測っていたか | 判定 |
|---|---|---|---|
| **~30%** | P1 調査 (`phase5-step30-profile.md` 由来) | 同 doc の表は STIME/(UTIME+STIME) = 70%、Rust user code は CPU 時間の 30% のみ と書いている。これは CPU 時間の内訳であって GPU 稼働率ではない。しかも同 doc の「誤解 2」節が既に自己訂正しており、「~55ms (~70%) = GPU compute ← CPU は blocked、GPU が物理的に計算」「~25ms (~30%) = CPU 側 encoding」と正解を書いている | **誤読**。roadmap v2 が「P1 調査は ~30%」と引いたのは CPU 側の取り分を GPU busy% に読み替えたもの。P1 時点の正しい読み (~70% GPU compute) は今回の実測 68.9% とほぼ一致していた |
| **~82%** | P2 opus-5 調査の「予算式」(roadmap v2 §並列性能軸の編入) | repo 内の doc に式が残っておらず**基礎データを復元できない**。roadmap の記述は「GPU busy 推定 ~82% (P2 予算式)」のみ | **過大評価** (実測比 +13pt)。式が復元できない以上、原因は特定せず「予算式が per-kernel の host 時間 (wait 込み) を GPU 時間として積み上げた可能性がある」という**仮説の域を出ない**ことを明記しておく |
| run (実行順) | wallclock | GPU W (steady) | CPU W | package W | GPU J (完走) | package J | P15 residency |
|---|---|---|---|---|---|---|---|
| idle (pre) | 60.1s | 0.17 | 11.14 | 11.31 | — | — | 0.0% |
| splat 12.5k r1 | 219.35s | 18.79 | 6.46 | 25.25 | 4,122 | 5,538 | 67.8% |
| msplat 30K r1 | 176.09s | 20.69 | 3.56 | 24.25 | 3,643 | 4,270 | 5.7% |
| msplat 30K r2 | 161.37s | 30.69 | 2.12 | 32.81 | 4,953 | 5,295 | 89.1% |
| splat 12.5k r2 | 222.71s | 17.37 | 8.57 | 25.94 | 3,869 | 5,776 | 83.8% |
| msplat 30K r3 | 173.93s | 27.80 | 3.54 | 31.34 | 4,836 | 5,452 | 52.7% |
| splat 12.5k r3 | 206.31s | 20.69 | 4.59 | 25.28 | 4,268 | 5,216 | 95.7% |
| msplat 30K r4 | 167.68s | 28.51 | 5.70 | 34.21 | 4,781 | 5,737 | 74.5% |
| idle (post) | 60.1s | 0.07 | 3.99 | 4.06 | — | — | 0.0% |
| **splat mean (n=3)** | **216.12s** | **18.95** | 6.54 | 25.49 | **4,086** | 5,510 | 82.4% |
| **msplat mean (n=4)** | **169.77s** | **26.92** | 3.73 | 30.65 | **4,553** | 5,188 | 55.5% |
| **比 (splat / msplat)** | **1.273×** | **0.704×** | 1.754× | 0.832× | **0.897×** | **1.062×** | — |
各 run は splat power-sample --interval 5 を workload と同時に走らせ (sampler duration は splat 200s / msplat 150s)、GPU W が peak の 50% を超えた最初のサンプル以降を steady 区間として時間加重平均した (dataset load / Python import の prologue を固定 offset でなく plateau 検出で落とすため)。完走 J は steady W × train-loop wallclock。CPU 列は信用してはいけない: IOReport は SoC-wide で、本 session 中は別プロジェクト (otksynth) の CPU 負荷と Claude エージェント自身が乗っており、idle 床が 3.99〜11.14 W と揺れた。GPU 列は idle 床 0.07〜0.17 W なので clean。package 行は参考値。
msplat は 20.69 → 30.69 → 27.80 → 28.51 W と 初回だけ 20.7 W まで落ち込み、以降 28〜31 W で安定した。同時に P15 residency が 5.7% → 89.1% → 52.7% → 74.5% と動いており、「電力/熱の余裕が無いときは clock を落として 20 W に留まり、余裕が出ると P15 で 30 W まで引き上げる」という power-limited な挙動そのもの。r1 は cold 開始 + ファン未加速の局面にあたる。一方 splat は 17.37〜20.69 W と一貫して 19 W 前後で、P15 residency が 67.8〜95.7% と高いのに W は上がらない — 電力上限に当たっておらず、demand-limited であることを示す。この非対称性が下の H3 判定の直接証拠になる。
| 仮説 | 判定基準 (P2 調査の判定表) | 実測 | verdict |
|---|---|---|---|
| **H1 — bubble (busy% が低い)** | W 比 ≈ busy 比 なら H1 で説明しきれる | W 比 **0.704**、busy 比 **0.689** (68.9% ÷ msplat 想定 100%)。両者は 2pt 差で一致 | **採用 — H1 単独で W 差を説明できる** |
| **H2 — activity factor (同 busy でも瞬時電力が低い)** | W 比が busy 比より**さらに低い**なら H2 が上乗せされている | W 比 0.704 は busy 比 0.689 より**わずかに高い** (= H2 の余地は負方向) | **null — 追加要因として不要**。本実装の kernel が msplat より電力密度が低いという証拠はない |
| **H3 — DVFS (bubble が P-state を下げ超線形に電力減)** | 本実装の P-state residency が低クロック側に寄っていれば H3 | 本実装は bench で **P15 = 97.6〜98.0%** (n=3、final regime)、full run でも 67.8〜95.7%。落ちているのは **msplat 側** (P15 5.7〜89.1%) | **棄却 (かつ逆向き)** — DVFS で clock を落とされているのは電力上限に当たる msplat であって本実装ではない |
busy 比の分母に置いた msplat ~100% は 実測値ではない。msplat には Layer 1 計装が無く、「1 CB / 0 wait だから CB レベルの bubble は原理的に存在しない」というアーキテクチャ上の主張から置いた値である。仮に msplat の実 busy% が 90% だとすると busy 比は 0.766 となり、W 比 0.704 との差 (-8%) が H2 の寄与として残る。H2 が完全に null と言い切れるのは msplat 側 busy% が 95% 以上のときだけであり、この前提は本 finding の限界として記録しておく。
元の観察 (本実装のファン騒音が msplat の半分以下) は 2× の電力差としては裏付けられなかった。package 平均は splat 25.49 W vs msplat 30.65 W = 1.20× にすぎず、GPU 単体でも 1.42× である。ただし msplat は瞬間 34〜35 W まで振れており (splat の最大は 24 W 帯)、ファン回転数は消費電力に対して強い非線形、体感騒音はさらに回転数の 2.5〜3 乗に効くため、「20% の package 電力差 + ピーク 1.5×」が体感 2× の騒音差を生むこと自体は矛盾しない。すなわち観察は正しいが、その機構は『本実装の GPU が半分しか働いていない』ではなく『msplat が電力上限に張り付き、本実装は上限に届かない』である。
| iter | splats | window ms/iter | window busy% | window busy ms | window sync-tax ms | cb/iter |
|---|---|---|---|---|---|---|
| 500 | 562 | 5.99 | 41.4% | 2.48 | 3.51 | 27.0 |
| 1,000 | 8,210 | 8.46 | 51.9% | 4.40 | 4.07 | 27.0 |
| 1,500 | 72,182 | 11.74 | 66.8% | 7.85 | 3.89 | 27.0 |
| 2,500 | 253,518 | 13.24 | 63.2% | 8.37 | 4.87 | 27.0 |
| 4,000 | 349,303 | 17.33 | 65.7% | 11.38 | 5.95 | 27.0 |
| 5,500 | 383,397 | 18.62 | 66.0% | 12.30 | 6.32 | 27.0 |
| 7,000 | 398,049 | 23.48 | 56.4% | 13.23 | 10.25 | 27.0 |
| 8,500 | 403,984 | 19.82 | 67.2% | 13.32 | 6.50 | 27.0 |
| 10,500 | 406,727 | 22.72 | **71.6%** | 16.27 | 6.44 | 27.0 |
| 11,500 | 407,700 | 32.93 | 45.1% | 14.84 | 18.08 | 27.0 |
| 12,500 | 409,140 | 26.34 | 54.4% | 14.32 | 12.02 | 27.0 |
| kernel (leaf) | final regime gpu_ms/iter | share | iter5000 gpu_ms/iter | share | host ms/iter (final) | 同期税 (host−gpu) |
|---|---|---|---|---|---|---|
| ts_fwd_radix_sort | 3.564 | **26.3%** | 2.975 | **27.2%** | 6.030 | 2.466 |
| ts_backward_raster (+project_back) | 4.556 | **33.6%** | 3.808 | **34.8%** | 5.319 | 0.763 |
| ts_adam | 1.879 | 13.8% | 1.692 | 15.5% | 2.753 | 0.874 |
| ts_fwd_rasterize | 1.611 | 11.9% | 1.314 | 12.0% | 1.798 | 0.187 |
| ts_ssim_fwd_grad | 1.354 | 10.0% | 1.263 | 11.5% | 2.775 | 1.421 |
| ts_fwd_project | 0.364 | 2.7% | 0.329 | 3.0% | 0.854 | 0.490 |
| ts_fwd_emit_pairs | 0.130 | 1.0% | 0.107 | 1.0% | 0.586 | 0.456 |
| ts_l1_combine_ssim | 0.103 | 0.8% | 0.097 | 0.9% | 0.300 | 0.197 |
| ts_fwd_extract_off | 0.013 | 0.1% | 0.012 | 0.1% | 0.160 | 0.147 |
| **leaf 合計** | **13.574** | 100% | **11.597** | 100% | 20.575 | 7.001 |
| (参照) 実測 GPU busy | 12.874 | — | 11.714 | — | 19.254 (= ms/iter) | 6.480 |
(1) label は入れ子なので ts_forward (親) は除いて leaf のみを並べた。leaf 合計 13.574 は実測 busy 12.874 を 5.4% 上回るが、これは非ブロッキング回収 (gputime::harvest) が wait なし commit の CB を後続 label に重複帰属させるためで、全体値は必ず gpu_busy_stats() を使うという timing.rs の注記どおり。(2) ts_project_back の gpu_ms は ts_backward_raster とビット単位で同値 (final 4.556 / iter5000 3.808) で、同一 CB 集合の二重計上。分離できないため 1 行に統合した。(3) host 列との差 (同期税) は per_kernel mode 固有で、default (chained) では loss chain 側が 1 CB に畳まれているため既に回収済み。
| HW-3 律速仮説 | 検証可能な列 | 実測 | verdict |
|---|---|---|---|
| radix_sort が forward の律速 | leaf gpu_ms share | forward chain 5.682 ms のうち radix が **3.564 ms = 62.7%**。GPU 全体でも 26.3%(両 regime 安定) | **支持** — 単一 kernel として最大。ただし S2 (hierarchical scan) が狙うのは radix の *CB/wait 数* であって kernel 時間ではない点に注意 |
| backward_raster が全体の律速 | leaf gpu_ms share | 33.6% (project_backwards と分離不能な合算値)。Phase 5 step30b 時点の 52.1% からは大幅低下 (W1 warp sparse skip の -35.2% が効いている) | **部分的に支持** — 依然 単独最大ブロックだが、もはや過半ではない |
| ssim が重い | leaf gpu_ms share | 10.0〜11.5%。host 側では 2.775 ms だが GPU は 1.354 ms — **差 1.42 ms は同期税**で、W3a (float4 化) の GPU 側 upside は小さい | **下方修正** — W3a の優先度を下げる根拠 |
| adam が無視できる | leaf gpu_ms share | 13.8〜15.5%。5 param group の合計として想定より大きい | **反証** — 無視できない。ただし W2 Stage A で CB は既に 1 本 |
| chained は host 時間だけを削る (GPU 時間は不変) | chained vs per_kernel の実測 busy ms/iter (leaf 合計ではなく union 値で比較) | final 12.498 (chained) vs 12.874 (per_kernel) = **-2.9%**、iter5000 11.312 vs 11.714 = **-3.4%**。ms/iter 差 (-5.6% / -6.2%) の**半分程度は GPU 側実時間の短縮** | **部分的に反証** — chained は host 同期税だけでなく GPU 側実時間も 3% 前後削っている (CB 立ち上げ / 終了の firmware コストが CB 数 36 → 27 に比例して減るため) |
| regime / mode | ms/iter | busy% | busy ms/iter | sync-tax ms/iter | GPU W | J/iter | P15 residency |
|---|---|---|---|---|---|---|---|
| final / **chained (default)** | 18.178 | **68.9%** | 12.498 | 5.662 | 19.33 | 0.350 | **97.6〜98.0%** |
| final / per_kernel | 19.254 | 66.3% | 12.874 | 6.480 | 19.49 | 0.375 | 80.7〜80.9% |
| iter5000 / **chained (default)** | 16.769 | **67.5%** | 11.312 | 5.464 | 16.40 | 0.276 | 94.6〜96.9% |
| iter5000 / per_kernel | 17.869 | 65.6% | 11.714 | 6.155 | 19.15 | 0.346 | 80.8〜94.3% |
chained の 2 regime は session 前半 (cold)、per_kernel の 2 regime は session 後半 (warm、msplat interleave 後) に取っており、thermal / 電力上限の状態が違う。実際 iter5000 の W は chained 16.40 / per_kernel 19.15 と逆転しているが、これは submission の差ではなく機体状態の差である (§2 で見たとおり warm な方が P15 を掴みやすい)。mode 間で意味があるのは ms/iter / busy% / sync-tax / P15 residency の 4 列だけ。W の mode 比較が要るなら 1 session 内で交互に取り直すこと。
同一 workload で submission だけを per_kernel に戻すと (CB 27 → 36 per iter)、P15 residency が 98% → 81% に落ち、P3/P4/P6/P8/P10/P12/P14 が立つ。これは「短い dispatch が断続すると DVFS が上がりきらない」という H3 の機構そのものが実在することの直接証拠である。default (chained) スタックでその影響が無視できるのは、W2 で CB 数を既に十分減らしてあるから。つまり H3 は『機構としては真、現行 default では効いていない』。逆に S2/S3 で CB をさらに減らしても、P15 は既に 98% なので DVFS 経由の上積みは期待できない — S2 の期待値は純粋に bubble (sync-tax 31%) の回収分に限られる。
この finding が観測された / 言及している実験 run。