Newer
Older
TIASShot / docs / 06_TEST / TEST_03_メモリ修正効果計測.md

メモリ修正効果の計測結果(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 baseline0.05 GB0.05 GB
Private peak2.83 GB0.62 GB
増分 (peak−base)2.78 GB0.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 peak0.53 GB0.13 GB
増分 (peak−base)0.48 GB0.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 フレーム)

ContinuousShootMemoryBenchmarkBlockingCollection の生産者-消費者・1 フレーム複数チャンネル変換・フレームバッファを合成フレームで構造再現)での計測。単体反復でなく「実機の連続撮影に近いフロー」での結果。

指標修正前(リーク)修正後(using)
Private peak2.60 GB0.92 GB
増分 (peak−base)2.55 GB0.87 GB
1 フレームあたり増分326 MB/frame112 MB/frame
WorkingSet peak2.47 GB0.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)

# テストプロジェクトを 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 で調整可。