mandelbrot/docs/NEXT_QUALITY_PLAN.md

178 lines
15 KiB
Markdown
Raw Normal View History

2026-09-11 16:05:20 +09:00
# 追加の軽量化・高品質化の実装計画
2026-09-08。B・Cを導入した現在のHTMLを読み直し、3案を検証用コピーで実測した。**本体への追加実装はまだ行っていない。** BLA、反復数削減、精度低下、数値失敗の塗りつぶしは使っていない。
ユーザーの「低性能PCなので、多少表示が重くても問題ない」という方針を反映し、**高画質では細部の改善を優先する**。軽量化で追加計算の負担を減らし、計算完了・キャンセル・パンへの応答を維持する。
## 結論と実装順
| 順序 | 実装候補 | 実測した効果 | 判断 |
| --- | --- | --- | --- |
| 1 | CPU補修の二乗に対する丸めを簡略化 | 深拡大の中央値8.99秒→7.99秒、約11.1%短縮 | 小さな変更として先に導入。ほかの視点の高速化は未確認 |
| 2 | 未脱出画素の継続計算に限り、1 dispatchの仕事量上限を400万→800万へ | 2倍解像度の中間倍率で14.17秒→12.42秒、約12.4%短縮 | 導入候補。1回の処理待ちは長くなるため操作応答を確認する |
| 3 | 「高画質」の縦横倍率を1.5倍→2倍へ | 4倍解像度の参考画像との差が約18~22%減少 | 画質優先で採用候補。計算量・メモリー増を明示する |
案3の計測では案1を併用し、案2の計測では案1+案3を共通の土台にした。**案ごとの短縮率を足し合わせない。** 解像度を上げた版が現行より軽くなった、という結果ではない。
## 読み直して確認した現在の処理
対象は [index.html](../index.html)。基準HTMLのSHA-256は `a0806944469b20152fefafe04a23e158044e127864cd9c0eef9c96894a8f5b4a`。
- `precisionFallbackWorkerSource()` の `pixel()` は、B・C導入後も各反復で3回、符号を扱う汎用の丸め関数を呼ぶ。このうち実部・虚部の二乗は必ず非負。
- `continueOperationLimitActive()` は `min(256, 残り反復, floor(4000000 / active画素数))` を使う。active画素が増えると1回で進む反復数が減り、状態の読み書きと件数の読戻しが増える。
- 1バッチにまとめるdispatch数は最大4。処理時間が10 ms未満なら増やし、32 msを超えたら1に戻す制御が既にある。
- 高画質は縦横1.5倍、画素上限2,359,296、反復上限32,768。`resize()` はさらに端末メモリーとGPUのバッファ・テクスチャ上限を適用する。
- 彩色後のcanvasをCSSサイズへ縮小表示している。旧「精細」の近隣画素を平均する `edgeAA` は通常操作で無効。今回も有効にしていない。
## 測定条件
Windows、Edge 152、Intel Gen-9。ブラウザーが報告するメモリー8 GB、論理プロセッサー8。専用Edge 1インスタンス・1タブで順番に実行し、時間比較中は編集や別テストを停止した。OSのバックグラウンド処理や発熱を完全に固定した測定ではない。
表示領域320×240、devicePixelRatio=1、座標精度448 bit、初期512反復、自動反復増分なし、彩色「昼夜」、色相アニメーションなし。標準画質の上限は16,384反復、高画質・参考画像は32,768反復で固定した。
| 視点 | 中心(実部, 虚部) | 表示幅 |
| --- | --- | --- |
| 初期表示 | -0.5, 0 | 3.4 |
| 深拡大 | -0.743643887037151, 0.13182590420533 | 3.4e-13 |
| 中間倍率 | 同上 | 1e-6 |
各条件1回のウォームアップ後3回測定し、2巡目は比較順を反転した。表は完成時間の中央値、括弧内は最小~最大。完成後の読戻し・ハッシュ・PNG検査は完成時間に含めない。参考画像だけは1回取得した。
## 案1:二乗の丸めを簡略化する
`pixel()` の次の2箇所を変更する。虚数成分の積に対する正負対称の丸めは維持する。
```js
// 現在
zr2 = round(zr * zr);
zi2 = round(zi * zi);
// 候補
zr2 = (zr * zr + half) >> B;
zi2 = (zi * zi + half) >> B;
```
`zr * zr` と `zi * zi` はBigIntの非負整数なので、元の丸め関数の負数分岐へ入らない。関数呼出しと不要な符号判定を省ける。シフト幅・丸め定数・2精度の照合・反復上限を変える必要はない。
標準画質320×240、単位ms:
| 視点 | 現行B+C | 案1 |
| --- | ---: | ---: |
| 初期表示 | 580.5(543.6~585.0) | 641.1(576.1~644.6) |
| 深拡大 | 8,985.2(8,177.0~9,162.4) | 7,989.3(7,711.2~8,793.8) |
| 中間倍率 | 2,899.3(2,799.1~2,998.7) | 3,051.6(2,852.9~3,184.8) |
深拡大は各巡で現行より短く、中央値で11.1%短縮した。初期表示・中間倍率の中央値は長くなっており、全視点での高速化は主張しない。結果はCPU高精度補修が多い場面を狙う判断材料とする。
比較用24描画すべてで、全画素のmeta・smoothのハッシュが一致。分類・脱出反復数・理由コード・連続彩色値のビット列を保持した。数値失敗0、指定上限への到達、PNG復号不一致0も確認した。
## 案2:継続計算の処理単位を増やす
対象を `continueOperationLimitActive()` の上限だけに限定する。従来の `PIXEL_FRONTIER_WORK=4000000` は局所補修などにも使われるため、その定数を一括変更しない。
```js
const ACTIVE_CONTINUATION_WORK = 8000000;
const chunk = Math.min(
256, remaining, Math.floor(ACTIVE_CONTINUATION_WORK / before)
);
```
画素数が多い場合に1回で進む反復数を増やし、途中状態の書戻しとGPU完了待ちを減らす。上限到達・誤差判定・画素の選別は変えない。最大256反復、最大4 dispatchのバッチ、処理時間に応じたバッチ数調整、世代トークンの確認は維持する。
案1+案3を共通の土台にした高画質640×480、単位ms:
| 視点 | 400万/dispatch | 800万/dispatch |
| --- | ---: | ---: |
| 初期表示 | 5,633.2(5,555.8~5,713.4) | 5,815.5(5,737.7~6,016.0) |
| 中間倍率 | 14,169.1(13,688.2~14,839.1) | 12,416.5(11,998.3~12,928.6) |
中間倍率の中央値は12.4%短縮。完了待ちの回数は606/631/701回から419/459/464回へ減り、中央値で約27.3%減った。初期表示は待ち回数が3回のままで、改善していない。
| 中間倍率のバッチ待ち時間 | 400万 | 800万 |
| --- | ---: | ---: |
| 各描画内の95パーセンタイル、3回の値 | 17.6 / 17.7 / 17.9 ms | 26.1 / 24.8 / 23.8 ms |
| 測定3回中の最大 | 28.8 ms | 45.4 ms |
これはGPU投入から件数読戻しまでの壁時計時間で、GPU timestampではない。1バッチには複数dispatchを含む場合がある。**完成時間は短くなるが、1回の処理待ちは長くなる**。これをそのままアプリ全体のキャンセル応答時間とは扱わない。
比較用16描画すべてで全画素ハッシュが一致し、数値失敗0、32,768反復への到達、PNG不一致0を確認した。各dispatchの仕事量も対応する400万/800万以下だった。
正式実装では継続用800万と補修用400万を別々に検査する。既存テストの「400万以下」を一律に「800万以下」へ緩めず、補修側の上限違反を検出できるようにする。
## 案3:「高画質」を縦横2倍へ
実際に計算するサンプル数を増やす。表示済みの隣接画素をぼかす方法は使わない。高速・標準の設定と高画質32,768反復の上限は維持する。
今回の試作は案1を併用し、高画質を倍率2、画素上限4,194,304にした。320×240の表示で、現行480×360から640×480へ増える。**画素数は現行高画質の約1.78倍**。
単位ms:
| 視点 | 現行高画質1.5倍 | 案1+2倍 |
| --- | ---: | ---: |
| 初期表示 | 3,184.7(3,177.8~3,303.0) | 5,448.0(5,270.0~5,515.2) |
| 中間倍率 | 7,573.1(7,278.2~7,746.4) | 13,701.5(13,658.1~13,716.9) |
計算時間は約1.71倍/1.81倍になった。案2を加えた場合の効果は前節の別測定を参照。これらの別実験を混ぜて厳密な総合高速化率を算出しない。
### 画質の数値比較
同じ座標・反復上限で1280×960の4倍解像度画像を作り、各4×4画素をRGB値の面積平均で320×240へ縮小したものを参考画像とした。現行・候補は、UIを隠した実際のブラウザー表示のスクリーンショットを比較に使った。
RGB各成分0~255の差からRMSEを求める。低いほど参考画像に近い。境界付近の指標は、参考画像の4×4サンプル内でいずれかのRGB成分の最大差が32を超える表示画素だけを対象とした。評価対象は初期表示4,346画素、中間倍率17,027画素。
| 視点 | 全体RMSE:現行→2倍 | 誤差減少 | 境界付近RMSE:現行→2倍 | PSNR:現行→2倍 |
| --- | ---: | ---: | ---: | ---: |
| 初期表示 | 6.834→5.341 | 約21.9% | 28.612→22.413 | 31.44→33.58 dB |
| 中間倍率 | 19.455→15.861 | 約18.5% | 41.303→33.671 | 22.35→24.12 dB |
両視点で、全体と境界付近の誤差が減った。比較画像でも細かな点や境界のギザつきが減っていることを確認した。**「画質が何%上がった」という普遍的尺度ではなく、この参考画像との差の減少率**である。
参考画像も有限反復・有限サンプル数の描画であり、集合境界の厳密解ではない。RGB平均は今回の表示比較のための基準で、線形光空間での色再現性を評価したものではない。別の座標・配色・DPRで同じ改善率になるとは限らない。
| 現行高画質 | 案1+2倍 | 4倍解像度からの参考画像 |
| --- | --- | --- |
| ![初期表示・現行](quality-plan-images/reset-current.png) | ![初期表示・2倍](quality-plan-images/reset-high2.png) | ![初期表示・参考](quality-plan-images/reset-reference.png) |
| ![中間倍率・現行](quality-plan-images/mid-current.png) | ![中間倍率・2倍](quality-plan-images/mid-high2.png) | ![中間倍率・参考](quality-plan-images/mid-reference.png) |
参考画像の完成時間は初期表示約20.4秒、中間倍率約166.1秒だった。中間倍率は最初の試行で検証APIの120秒の待機上限に達した。計算条件を変えず、参考画像の観測時間だけ延ばして再取得した。これは通常描画が120秒で停止する仕様という意味ではない。
途中の診断では92万画素以上の未脱出画素が継続計算に残っていた。400万の仕事量上限では、これだけで1 dispatchの反復数が4程度になる。大きい画像で状態の読み書き・往復が増える点が、案2を追加した理由である。4倍表示を通常設定へ追加する計画にはしない。
## 実装時の負荷と画素上限
高画質は倍率2を候補にするが、端末メモリー・GPU制限による縮小を維持する。試作の画素上限4,194,304は、GPUのstorage buffer上限÷40などでさらに制限される。各GPUバッファが上限内でも、プロセス全体のメモリーが小さいことを保証するわけではない。
640×480では設定上限が効かないため、今回確認できたのは倍率変更の効果。**大きい画面で画素上限を引き上げる際のピークメモリーと性能は未測定**。正式実装では1100×720以上、DPR=2、低メモリー条件でも確認する。
画質を優先する方針に従い、多少の時間増は許容する。GPU制限は超えず、完成済み画像の再投影、現在の世代以外の結果の拒否、補修中のキャンセルを保つ。上限の引上げでメモリー圧迫や操作不能が出る場合は、倍率2を維持しつつ画素上限を現行値へ戻す判断をする。近隣色の平均や反復削減で完成扱いにはしない。
## 実装・検証の手順
1. **案1を単独実装。** Workerの二乗2箇所だけを変更し、正負を扱う積の丸めと2精度照合は残す。CPU回帰と現行版とのmeta・smooth全画素一致を確認する。
2. **案2を独立した上限定数で実装。** 継続計算と数値補修の仕事量を区別し、既存のバッチ数調整とトークン確認を残す。高画質の大量active画素、0/1/端数候補、目標反復直前のケースを確認する。色相変化中のパン・ズーム・補修中のキャンセルを実操作で検査する。
3. **高画質の倍率を2へ変更。** 高速・標準、共有URLの3段階、反復予算を維持する。大画面・DPR=2・メモリー上限を検査し、画素数上限を決める。彩色だけの操作で数値再計算が増えないことも確認する。
4. **組合せを再測定。** 実装前後で座標・反復予算・端末・表示サイズを固定する。画質を変えない比較は全画素ビット一致、解像度を変える比較は参考画像との誤差とPNGで評価する。完成時間、処理待ち時間、数値失敗、キャンセル、メモリーを別々に記録する。
通常の確認には `node tests/regression.mjs` と `node tests/browser_smoke.mjs 9333 all` を使う。数値シェーダーを変えない案なので、固定ハッシュを再生成して通す必要はない。新たな詳細計測は検証モード内に置き、通常表示へ監査ループを追加しない。
今回は案の作成と隔離した試作の測定まで。案2を加えた版の全画素一致は確認したが、その版での操作応答・大画面メモリー・全回帰は正式実装時の確認事項であり、確認済みとはしていない。
## 再現方法と証拠
基準HTMLのハッシュが上記と一致する作業ツリーで、専用Edgeのデバッグポート9333と1タブを使う。
```sh
node tests/quality_plan.mjs 9333 all
node tests/active_work_plan.mjs 9333
```
参考画像だけを取り直す場合は、既存JSONと比較画像を保持したまま次を実行する。完成済みの時間比較をやり直さない。
```sh
node tests/quality_plan.mjs 9333 reference
```
- [速度・画質の生データ](NEXT_QUALITY_PLAN_DATA.json):案1の24描画、案3の16描画、参考画像2枚、画像誤差4件。各条件の1回目は `round=-1` のウォームアップ。
- [処理単位の生データ](NEXT_ACTIVE_WORK_DATA.json):案2の16描画と各バッチの仕事量・待ち時間。
- [比較用スクリプト](../tests/quality_plan.mjs)、[処理単位のスクリプト](../tests/active_work_plan.mjs):本体からコピーを作り、変更箇所を限定して比較する。終了時にコピーを削除する。
成功した比較用描画は合計58件(ウォームアップを含む、起動時の描画は除く)。それぞれ数値失敗0、有限上限での完了、PNG不一致0を検査した。同じサンプル位置を使う速度比較では全画素ハッシュが一致した。試験後に検証用Edgeと専用プロファイルを終了・削除し、残存Edgeプロセス0を確認した。