解放やコピーのコスト


初心者向けの話のつづき

解放にもコストがかかる
(いずれにしろ不明な例外起きた時の対応コストが発生するので解放した方がマシかどうかは判断が必要としても)
※個人的には影響ありそうなものを解放スコープに入れて不具合解消に至らず、片っ端からで解消できたような経緯から単体で問題なさそうでも片っ端から解放すべきと言う考え

オブジェクトを生み出さないのがCOM参照解放の基本。
ループ内の新規参照に関しては検討が必要。

ちなみにこんなもの(これ見ると解放コストは誤差レベルなので気にするレベルではない)
1千万回ループ:98.8596516.859秒 内4ミリ秒以上:63
1千万回ループ:110.3761041.376秒 内4ミリ秒以上:30
1千万回ループ:5.7084975.708秒 内4ミリ秒以上:0
最も重要なことはDeepCopyされるオブジェクト返却のラッピングを不用意に呼ばず参照で済ませる事ってこと。
メンテ対象のクラスでもクラス隠蔽の概念的には分離性考えたらDeepCopyだったりするが、COMの場合は解放絡みもついてくるので。。。と言う所。
まあ、1千万回でも誤差レベルと考えて問題のある処理のみ注力すればいいのだが知ってて記述するのと頭の片隅にあるのとで問題発生時の対応が変わるし
ループの中で無邪気にCOMオブジェクトをNewしたりで生み出して嵌る瞬間はあるはずなので書いとく。

var mxdPath = @"C:\temp\TEST.mxd";

using (ComReleaser com = new ComReleaser())
{
  IMapDocument mapDoc = new MapDocumentClass();
  com.ManageLifetime(mapDoc);

  mapDoc.Open(mxdPath);

  var map = mapDoc.get_Map(0);
  com.ManageLifetime(map);

  mapDoc.Close();
  Stopwatch loopCounter = new Stopwatch();
  loopCounter.Start();
  int elpCount = 0;

  //特段なにもしない場合。

  Stopwatch spWatch = new Stopwatch();
  //ループ
  for (int i = 0; i < 10000000; i++)
  {
    spWatch.Reset();
    spWatch.Start();

    var ext = ((IActiveView)map).Extent;
    //com.ManageLifetime(ext);
    //ちなみにこういう場合はループ回数も考えずスタックしてはいけない
    //解放対象として参照され、解放メソッドまで参照されるのでループ内では
  //ループ内用のものを用意するかThrowで解放されなくてもよいものか検討が必要

    spWatch.Stop();
    if (spWatch.ElapsedMilliseconds > 4)
    {
      Console.WriteLine("{0}, {1} , {2}", i, spWatch.ElapsedMilliseconds, ext.Width);
      elpCount++;
    }
  }//end loop

  loopCounter.Stop();
  Console.WriteLine("1千万回ループ:{0}.{1}秒 内4ミリ秒以上:{2}", loopCounter.Elapsed.TotalSeconds, loopCounter.Elapsed.Milliseconds, elpCount);
  loopCounter.Reset();

  GC.Collect();
  GC.WaitForPendingFinalizers();
  GC.Collect();
  GC.WaitForFullGCComplete();

  // FinalReleaseComObjectでつど解放した場合

  loopCounter.Start();
  elpCount = 0;

  spWatch.Reset();
  spWatch.Start();

  for (int i = 0; i < 10000000; i++)
  {
    var ext = ((IActiveView)map).Extent;

    spWatch.Stop();
    if (spWatch.ElapsedMilliseconds > 4)
    {
      Console.WriteLine("{0}, {1} , {2}", i, spWatch.ElapsedMilliseconds, ext.Width);
      elpCount++;
    }
    spWatch.Reset();
    spWatch.Start();

    Marshal.FinalReleaseComObject(ext);
  }//end loop
  loopCounter.Stop();
  Console.WriteLine("1千万回ループ:{0}.{1}秒 内4ミリ秒以上:{2}", loopCounter.Elapsed.TotalSeconds, loopCounter.Elapsed.Milliseconds, elpCount);
  loopCounter.Reset();

  GC.Collect();
  GC.WaitForPendingFinalizers();
  GC.Collect();
  GC.WaitForFullGCComplete();

  //オブジェクト生成しなかった場合
  loopCounter.Start();
  elpCount = 0;

  spWatch.Reset();
  spWatch.Start();
  var extent = ((IActiveView)map).Extent;

  for (int i = 0; i < 10000000; i++)
  {

    spWatch.Stop();
    if (spWatch.ElapsedMilliseconds > 4)
    {
      Console.WriteLine("{0}, {1} , {2}", i, spWatch.ElapsedMilliseconds, extent.Width);
      elpCount++;
    }
    spWatch.Reset();
    spWatch.Start();

  }//end loop
  Marshal.FinalReleaseComObject(extent);

  Console.WriteLine("1千万回ループ:{0}.{1}秒 内4ミリ秒以上:{2}", loopCounter.Elapsed.TotalSeconds, loopCounter.Elapsed.Milliseconds, elpCount);

}//end com
カテゴリー: 開発 タグ: パーマリンク