성능 최적화: 개체 동작

WPF 개체의 기본 동작을 이해하면 기능과 성능 간의 균형을 적절하게 조정할 수 있습니다.

개체에 대한 이벤트 처리기를 제거하지 않으면 개체가 활성 상태로 유지될 수 있음

개체가 해당 이벤트에 전달하는 대리자는 사실상 해당 개체에 대한 참조입니다. 따라서 이벤트 처리기는 예상보다 오래 개체의 활성 상태를 유지할 수 있습니다. 개체의 이벤트를 수신하도록 등록된 개체를 정리하려면 개체를 해제하기 전에 먼저 대리자를 제거해야 합니다. 불필요한 개체를 활성 상태로 유지하면 애플리케이션의 메모리 사용이 늘어납니다. 이러한 현상은 특히 개체가 논리적 트리 또는 시각적 트리의 루트인 경우 특히 그렇습니다.

WPF에는 소스와 수신기 간의 개체 수명 관계를 추적하기 어려운 경우에 유용할 수 있는 이벤트에 대한 약한 이벤트 수신기 패턴이 도입되었습니다. 일부 기존 WPF 이벤트에서 이 패턴을 사용하기도 합니다. 사용자 지정 이벤트를 사용하여 개체를 구현하는 경우 이 패턴이 유용할 수 있습니다. 자세한 내용은 약한 이벤트 패턴을 참조하세요.

지정된 프로세스의 메모리 사용에 대한 정보를 제공할 수 있는 CLR 프로파일러 및 작업 집합 뷰어 등 여러 가지 도구가 있습니다. CLR 프로파일러에는 할당된 형식에 대한 히스토그램, 할당 및 호출 그래프, 다양한 세대의 가비지 수집을 보여 주는 시간 표시 막대, 이러한 수집 이후의 관리되는 힙에 대한 결과 상태, 메서드당 할당 및 어셈블리 로드를 보여 주는 호출 트리 등 할당 프로필에 대한 매우 유용한 여러 뷰가 포함되어 있습니다. 자세한 내용은 성능을 참조하세요.

종속성 속성 및 개체

일반적으로 DependencyObject의 종속성 속성에 액세스하는 작업은 CLR 속성에 액세스하는 것보다 느리지 않습니다. 속성 값을 설정하는 데 약간의 성능 오버헤드가 있지만 값을 가져오는 경우 CLR 속성에서 값을 가져올 때와 속도가 비슷합니다. 이러한 약간의 성능 오버헤드가 발생하지만 종속성 속성이 데이터 바인딩, 애니메이션, 상속 및 스타일 지정과 같은 강력한 기능을 지원합니다. 자세한 내용은 종속성 속성 개요를 참조하세요.

DependencyProperty 최적화

애플리케이션에서 종속성 속성을 정의할 때는 매우 신중해야 합니다. DependencyPropertyAffectsMeasure와 같은 다른 메타데이터 옵션이 아닌 렌더링 형식 메타데이터 옵션에만 영향을 미치는 경우 메타데이터를 재정의하여 이를 표시해야 합니다. 속성 메타데이터 재정의 또는 가져오기에 대한 자세한 내용은 종속성 속성 메타데이터를 참조하세요.

모든 속성 변경이 실제로 측정, 정렬 및 렌더링에 영향을 미치는 것이 아니라면 속성 변경 처리기가 측정, 정렬 및 렌더링 단계를 수동으로 무효화하도록 하는 것이 더 효율적일 수 있습니다. 예를 들어 값이 설정된 한계보다 클 경우에만 배경을 다시 렌더링하도록 결정한 경우 속성 변경 처리기는 값이 설정된 한계를 초과하는 경우에만 렌더링을 무효화합니다.

DependencyProperty를 상속 가능하게 만드는 경우 성능에 미치는 영향

기본적으로 등록된 종속성 속성은 상속되지 않습니다. 그러나 어떤 속성이든 명시적으로 상속 가능하게 만들 수 있습니다. 이는 유용한 기능이지만 속성을 상속 가능한 속성으로 변환하면 속성 무효화를 위한 시간이 길어져서 성능에 영향을 미칩니다.

RegisterClassHandler의 신중한 사용

RegisterClassHandler를 호출하여 인스턴스 상태를 저장하는 경우 모든 인스턴스에 처리기가 호출되어 성능 문제를 발생시킬 수 있으므로 주의해야 합니다. 애플리케이션에서 인스턴스 상태를 저장해야 하는 경우 RegisterClassHandler만 사용합니다.

등록 시 DependencyProperty에 대한 기본값 설정

기본값을 필요로 하는 DependencyProperty를 만들 때 DependencyPropertyRegister 메서드에 대한 매개 변수로 전달된 기본 메타데이터를 사용하여 값을 설정합니다. 생성자 또는 요소의 각 인스턴스에 속성 값을 설정하는 기술보다 이 기술을 사용합니다.

레지스터를 사용하여 PropertyMetadata 값 설정

DependencyProperty를 만들 때 Register 또는 OverrideMetadata 메서드를 사용하여 PropertyMetadata를 설정할 수 있습니다. 개체에 OverrideMetadata를 호출하기 위한 정적 생성자가 있을 수 있지만 이는 적절한 솔루션이 아니며 성능에 영향을 미칩니다. 최적의 성능을 위해 Register 호출 도중 PropertyMetadata를 설정합니다.

Freezable 개체

Freezable은 고정되지 않음과 고정이라는 두 가지 상태를 가지는 특수한 개체 형식입니다. 가능할 때마다 개체를 고정하면 애플리케이션 성능이 향상되며 해당 작업 집합을 줄일 수 있습니다. 자세한 내용은 Freezable 개체 개요를 참조하세요.

Freezable에는 변경될 때마다 발생하는 Changed 이벤트가 있습니다. 그러나 변경 알림은 애플리케이션 성능을 저하시킵니다.

Rectangle에서 동일한 Brush 개체를 사용하는 다음 예제를 고려하세요.

rectangle_1.Fill = myBrush;
rectangle_2.Fill = myBrush;
rectangle_3.Fill = myBrush;
// ...
rectangle_10.Fill = myBrush;
rectangle_1.Fill = myBrush
rectangle_2.Fill = myBrush
rectangle_3.Fill = myBrush
' ...
rectangle_10.Fill = myBrush

기본적으로 WPF는 Rectangle 개체의 Fill 속성을 무효화하기 위해 SolidColorBrush 개체의 Changed 이벤트에 대한 이벤트 처리기를 제공합니다. 이 경우 SolidColorBrushChanged 이벤트를 발생시켜야 할 때마다 각 Rectangle에 대한 콜백 함수를 호출해야 합니다. 이러한 콜백 함수 호출의 누적은 커다란 성능 저하를 유발합니다. 또한 이 시점에서 처리기를 추가 및 제거하면 애플리케이션에서 이 작업을 위해 전체 목록을 통과해야 하므로 성능에 상당한 영향을 미칩니다. 애플리케이션 시나리오에서 SolidColorBrush를 변경하지 않는 경우 불필요하게 Changed 이벤트 처리기를 유지하는 데 따른 비용이 발생합니다.

Freezable을 고정하면 변경 알림 유지 관리에 더 이상 리소스를 사용할 필요가 없으므로 성능을 향상시킬 수 있습니다. 아래 표는 IsFrozen 속성이 true로 설정될 때 설정되지 않을 때와 비교하여 간단한 SolidColorBrush의 크기를 보여줍니다. 10개의 Rectangle 개체의 Fill 속성에 1개의 브러시를 적용한다고 가정합니다.

State 크기
고정 SolidColorBrush 212바이트
비고정 SolidColorBrush 972바이트

다음 코드 샘플에서는 이러한 개념을 보여 줍니다.

Brush frozenBrush = new SolidColorBrush(Colors.Blue);
frozenBrush.Freeze();
Brush nonFrozenBrush = new SolidColorBrush(Colors.Blue);

for (int i = 0; i < 10; i++)
{
    // Create a Rectangle using a non-frozed Brush.
    Rectangle rectangleNonFrozen = new Rectangle();
    rectangleNonFrozen.Fill = nonFrozenBrush;

    // Create a Rectangle using a frozed Brush.
    Rectangle rectangleFrozen = new Rectangle();
    rectangleFrozen.Fill = frozenBrush;
}
Dim frozenBrush As Brush = New SolidColorBrush(Colors.Blue)
frozenBrush.Freeze()
Dim nonFrozenBrush As Brush = New SolidColorBrush(Colors.Blue)

For i As Integer = 0 To 9
    ' Create a Rectangle using a non-frozed Brush.
    Dim rectangleNonFrozen As New Rectangle()
    rectangleNonFrozen.Fill = nonFrozenBrush

    ' Create a Rectangle using a frozed Brush.
    Dim rectangleFrozen As New Rectangle()
    rectangleFrozen.Fill = frozenBrush
Next i

고정되지 않은 Freezable에 대한 처리기를 변경하면 개체가 활성 상태로 유지될 수 있음

개체가 Freezable 개체의 Changed 이벤트에 전달하는 대리자는 사실상 해당 개체에 대한 참조입니다. 따라서 Changed 이벤트 처리기는 예상보다 오래 개체의 활성 상태를 유지할 수 있습니다. Freezable 개체의 Changed 이벤트를 수신하도록 등록된 개체를 정리하려면 개체를 해제하기 전에 먼저 대리자를 제거해야 합니다.

WPF는 또한 Changed 이벤트를 내부적으로 연결합니다. 예를 들어 Freezable을 값으로 갖는 모든 종속성 속성은 자동으로 Changed 이벤트를 수신 대기합니다. Brush를 갖는 Fill 속성은 이 개념을 보여줍니다.

Brush myBrush = new SolidColorBrush(Colors.Red);
Rectangle myRectangle = new Rectangle();
myRectangle.Fill = myBrush;
Dim myBrush As Brush = New SolidColorBrush(Colors.Red)
Dim myRectangle As New Rectangle()
myRectangle.Fill = myBrush

myRectangle.FillmyBrush를 할당할 때 Rectangle 개체를 가리키는 대리자가 SolidColorBrush 개체의 Changed 이벤트에 추가됩니다. 이는 다음 코드에서 실제로 myRect를 가비지 수집에 적합하게 만들지 않음을 의미합니다.

myRectangle = null;
myRectangle = Nothing

이 경우 myBrushmyRectangle을 계속해서 활성 상태로 유지하고 Changed 이벤트가 발생하면 해당 이벤트로 콜백합니다. 새 RectangleFill 속성에 myBrush를 할당하면 다른 이벤트 처리기가 myBrush에 추가됩니다.

이러한 개체 형식을 정리하는 데 좋은 방법은 Fill 속성에서 Brush를 제거하는 것으로, 이를 통해 Changed 이벤트 처리기가 제거됩니다.

myRectangle.Fill = null;
myRectangle = null;
myRectangle.Fill = Nothing
myRectangle = Nothing

사용자 인터페이스 가상화

WPF는 또한 데이터 바인딩된 자식 콘텐츠를 자동으로 “가상화”하는 StackPanel 요소의 변형을 제공합니다. 여기서 가상화라는 단어는 화면에 표시되는 항목에 따라 많은 수의 데이터 항목에서 개체의 하위 집합이 생성되는 기술을 가리킵니다. 특정 시점에 화면에 표시되는 것보다 많은 개수의 UI 요소를 생성하는 것은 메모리 및 프로세서 측면에서 비효율적입니다. VirtualizingStackPanel(VirtualizingPanel에 의해 제공된 기능을 통한)은 표시된 항목을 계산하고 ItemsControl(예: ListBox 또는 ListView)에서 ItemContainerGenerator를 사용하여 표시된 항목에 대해서만 요소를 만듭니다.

성능을 최적화하기 위해 이러한 항목에 대한 시각적 개체는 화면에 보이는 경우에만 생성되거나 활성 상태를 유지합니다. 컨트롤의 가시 영역에 더 이상 존재하지 않는 시각적 개체는 제거될 수 있습니다. 이는 데이터 개체가 로컬 컬렉션에 전혀 존재하지 않고 필요에 따라 스트리밍되는 데이터 가상화와 혼동해서는 안 됩니다.

아래 표는 5,000개의 TextBlock 요소를 StackPanelVirtualizingStackPanel에 추가 및 렌더링할 때 경과된 시간을 보여줍니다. 이 시나리오에서 측정값은 텍스트 문자열을 ItemsControl 개체의 ItemsSource 속성에 연결하는 시간부터 패널 요소가 텍스트 문자열을 표시할 때까지의 시간을 나타냅니다.

호스트 패널 렌더링 시간(ms)
StackPanel 3210
VirtualizingStackPanel 46

참고 항목