diff --git a/TIASshot.Tests/BitmapUtilTests.cs b/TIASshot.Tests/BitmapUtilTests.cs index 7e1f32b..05a7041 100644 --- a/TIASshot.Tests/BitmapUtilTests.cs +++ b/TIASshot.Tests/BitmapUtilTests.cs @@ -16,7 +16,7 @@ /// Lucam コールバックスレッドと UI スレッドの GDI 競合・WM_PAINT/WM_TIMER の /// スケジューリングは,実メッセージループ・実カメラ・副ディスプレイに依存する /// ハードウェア結合層(GUIDE_08)であり,NUnit の自動ユニットテストの対象外とする。 - /// これらは実機の MEMMONITOR 計測(TEST_01 参照)で反復検証済み。 + /// これらは実機の MEMMONITOR 計測(TEST_03 参照)で反復検証済み。 /// フェイクの UI ループを組んでまで検証する脆いテストは作らない。 /// [TestFixture] diff --git "a/docs/02_ENV/ENV_05_\343\203\207\343\202\243\343\203\254\343\202\257\343\203\210\343\203\252\346\247\213\346\210\220.md" "b/docs/02_ENV/ENV_05_\343\203\207\343\202\243\343\203\254\343\202\257\343\203\210\343\203\252\346\247\213\346\210\220.md" index 7548e21..8bdcde3 100644 --- "a/docs/02_ENV/ENV_05_\343\203\207\343\202\243\343\203\254\343\202\257\343\203\210\343\203\252\346\247\213\346\210\220.md" +++ "b/docs/02_ENV/ENV_05_\343\203\207\343\202\243\343\203\254\343\202\257\343\203\210\343\203\252\346\247\213\346\210\220.md" @@ -34,10 +34,17 @@ ├── UI/ ← フォーム(WinForms) │ ├── Form1.cs ← メインフォーム(UI・操作イベント) │ ├── Form1.Designer.cs ← メインフォームのデザイナー自動生成コード +│ ├── Form1.WhiteBoard.cs ← 白板撮影ボタン(WHITEBOARD ビルド限定の partial) +│ ├── Form1.MemMonitor.cs ← メモリ観測ウィンドウ起動・F9 トグル(MEMMONITOR 限定の partial) │ ├── PreviewMonitor.cs ← プレビューモニタフォーム -│ └── PreviewMonitor.Designer.cs ← プレビューモニタのデザイナー自動生成コード +│ ├── PreviewMonitor.Designer.cs ← プレビューモニタのデザイナー自動生成コード +│ ├── MemoryMonitorForm.cs ← メモリ観測ウィンドウ(MEMMONITOR 限定) +│ ├── MemoryMonitorForm.Designer.cs ← 同デザイナーコード(MEMMONITOR 限定) +│ ├── PreviewProfiler.cs ← プレビュー計測カウンタ(MEMMONITOR 限定) +│ └── TimedPictureBox.cs ← 副モニタ実描画時間の計測(MEMMONITOR 限定) └── Utils/ ← ユーティリティ ├── IoUtil.cs ← ファイル I/O(CSV・保存フォルダ・撮影情報)・サウンド + ├── BitmapUtil.cs ← Bitmap 生画素コピー(FastClone・純粋ロジック) └── ImageStats.cs ← 飽和統計算出・JSON 生成(白板撮影用・純粋ロジック) ``` @@ -82,7 +89,14 @@ ├── packages.config ← NuGet パッケージ一覧(NUnit 等) ├── IoUtilTests.cs ← CSV 読み書き往復のテスト ├── ConfigTests.cs ← config.xml パースのテスト -└── ColorCorrectorTests.cs ← 次元拡張・変換行列・色変換のテスト +├── ColorCorrectorTests.cs ← 次元拡張・変換行列・色変換のテスト +├── GainCalcTests.cs ← ゲイン更新比率計算のテスト +├── ImageStatsTests.cs ← 飽和統計・JSON 生成のテスト(白板撮影用) +├── BitmapUtilTests.cs ← FastClone の仕様ベーステスト +├── ConvertImageMemoryBenchmark.cs ← ConvertImage 単体のメモリ計測ベンチ([Explicit]) +├── ContinuousShootMemoryBenchmark.cs ← 連続撮影フロー再現のメモリ計測ベンチ([Explicit]) +├── MemBenchSupport.cs ← メモリベンチ共通ユーティリティ +└── MemBenchSupportTests.cs ← 同ユーティリティのテスト ``` ## ドキュメント docs/ (Documents) diff --git "a/docs/02_ENV/ENV_06_\343\203\223\343\203\253\343\203\211\346\247\213\346\210\220\343\201\250\343\203\223\343\203\253\343\203\211\343\203\225\343\203\251\343\202\260.md" "b/docs/02_ENV/ENV_06_\343\203\223\343\203\253\343\203\211\346\247\213\346\210\220\343\201\250\343\203\223\343\203\253\343\203\211\343\203\225\343\203\251\343\202\260.md" index abe6969..f22905f 100644 --- "a/docs/02_ENV/ENV_06_\343\203\223\343\203\253\343\203\211\346\247\213\346\210\220\343\201\250\343\203\223\343\203\253\343\203\211\343\203\225\343\203\251\343\202\260.md" +++ "b/docs/02_ENV/ENV_06_\343\203\223\343\203\253\343\203\211\346\247\213\346\210\220\343\201\250\343\203\223\343\203\253\343\203\211\343\203\225\343\203\251\343\202\260.md" @@ -50,4 +50,4 @@ - 各フラグ導入の経緯・設計判断は [docs/PROGRESS.md](../PROGRESS.md) の該当エントリを参照: - `WHITEBOARD`(白板撮影の専用ビルド記号化) - `MEMMONITOR`(メモリ修正の効果検証ツール) -- メモリ計測の結果は [docs/06_TEST/TEST_01_メモリ修正効果計測.md](../06_TEST/TEST_01_メモリ修正効果計測.md). +- メモリ計測の結果は [docs/06_TEST/TEST_03_メモリ修正効果計測.md](../06_TEST/TEST_03_メモリ修正効果計測.md). diff --git "a/docs/03_PLAN/PLAN_03_\343\203\227\343\203\254\343\203\223\343\203\245\343\203\274\343\203\241\343\203\242\343\203\252\343\203\252\343\203\274\343\202\257\344\277\256\346\255\243\350\250\210\347\224\273.md" "b/docs/03_PLAN/PLAN_03_\343\203\227\343\203\254\343\203\223\343\203\245\343\203\274\343\203\241\343\203\242\343\203\252\343\203\252\343\203\274\343\202\257\344\277\256\346\255\243\350\250\210\347\224\273.md" index bc54b53..e393b03 100644 --- "a/docs/03_PLAN/PLAN_03_\343\203\227\343\203\254\343\203\223\343\203\245\343\203\274\343\203\241\343\203\242\343\203\252\343\203\252\343\203\274\343\202\257\344\277\256\346\255\243\350\250\210\347\224\273.md" +++ "b/docs/03_PLAN/PLAN_03_\343\203\227\343\203\254\343\203\223\343\203\245\343\203\274\343\203\241\343\203\242\343\203\252\343\203\252\343\203\274\343\202\257\344\277\256\346\255\243\350\250\210\347\224\273.md" @@ -68,7 +68,7 @@ ## 検証 (Verification) - `MEMMONITOR` ビルド(`UI/MemoryMonitorForm`)で,**撮影せずプレビュー表示のまま放置**し,メモリ(PrivateMemory/WorkingSet)が時間とともに増え続けないことを確認する.修正前後で対比する. -- 既存のメモリ計測ベンチ([TEST_01](../06_TEST/TEST_01_メモリ修正効果計測.md))は**撮影フロー対象**であり,本件(プレビュー放置)はカバーしていない.プレビュー放置シナリオの観測は別途行う. +- 既存のメモリ計測ベンチ([TEST_03](../06_TEST/TEST_03_メモリ修正効果計測.md))は**撮影フロー対象**であり,本件(プレビュー放置)はカバーしていない.プレビュー放置シナリオの観測は別途行う. - GDI ハンドルのリークが疑われるため,観測ウィンドウに **GDI オブジェクト数**(`GetGuiResources` 等)の表示を追加して,メモリだけでなくハンドル数も時間で増えないことを確認することを推奨する(任意). ## 修正順序まとめ (Summary) @@ -81,4 +81,4 @@ ## 関連 (Related) -- [TEST_01 メモリ修正効果計測](../06_TEST/TEST_01_メモリ修正効果計測.md) — 撮影経路のメモリ計測結果. +- [TEST_03 メモリ修正効果計測](../06_TEST/TEST_03_メモリ修正効果計測.md) — 撮影経路のメモリ計測結果. diff --git "a/docs/03_PLAN/PLAN_04_\346\200\247\350\203\275\350\251\225\344\276\241\350\250\210\346\270\254\350\250\210\347\224\273.md" "b/docs/03_PLAN/PLAN_04_\346\200\247\350\203\275\350\251\225\344\276\241\350\250\210\346\270\254\350\250\210\347\224\273.md" index a724142..cf14d3e 100644 --- "a/docs/03_PLAN/PLAN_04_\346\200\247\350\203\275\350\251\225\344\276\241\350\250\210\346\270\254\350\250\210\347\224\273.md" +++ "b/docs/03_PLAN/PLAN_04_\346\200\247\350\203\275\350\251\225\344\276\241\350\250\210\346\270\254\350\250\210\347\224\273.md" @@ -18,7 +18,7 @@ ## 計測手段と役割分担 (Method & Roles) -- **手段**: 実機 MEMMONITOR(メモリ観測ウィンドウ+プレビュー計測)を使ったライブ計測.自動 NUnit ベンチ(TEST_01)ではなく,**実カメラで普通にアプリを使ったときの挙動**を対象とする. +- **手段**: 実機 MEMMONITOR(メモリ観測ウィンドウ+プレビュー計測)を使ったライブ計測.自動 NUnit ベンチ(TEST_03)ではなく,**実カメラで普通にアプリを使ったときの挙動**を対象とする. - **カメラ**: IScam(ImagingSource DFK 23UX249.白板撮影で使用したもの). - **役割分担**: - **Claude**: 3 本の計装済みビルド(後述の使い捨てブランチ)と実行チェックリストを用意し,出力 CSV を集計して結果ドキュメント(TEST_02)にまとめる. @@ -121,7 +121,7 @@ ## 注意点・リスク (Cautions & Risks) -- **実機 OOM 上限**: 自動ベンチ(TEST_01)には打ち切り上限ガードがあったが,実機観測窓(`MemoryMonitorForm`)にはガードが無い(記録するのみ).S1 でリーク版(v1.6)を長く回すと OOM に達し得るため,**サイクル数 M・放置時間 T に上限を決めておく**(値は計測実施時に確定). +- **実機 OOM 上限**: 自動ベンチ(TEST_03)には打ち切り上限ガードがあったが,実機観測窓(`MemoryMonitorForm`)にはガードが無い(記録するのみ).S1 でリーク版(v1.6)を長く回すと OOM に達し得るため,**サイクル数 M・放置時間 T に上限を決めておく**(値は計測実施時に確定). - **移植の非マージ**: `bench/v1.6`・`bench/v1.7` は計測専用の使い捨てブランチであり,`main` にマージ・push・PR しない. - 旧版が現行と同じツールチェーン(x64 / .NET Framework 4.8 / OpenCvSharp パッケージ)でビルドできるかは移植着手時に確認する. @@ -133,12 +133,12 @@ ## 成果物 (Deliverables) - 本計画: `docs/03_PLAN/PLAN_04_性能評価計測計画.md`. -- 計測結果: `docs/06_TEST/TEST_02_性能評価計測結果.md`(TEST_01 と同形式の 3 版比較表). +- 計測結果: `docs/06_TEST/TEST_02_性能評価計測結果.md`(TEST_03 と同形式の 3 版比較表). ## 関連 (Related) - ビルドフラグ(`MEMMONITOR`): [ENV_06_ビルド構成とビルドフラグ](../02_ENV/ENV_06_ビルド構成とビルドフラグ.md). -- 自動ベンチによるメモリ計測(参考・別手段): [TEST_01_メモリ修正効果計測](../06_TEST/TEST_01_メモリ修正効果計測.md). +- 自動ベンチによるメモリ計測(参考・別手段): [TEST_03_メモリ修正効果計測](../06_TEST/TEST_03_メモリ修正効果計測.md). - プレビューメモリリーク修正の詳細: [PLAN_03_プレビューメモリリーク修正計画](PLAN_03_プレビューメモリリーク修正計画.md). diff --git "a/docs/06_TEST/TEST_01_\343\203\241\343\203\242\343\203\252\344\277\256\346\255\243\345\212\271\346\236\234\350\250\210\346\270\254.md" "b/docs/06_TEST/TEST_01_\343\203\241\343\203\242\343\203\252\344\277\256\346\255\243\345\212\271\346\236\234\350\250\210\346\270\254.md" deleted file mode 100644 index 4489ba2..0000000 --- "a/docs/06_TEST/TEST_01_\343\203\241\343\203\242\343\203\252\344\277\256\346\255\243\345\212\271\346\236\234\350\250\210\346\270\254.md" +++ /dev/null @@ -1,80 +0,0 @@ -# メモリ修正効果の計測結果(PLAN_02 優先度2) (Memory Fix Effect Measurement) - -: 2026-06-23 -対象: PLAN_02 優先度2「`ColorCorrector.ConvertImage` の中間 Mat 未 Dispose(OOM リーク)」修正 -計測者: 自動ベンチ(NUnit、ヘッドレス実行・カメラ実機不要) - -## 目的 (Purpose) - -修正前(中間 Mat を解放しない=旧実装)と修正後(入れ子 `using` で解放)で、連続処理時のメモリ使用量の「増え方」がどれだけ変わるかを同一条件で定量比較する。 - -## 方法 (Method) - -- ベンチ: `TIASshot.Tests/ConvertImageMemoryBenchmark.cs`(`[Explicit]`)。 - - 修正後 = 実コード `ColorCorrector.ConvertImage`(using 版)。 - - 修正前 = 旧実装を忠実に再現(`flatten`/`extended`/`converted`/`convertedImage` を解放しない)。 - - 同一画像を N 回反復し、各反復後に `Process.PrivateMemorySize64` / `WorkingSet64` をサンプリング。 -- 指標: Mat はネイティブメモリのため `GC.GetTotalMemory` では捕捉できない → `PrivateMemorySize64`(コミット量)を主指標とする。 -- 計測ループ中は GC を強制しない(本番のリーク機序を再現するため)。物理メモリ枯渇を防ぐ打ち切り上限ガード付き。 -- 実行環境: x64 / .NET Framework 4.8 / 物理メモリ 16 GB。 - -## 結果 (Results) - -### 条件A: IScam フルスケール(2440×1840・17ch・40 回) - -| 指標 | 修正前(リーク) | 修正後(using) | -| --- | --- | --- | -| Private baseline | 0.05 GB | 0.05 GB | -| Private peak | **2.83 GB** | **0.62 GB** | -| 増分 (peak−base) | 2.78 GB | 0.57 GB | -| 1 回あたり平均増分 | **71 MB/回** | **15 MB/回** | -| 推移 | 0.82→1.49→2.16→2.83 GB と階段状に上昇 | 全 40 回 0.62 GB で一定 | - -→ 修正後の増分は修正前の **約 20%**。 - -### 条件B: 小サイズ・多反復(900×700・17ch・60 回) - -| 指標 | 修正前(リーク) | 修正後(using) | -| --- | --- | --- | -| Private peak | **0.53 GB** | **0.13 GB** | -| 増分 (peak−base) | 0.48 GB | 0.08 GB | -| 1 回あたり平均増分 | **8 MB/回** | **1 MB/回** | -| 推移 | 反復に伴い増加(0.16→0.44→0.53 GB) | 全 60 回 0.13 GB で一定 | - -→ 修正後の増分は修正前の **約 17%**。 - -### 条件C: 連続撮影フロー再現(フルスケール・2440×1840・channels 4/10/17・8 フレーム) - -`ContinuousShootMemoryBenchmark`(`BlockingCollection` の生産者-消費者・1 フレーム複数チャンネル変換・フレームバッファを合成フレームで構造再現)での計測。単体反復でなく「実機の連続撮影に近いフロー」での結果。 - -| 指標 | 修正前(リーク) | 修正後(using) | -| --- | --- | --- | -| Private peak | **2.60 GB** | **0.92 GB** | -| 増分 (peak−base) | 2.55 GB | 0.87 GB | -| **1 フレームあたり増分** | **326 MB/frame** | **112 MB/frame** | -| WorkingSet peak | 2.47 GB | 0.92 GB | - -→ 修正後の増分は修正前の **約 34%**。修正前は 1 フレームごとに約 326 MB 積み上がる(フレーム数を増やせば打ち切り上限=OOM へ)。修正後は約 0.9 GB で頭打ち(処理中の一時ピークで蓄積なし)。修正後の per-frame 112 MB は主に「保持中フレーム+処理中の一時メモリ」で,反復しても天井は一定. - -## 解釈 (Interpretation) - -- **修正後は反復しても増えない**(peak の天井が一定=蓄積ゼロ)。各呼び出しで中間 Mat が確定的に解放される。 -- **修正前は反復に伴いメモリが増える**(1 回あたり数十 MB〜)。本番は IScam 17ch × 複数フレームを `SaveThread` で高速に回すため、ファイナライザの回収が追いつかず確保が先行 → OOM クラッシュに至る(PLAN_02 の症状)。 -- 修正前のグラフが滑らかでなく**階段状/のこぎり波**になるのは、OpenCvSharp が GC にメモリ圧を通知し、未解放でも GC+ファイナライザが周期的にネイティブメモリを一掃するため。見るべきは振れ幅でなく**ピーク包絡線のトレンド**(修正後=水平、修正前=上昇)。 - -## 限界・補足 (Limitations) - -- 条件A/B は `ConvertImage` 単体の隔離計測。条件C は連続撮影フローを**構造再現**したもので、生産者-消費者スレッディング・多チャンネル・フレームバッファを含むが、**カメラ取得部のみ合成フレームに差し替えた再現**であり本番メソッド(`Shot`/`RunShotLoop`/`SaveThread`)そのものの呼び出しではない。実フレームレート・実カメラ・ファイル I/O(既定 OFF)までは含まない。実使用そのものの挙動は、`MEMMONITOR` ビルドのメモリ観測ウィンドウ+実機撮影の録画で別途確認する。 -- 優先度5(Clear 時の未 Dispose)は未修正のため本対比の対象外(修正前/後どちらの変種にも同じだけ含まれ、差分には現れない)。 - -## 再現方法 (Reproduction) - -```powershell -# テストプロジェクトを Debug|x64 でビルド後 -$vstest = "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Common7\IDE\Extensions\TestPlatform\vstest.console.exe" -$dll = "TIASshot.Tests\bin\x64\Debug\TIASshot.Tests.dll" -$env:BENCH_WIDTH="2440"; $env:BENCH_HEIGHT="1840"; $env:BENCH_CHANNEL="17"; $env:BENCH_FRAMES="40"; $env:BENCH_CEILING_GB="10" -& $vstest $dll /Platform:x64 /Framework:".NETFramework,Version=v4.8" /Tests:Compare_LeakyVsFixed_MemoryUsage /logger:"console;verbosity=normal" -``` - -※ スケールは環境変数 `BENCH_WIDTH/HEIGHT/CHANNEL/FRAMES/CEILING_GB` で調整可。 diff --git "a/docs/06_TEST/TEST_02_\346\200\247\350\203\275\350\251\225\344\276\241\350\250\210\346\270\254\347\265\220\346\236\234.md" "b/docs/06_TEST/TEST_02_\346\200\247\350\203\275\350\251\225\344\276\241\350\250\210\346\270\254\347\265\220\346\236\234.md" index 24d8386..21b9f7e 100644 --- "a/docs/06_TEST/TEST_02_\346\200\247\350\203\275\350\251\225\344\276\241\350\250\210\346\270\254\347\265\220\346\236\234.md" +++ "b/docs/06_TEST/TEST_02_\346\200\247\350\203\275\350\251\225\344\276\241\350\250\210\346\270\254\347\265\220\346\236\234.md" @@ -77,7 +77,7 @@ - **プレビュー性能は v1.6 と数値まで一致**(コピー ~45 ms・`callback_fps` ~17・副モニタ飢餓 ~3fps).v1.7 がプレビュー経路に手を付けていないことをデータが裏付ける. - **メモリのピーク包絡線は v1.6 の ~890 MB → v1.7 は ~700 MB に低下**(`SaveThread` の Mat 都度 Dispose+校正時の中間 Mat 解放).ただしプレビュー経路のリークは残るため,放置区間でも上昇は続き,現行のような平坦な天井にはならない. -- ※ 本プロトコルは単発撮影のため保存経路の差(v1.6→v1.7)は控えめに出る.保存経路単体の効果は連続撮影で顕著になり,自動ベンチの [TEST_01_メモリ修正効果計測](TEST_01_メモリ修正効果計測.md) に隔離計測値がある. +- ※ 本プロトコルは単発撮影のため保存経路の差(v1.6→v1.7)は控えめに出る.保存経路単体の効果は連続撮影で顕著になり,自動ベンチの [TEST_03_メモリ修正効果計測](TEST_03_メモリ修正効果計測.md) に隔離計測値がある. ## v1.6 の結果 (v1.6) @@ -127,12 +127,12 @@ 改善は 2 経路・2 段階で起きたことが定量的に確認できた. -- **保存経路(メモリ)は v1.6 → v1.7 で改善**: ピーク包絡線が ~890 MB → ~700 MB に低下(`SaveThread`/校正の Mat 解放).単発撮影プロトコルのため差は控えめだが方向は明確(連続撮影での隔離値は TEST_01). +- **保存経路(メモリ)は v1.6 → v1.7 で改善**: ピーク包絡線が ~890 MB → ~700 MB に低下(`SaveThread`/校正の Mat 解放).単発撮影プロトコルのため差は控えめだが方向は明確(連続撮影での隔離値は TEST_03). - **プレビュー経路は v1.7 → 現行で改善**: コピー ~45 ms → ~2.6 ms(FastClone,約 18 倍),`callback_fps` ~17 → ~30,副モニタ ~3fps(飢餓)→ ~19fps,メモリ包絡線 ~700 MB(上昇)→ ~200 MB(平坦・リーク解消).v1.6/v1.7 のプレビュー指標が数値まで一致することが,この経路が v1.7 まで未修正だった裏付けになる. - GDI/ハンドルはどの版も暴走せず,メモリ問題の原因がネイティブ画素バッファ(ハンドル枯渇ではない)であることを 3 版で一貫して確認. ## 関連 (Related) - 計測計画・指標定義・移植方針: [PLAN_04_性能評価計測計画](../03_PLAN/PLAN_04_性能評価計測計画.md). -- 自動ベンチによるメモリ計測(別手段・参考): [TEST_01_メモリ修正効果計測](TEST_01_メモリ修正効果計測.md). +- 自動ベンチによるメモリ計測(別手段・参考): [TEST_03_メモリ修正効果計測](TEST_03_メモリ修正効果計測.md). diff --git "a/docs/06_TEST/TEST_03_\343\203\241\343\203\242\343\203\252\344\277\256\346\255\243\345\212\271\346\236\234\350\250\210\346\270\254.md" "b/docs/06_TEST/TEST_03_\343\203\241\343\203\242\343\203\252\344\277\256\346\255\243\345\212\271\346\236\234\350\250\210\346\270\254.md" new file mode 100644 index 0000000..4489ba2 --- /dev/null +++ "b/docs/06_TEST/TEST_03_\343\203\241\343\203\242\343\203\252\344\277\256\346\255\243\345\212\271\346\236\234\350\250\210\346\270\254.md" @@ -0,0 +1,80 @@ +# メモリ修正効果の計測結果(PLAN_02 優先度2) (Memory Fix Effect Measurement) + +: 2026-06-23 +対象: PLAN_02 優先度2「`ColorCorrector.ConvertImage` の中間 Mat 未 Dispose(OOM リーク)」修正 +計測者: 自動ベンチ(NUnit、ヘッドレス実行・カメラ実機不要) + +## 目的 (Purpose) + +修正前(中間 Mat を解放しない=旧実装)と修正後(入れ子 `using` で解放)で、連続処理時のメモリ使用量の「増え方」がどれだけ変わるかを同一条件で定量比較する。 + +## 方法 (Method) + +- ベンチ: `TIASshot.Tests/ConvertImageMemoryBenchmark.cs`(`[Explicit]`)。 + - 修正後 = 実コード `ColorCorrector.ConvertImage`(using 版)。 + - 修正前 = 旧実装を忠実に再現(`flatten`/`extended`/`converted`/`convertedImage` を解放しない)。 + - 同一画像を N 回反復し、各反復後に `Process.PrivateMemorySize64` / `WorkingSet64` をサンプリング。 +- 指標: Mat はネイティブメモリのため `GC.GetTotalMemory` では捕捉できない → `PrivateMemorySize64`(コミット量)を主指標とする。 +- 計測ループ中は GC を強制しない(本番のリーク機序を再現するため)。物理メモリ枯渇を防ぐ打ち切り上限ガード付き。 +- 実行環境: x64 / .NET Framework 4.8 / 物理メモリ 16 GB。 + +## 結果 (Results) + +### 条件A: IScam フルスケール(2440×1840・17ch・40 回) + +| 指標 | 修正前(リーク) | 修正後(using) | +| --- | --- | --- | +| Private baseline | 0.05 GB | 0.05 GB | +| Private peak | **2.83 GB** | **0.62 GB** | +| 増分 (peak−base) | 2.78 GB | 0.57 GB | +| 1 回あたり平均増分 | **71 MB/回** | **15 MB/回** | +| 推移 | 0.82→1.49→2.16→2.83 GB と階段状に上昇 | 全 40 回 0.62 GB で一定 | + +→ 修正後の増分は修正前の **約 20%**。 + +### 条件B: 小サイズ・多反復(900×700・17ch・60 回) + +| 指標 | 修正前(リーク) | 修正後(using) | +| --- | --- | --- | +| Private peak | **0.53 GB** | **0.13 GB** | +| 増分 (peak−base) | 0.48 GB | 0.08 GB | +| 1 回あたり平均増分 | **8 MB/回** | **1 MB/回** | +| 推移 | 反復に伴い増加(0.16→0.44→0.53 GB) | 全 60 回 0.13 GB で一定 | + +→ 修正後の増分は修正前の **約 17%**。 + +### 条件C: 連続撮影フロー再現(フルスケール・2440×1840・channels 4/10/17・8 フレーム) + +`ContinuousShootMemoryBenchmark`(`BlockingCollection` の生産者-消費者・1 フレーム複数チャンネル変換・フレームバッファを合成フレームで構造再現)での計測。単体反復でなく「実機の連続撮影に近いフロー」での結果。 + +| 指標 | 修正前(リーク) | 修正後(using) | +| --- | --- | --- | +| Private peak | **2.60 GB** | **0.92 GB** | +| 増分 (peak−base) | 2.55 GB | 0.87 GB | +| **1 フレームあたり増分** | **326 MB/frame** | **112 MB/frame** | +| WorkingSet peak | 2.47 GB | 0.92 GB | + +→ 修正後の増分は修正前の **約 34%**。修正前は 1 フレームごとに約 326 MB 積み上がる(フレーム数を増やせば打ち切り上限=OOM へ)。修正後は約 0.9 GB で頭打ち(処理中の一時ピークで蓄積なし)。修正後の per-frame 112 MB は主に「保持中フレーム+処理中の一時メモリ」で,反復しても天井は一定. + +## 解釈 (Interpretation) + +- **修正後は反復しても増えない**(peak の天井が一定=蓄積ゼロ)。各呼び出しで中間 Mat が確定的に解放される。 +- **修正前は反復に伴いメモリが増える**(1 回あたり数十 MB〜)。本番は IScam 17ch × 複数フレームを `SaveThread` で高速に回すため、ファイナライザの回収が追いつかず確保が先行 → OOM クラッシュに至る(PLAN_02 の症状)。 +- 修正前のグラフが滑らかでなく**階段状/のこぎり波**になるのは、OpenCvSharp が GC にメモリ圧を通知し、未解放でも GC+ファイナライザが周期的にネイティブメモリを一掃するため。見るべきは振れ幅でなく**ピーク包絡線のトレンド**(修正後=水平、修正前=上昇)。 + +## 限界・補足 (Limitations) + +- 条件A/B は `ConvertImage` 単体の隔離計測。条件C は連続撮影フローを**構造再現**したもので、生産者-消費者スレッディング・多チャンネル・フレームバッファを含むが、**カメラ取得部のみ合成フレームに差し替えた再現**であり本番メソッド(`Shot`/`RunShotLoop`/`SaveThread`)そのものの呼び出しではない。実フレームレート・実カメラ・ファイル I/O(既定 OFF)までは含まない。実使用そのものの挙動は、`MEMMONITOR` ビルドのメモリ観測ウィンドウ+実機撮影の録画で別途確認する。 +- 優先度5(Clear 時の未 Dispose)は未修正のため本対比の対象外(修正前/後どちらの変種にも同じだけ含まれ、差分には現れない)。 + +## 再現方法 (Reproduction) + +```powershell +# テストプロジェクトを Debug|x64 でビルド後 +$vstest = "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Common7\IDE\Extensions\TestPlatform\vstest.console.exe" +$dll = "TIASshot.Tests\bin\x64\Debug\TIASshot.Tests.dll" +$env:BENCH_WIDTH="2440"; $env:BENCH_HEIGHT="1840"; $env:BENCH_CHANNEL="17"; $env:BENCH_FRAMES="40"; $env:BENCH_CEILING_GB="10" +& $vstest $dll /Platform:x64 /Framework:".NETFramework,Version=v4.8" /Tests:Compare_LeakyVsFixed_MemoryUsage /logger:"console;verbosity=normal" +``` + +※ スケールは環境変数 `BENCH_WIDTH/HEIGHT/CHANNEL/FRAMES/CEILING_GB` で調整可。 diff --git a/docs/PROGRESS.md b/docs/PROGRESS.md index 06b4c24..02ce442 100644 --- a/docs/PROGRESS.md +++ b/docs/PROGRESS.md @@ -72,7 +72,7 @@ - 計測の要点: Mat はネイティブメモリで `GC.GetTotalMemory` では捕捉不可 → `Process.PrivateMemorySize64`/`WorkingSet64` を使用。OpenCvSharp が GC にメモリ圧を通知するためリーク版でも GC+ファイナライザで周期回収が起き、グラフはのこぎり波になる(見るべきは振れ幅でなくピーク包絡線のトレンド:修正後=天井一定、リーク=上昇)。 - 後段: 優先度5(`_shots`/`_chartMasks` の Clear 時未解放)は未修正のため LEAKY_REPRO の対象外(対比は優先度2のみ)。グラフのトレンド可視化(包絡線・移動平均等)は要望次第で追加余地。 -- [x] **連続撮影フロー再現のメモリ計測ベンチ+計測結果レポート** (2026-06-23): 既存の単体ループベンチ(`ConvertImage` 隔離)では本番の連続撮影フローを再現できていなかったため、カメラ取得部だけを合成フレームに差し替えて本番フローを構造再現するベンチ(`ContinuousShootMemoryBenchmark`)を追加。生産者-消費者スレッディング・1 フレーム複数チャンネル変換・フレームバッファを含む。修正前/後の計測結果を `docs/06_TEST/TEST_01_メモリ修正効果計測.md` にまとめた(カメラ実機不要・ヘッドレスで Claude 側が実行可能)。 +- [x] **連続撮影フロー再現のメモリ計測ベンチ+計測結果レポート** (2026-06-23): 既存の単体ループベンチ(`ConvertImage` 隔離)では本番の連続撮影フローを再現できていなかったため、カメラ取得部だけを合成フレームに差し替えて本番フローを構造再現するベンチ(`ContinuousShootMemoryBenchmark`)を追加。生産者-消費者スレッディング・1 フレーム複数チャンネル変換・フレームバッファを含む。修正前/後の計測結果を `docs/06_TEST/TEST_03_メモリ修正効果計測.md` にまとめた(カメラ実機不要・ヘッドレスで Claude 側が実行可能)。 - 設計判断: 本番 `CameraBase.RunShotLoop`/`SaveThread`/`SaveImages` を直接ヘッドレス実行するには Form1・Config・TCC・校正で埋まる `_convRGB2SRGB` 依存が重すぎる → 本番コード無改造のまま「フローの構造再現」を選択(本番メソッドそのものの呼び出しではない旨を明記)。リーク再現と計測ユーティリティは `MemBenchSupport` に共有化し既存ベンチと統一。 - 結果の要点: フロー再現フルスケールで修正後増分は修正前の約 34%(per-frame 112 vs 326 MB)。単体隔離(約 17〜20%)と数値が違うのは、フロー版がフレーム保持+多チャンネル+並行処理の一時メモリを含むため。 - 役割分担の整理: メモリの「数値」はベンチ(Claude 側・再現性高)で、実使用の「動画デモ」は MEMMONITOR +実機録画(人間)で、と用途を分けた。 @@ -87,7 +87,7 @@ - 後段: 両プレビューの上限 ~10fps は毎フレームのフルフレーム Bitmap コピー(各 ~25ms)が律速だったため、別コミットで高速化(下記)。Phase 2/3(自動テスト・リファクタ)は本変更が WinForms UI スレッド・GDI ライフサイクル・カメラコールバック並行性主体で既存ユニットテスト基盤の対象外のため、実機メモリ計測での検証に代替(ユーザー指示によりこの検証済み状態でコミット)。 - [x] **3 バージョン(v1.6/v1.7/現行)の性能評価計測** (2026-07-14): 上流 v1.6/v1.7 と現行フォークを実機 MEMMONITOR で同一プロトコル計測し、メモリリーク・フレームコピー時間・プレビュー FPS を横並び比較。改善が「保存経路 v1.6→v1.7」「プレビュー経路 v1.7→現行」の 2 段階で起きたことを定量化(計画 PLAN_04・結果 TEST_02、PR #18 で main へ)。 - - 動機: 研究室共有用に、素の上流版と現行の性能差を録画+スライドで示したい。数値ベンチ(TEST_01)ではなく「普通にアプリを使ったときの実挙動」を対象にした。 + - 動機: 研究室共有用に、素の上流版と現行の性能差を録画+スライドで示したい。数値ベンチ(TEST_03)ではなく「普通にアプリを使ったときの実挙動」を対象にした。 - 計測基盤: 観測ウィンドウのグラフを左軸メモリ+右軸 FPS(本/副モニタ)の 2 軸化し、起動時に本体+観測窓を録画向けに自動配置。基準記録(校正→単発撮影 30 秒間隔×3→放置 180 秒)の秒数を全版で再現。 - 旧版移植(設計判断): bench/v1.6・v1.7(throwaway・非マージ)へ観測フックのみ注入しプレビューのロジックは不変(リーク・コピー方式・Invalidate タイミングを保存)。v1.7 は v1.6 の移植を cherry-pick で流用(プレビュー経路が同一のため)。 - 失敗パターンの圧縮: 旧版は毎フレーム Invalidate で本体 UI スレッドが飽和し、同一スレッドの観測窓がグラフ描画・サンプリングともに WM_PAINT 洪水で飢餓(更新が遅い・サンプル間隔が乱れる)→ 観測窓を専用 UI スレッドへ分離(測定中立)+WS_EX_COMPOSITED でラベルのちらつき抑制。