2010년 2월 6일 토요일

[Tips]Understanding Cursor - Recycling

이 문서는 ArcGIS 9.3. 버전을 기준으로 작성되었으며, .NET C#(3.0) 샘플 코드 실행을 위해서는 다음의 어셈블리를 참조해야 한다.
- ESRI.ArcGIS.ADF
- ESRI.ArcGIS.Geodatabase
- ESRI.ArcGIS.System (ESRI.ArcGIS.esriSystem)

이 문서는 Geodatabase API를 사용하는 개발자들에게 CheatSheet(커닝페이퍼?)을 제공할 목적으로 작성되었으며 주요 내용은 성능향상, 범하기 쉬운 실수 등을 제공한다.
예제 코드는 특정 상황에 대한 샘플 코드로서 패턴을 제공하고, 상황에 따라 오류를 비교하기 위해 임의의 오류를 포함하고 있으므로 이 코드를 재활용해서는 안된다.


② Understanding Cursor - Recycling
Recycling은 Row(IRow, IFeature)가 Cursor(ICursor, IFeatureCursor)로부터 어떻게 생성될 것인지를 결정하는 속성으로 ITable.Search 와 ISelectionSet.Search를 포함하는 몇몇 Cursor 생성에 대한 Boolean 파라미터이다.
예)
   ICursor myCursor = myTable.Search(null, false);
   IFeatureCursor myCursor = myFeatureLayer.Search(null, true);

만약
Recycling Cursor를 사용하면, Cursor로부터 리턴되는 Row의 수에 상관없이 오직 단일 Row에 대한 메모리만 할당한다. 이는 메모리 사용량과 실행시간의 측면에서는 성능 개선에 대한 이점을 제공하지만 특정 워크플로우에 대해서는 단점이 있다.

Recycling Cursor가 유용할 경우는 특정 시점에 단일 Row만 참조하는 상황이다.
예를 들면, 지오메트리를 그리거나 현재 Row의 Object ID를 콘솔 윈도우에 표시하는 경우이다.

Cursor에서 여러 개의 Row를 반환하여 동시에 비교하거나, 편집할 때는 Recycling Cursor를 사용하면 안된다.

FeatureCursor로부터 첫 두개의 지오메트리가 같은지 비교하는 다음의 코드를 살펴보자.
[code c#]
public static void RecyclingInappropriateExample(IFeatureClass featureClass, Boolean enableRecycling)
{
  using(ComReleaser comReleaser = new ComReleaser())
  {
    // Create a search cursor.
    IFeatureCursor featureCursor = featureClass.Search(null, enableRecycling);
    comReleaser.ManageLifetime(featureCursor);

    // Get the first two geometries and see if they intersect.
    IFeature feature1 = featureCursor.NextFeature();
    IFeature feature2 = featureCursor.NextFeature();
    
    IRelationalOperator relationalOperator = (IRelationalOperator)feature1.Shape;
    
    Boolean geometriesEqual = relationalOperator.Equals(feature2.Shape);
    
    Console.WriteLine("Geometries are equal: {0}", geometriesEqual);
  }
}
[/code]
만약 Recyling Cursor를 사용하면, 위 코드는 항상 True를 리턴한다. 왜냐하면 피쳐1과 피쳐2는 같은 객체를 참조하기 때문이다. 두번째의 NextFeature 호출은 새로운 Row를 생성하지 않고, 단순히 기존 Row의 값만 덮어쓴다.
IRelationalOperator.Equals는 단지 지오메트리 자체를 비교한다. 같은 이유로 두 피쳐의 속성이나 Objectd ID간의 비교도 True를 반환한다.

이에 반해
Non-recycling Cursor는 위의 경우처럼 얘기치 않은 결과를 반환할 가능성이 Recyling Cursor보다 적지만 성능에 대한 상당한 불이익을 감수해야 한다.

아래 코드는 피쳐클래스의 Cursor를 열고 모든 피쳐의 면적합을 계산하는 예이다.
이 코드는 패치된 이전의 어떤 Row도 참조하지 않기 때문에, Recyling Cursor의 사용이 최선의 방법이다.

[code c#]
public static void RecyclingAppropriateExample(IFeatureClass featureClass,  Boolean enableRecycling)
{
  using(ComReleaser comReleaser = new ComReleaser())
  {
    // Create a search cursor.
    IFeatureCursor featureCursor = featureClass.Search(null, enableRecycling);
    comReleaser.ManageLifetime(featureCursor);

    // Create a sum of each geometry's area.
    IFeature feature = null;
    double totalShapeArea = 0;
    while ((feature = featureCursor.NextFeature()) != null)
    {
      IArea shapeArea = (IArea)feature.Shape;
      totalShapeArea += shapeArea.Area;
    }

    Console.WriteLine("Total shape area: {0}", totalShapeArea);
  }
}
[/code]
위 테스트 코드는 약 500,000개의 피쳐를 포함하는 파일 지오데이터베이스 피쳐클래스에 대해서 수행했으며 결과는 다음과 같다.
 - 프로세스의 작업세트(Working Set[footnote]프로세스가 실행될 때 프로세스 전용으로 할당된 물리적 메모리 페이지 그룹을 그 프로세스의 작업 세트(working set)라고 한다. 작업 세트에 포함된 페이지 수는 시스템 전반에 걸쳐 사용 가능한 총 페이지 수에 따라서 증가되거나 줄어든다.[/footnote])는 Recyling Cursor의 경우 ~4%,
Non-recycling Cursor의 경우 ~48% 증가
 - Non-recycling Cursor의 경우 2.25배 이상의 시간이 더 소요
 
Non-recycling Cursor를 부적절하게 사용할 떄, 최악의 경우 Recyling Cursor를 사용할 때 보다 working set는 거의 250%, 12배 이상의 실행시간이 소요될 수 있다고 한다.
 

출처 : ArcGIS Resource Center

각주: