다음을 통해 공유


XAML 마크업 최적화

XAML 태그를 구문 분석하여 메모리에서 개체를 생성하는 것은 복잡한 UI에 시간이 많이 걸립니다. XAML 태그의 구문 분석 및 로드 시간과 앱의 메모리 효율성을 개선하기 위해 수행할 수 있는 몇 가지 작업은 다음과 같습니다.

앱을 시작할 때 로드되는 XAML 태그를 초기 UI에 필요한 항목으로만 제한합니다. 초기 페이지(페이지 리소스 포함)의 태그를 검사하고 즉시 필요하지 않은 추가 요소를 로드하지 않는지 확인합니다. 이러한 요소는 리소스 사전, 처음에 축소된 요소, 그리고 다른 요소 위에 그려진 요소와 같은 다양한 소스에서 가져올 수 있습니다.

효율성을 위해 XAML을 최적화하려면 절편이 필요합니다. 모든 상황에 대한 단일 솔루션이 항상 있는 것은 아닙니다. 여기서는 몇 가지 일반적인 문제를 살펴보고 앱에 적합한 장단점을 만드는 데 사용할 수 있는 지침을 제공합니다.

요소 수 최소화

XAML 플랫폼은 많은 수의 요소를 표시할 수 있지만 원하는 시각적 개체를 달성하기 위해 가장 적은 수의 요소를 사용하여 앱을 배치하고 더 빠르게 렌더링할 수 있습니다.

UI 컨트롤을 배치하는 방법에서 선택하는 것은 앱이 시작될 때 생성되는 UI 요소의 수에 영향을 줍니다. 레이아웃 최적화에 대한 자세한 내용은 XAML 레이아웃 최적화를 참조하세요.

각 요소가 각 데이터 항목에 대해 다시 생성되므로 데이터 템플릿에서 요소 수는 매우 중요합니다. 목록 또는 그리드에서 요소 수를 줄이는 방법에 대한 정보는 항목당 요소 감소을 참고하시고, ListView 및 GridView UI 최적화 문서를 확인하세요.

여기서는 시작 시 앱이 로드해야 하는 요소의 수를 줄일 수 있는 몇 가지 다른 방법을 살펴봅니다.

항목 만들기 연기

XAML 마크업에 즉시 표시되지 않는 요소가 포함되어 있다면, 이러한 요소를 표시할 때까지 로드를 연기할 수 있습니다. 예를 들어 탭과 유사한 UI에서 보조 탭과 같이 보이지 않는 콘텐츠 만들기를 지연할 수 있습니다. 또는 기본적으로 그리드 보기에 항목을 표시할 수 있지만 사용자가 대신 목록에서 데이터를 볼 수 있는 옵션을 제공합니다. 필요할 때까지 목록 로드를 지연할 수 있습니다.

Visibility 속성 대신 x:Load 특성을 사용하여 요소가 표시되는 시기를 제어합니다. 요소의 가시성이 Collapsed으로 설정되면, 렌더링 패스 중에 건너뛰지만 메모리에서 개체 인스턴스 비용은 여전히 발생합니다. 대신 x:Load를 사용하는 경우 프레임워크는 필요할 때까지 개체 인스턴스를 만들지 않으므로 메모리 비용이 훨씬 낮습니다. 단점은 UI가 로드되지 않을 때 작은 메모리 오버헤드(약 600바이트)를 지불하는 것입니다.

비고

x:Load 또는 x:DeferLoadStrategy 특성을 사용하여 요소 로드를 지연할 수 있습니다. x:Load 특성은 Windows 10 크리에이터스 업데이트(버전 1703, SDK 빌드 15063)부터 사용할 수 있습니다. x:Load를 사용하려면 Visual Studio 프로젝트의 대상 최소 버전은 Windows 10 크리에이터 업데이트(10.0, 빌드 15063) 이어야 합니다. 이전 버전을 대상으로 지정하려면 x:DeferLoadStrategy를 사용합니다.

다음 예제에서는 다른 기술을 사용하여 UI 요소를 숨길 때 요소 수와 메모리 사용의 차이를 보여 줍니다. 동일한 항목을 포함하는 ListView 및 GridView는 페이지의 루트 그리드에 배치됩니다. ListView는 표시되지 않지만 GridView가 표시됩니다. 이러한 각 예제의 XAML은 화면에서 동일한 UI를 생성합니다. Visual Studio의 도구를 사용하여 요소 수 및 메모리 사용을 확인하기 위해 프로파일링 및 성능을 수행합니다.

옵션 1 - 비효율적

여기서 ListView는 로드되지만 너비가 0이므로 표시되지 않습니다. ListView 및 각 자식 요소는 시각적 트리에서 만들어지고 메모리에 로드됩니다.

<!-- NOTE: EXAMPLE OF INEFFICIENT CODE; DO NOT COPY-PASTE.-->
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

    <ListView x:Name="List1" Width="0">
        <ListViewItem>Item 1</ListViewItem>
        <ListViewItem>Item 2</ListViewItem>
        <ListViewItem>Item 3</ListViewItem>
        <ListViewItem>Item 4</ListViewItem>
        <ListViewItem>Item 5</ListViewItem>
        <ListViewItem>Item 6</ListViewItem>
        <ListViewItem>Item 7</ListViewItem>
        <ListViewItem>Item 8</ListViewItem>
        <ListViewItem>Item 9</ListViewItem>
        <ListViewItem>Item 10</ListViewItem>
    </ListView>

    <GridView x:Name="Grid1">
        <GridViewItem>Item 1</GridViewItem>
        <GridViewItem>Item 2</GridViewItem>
        <GridViewItem>Item 3</GridViewItem>
        <GridViewItem>Item 4</GridViewItem>
        <GridViewItem>Item 5</GridViewItem>
        <GridViewItem>Item 6</GridViewItem>
        <GridViewItem>Item 7</GridViewItem>
        <GridViewItem>Item 8</GridViewItem>
        <GridViewItem>Item 9</GridViewItem>
        <GridViewItem>Item 10</GridViewItem>
    </GridView>
</Grid>

ListView가 로드된 실시간 시각적 트리입니다. 페이지의 총 요소 수는 89개입니다.

목록 보기가 있는 시각적 트리의 스크린샷

ListView 및 그 자식 요소들은 메모리에 로드됩니다.

ListView 및 해당 자식이 메모리에 로드되는 것을 보여 주는 관리되는 메모리 테스트 앱 1 점 E X E 테이블의 스크린샷

옵션 2 - 개선

여기서 ListView의 Visibility 속성이 숨김으로 설정됩니다(다른 XAML은 원본과 동일합니다). ListView는 시각적 트리에서 생성되지만, 그 자식 요소들은 생성되지 않습니다. 그러나 메모리에 로드되므로 메모리 사용은 이전 예제와 동일합니다.

<ListView x:Name="List1" Visibility="Collapsed">

ListView가 축소된 상태의 라이브 비주얼 트리. 페이지의 총 요소 수는 46개입니다.

축소된 목록 보기가 있는 시각적 트리의 스크린샷

ListView 및 그 자식 요소들은 메모리에 로드됩니다.

업데이트된 Managed Memory Test App 1 dot EXE 테이블의 스크린샷으로, ListView 및 그 자식들이 메모리에 로드된 모습을 보여줍니다.

옵션 3 - 가장 효율적

여기서 ListView에는 x:Load 특성이 False로 설정됩니다(다른 XAML은 원본과 동일). ListView는 시각적 트리에서 생성되거나 시작 시 메모리에 로드되지 않습니다.

<ListView x:Name="List1" Visibility="Collapsed" x:Load="False">

ListView가 로드되지 않은 라이브 시각적 트리입니다. 페이지의 총 요소 수는 45개입니다.

목록 보기가 로드되지 않은 시각적 트리

ListView 및 해당 자식은 메모리에 로드되지 않습니다.

목록 보기가 있는 시각적 트리

비고

이러한 예제의 요소 수와 메모리 사용은 매우 작으며 개념을 보여 주는 데만 표시됩니다. 이러한 예제에서는 x:Load를 사용하는 오버헤드가 메모리 절약보다 크므로 앱이 도움이 되지 않습니다. 앱에서 프로파일링 도구를 사용하여 앱이 지연된 로드를 활용할 수 있는지 여부를 결정해야 합니다.

레이아웃 패널 속성 사용

레이아웃 패널에는 배경 속성이 있으므로 색을 지정하기 위해 패널 앞에 사각형 을 배치할 필요가 없습니다.

비효율적인

<!-- NOTE: EXAMPLE OF INEFFICIENT CODE; DO NOT COPY-PASTE. -->
<Grid>
    <Rectangle Fill="Black"/>
</Grid>

효율적인

<Grid Background="Black"/>

레이아웃 패널에는 기본 제공 테두리 속성도 있으므로 레이아웃 패널 주위에 Border 요소를 배치할 필요가 없습니다. 자세한 정보 및 예제는 XAML 레이아웃 최적화 를 참조하세요.

벡터 기반 요소 대신 이미지 사용

동일한 벡터 기반 요소를 충분한 시간 동안 다시 사용하면 Image 요소를 대신 사용하는 것이 더 효율적입니다. CPU가 각 개별 요소를 별도로 만들어야 하므로 벡터 기반 요소는 더 비쌀 수 있습니다. 이미지 파일은 한 번만 디코딩해야 합니다.

자원 및 자원 사전 최적화

일반적으로 리소스 사전을 사용하여 앱의 여러 위치에서 참조하려는 리소스를 전역 수준에서 저장합니다. 예를 들어 스타일, 브러시, 템플릿 등이 있습니다.

일반적으로 리소스를 요청하지 않는 한 리소스를 인스턴스화하지 않도록 ResourceDictionary 최적화했습니다. 그러나 리소스가 불필요하게 인스턴스화되지 않도록 피해야 하는 상황이 있습니다.

x:Name을 사용하는 리소스

x:Key 속성을 사용하여 리소스를 참조하세요. x:Name 속성이인 리소스는 플랫폼 최적화를 받을 수 없습니다. 대신, ResourceDictionary가 생성되면 즉시 인스턴스화됩니다. 이 현상은 x:Name이 플랫폼에 앱이 이 리소스에 대한 필드 액세스가 필요하다는 것을 알리기 때문입니다. 따라서 플랫폼은 참조를 생성하기 위해 어떤 대상을 만들어야 합니다.

UserControl의 ResourceDictionary

UserControl 내부에 정의된 ResourceDictionary는 페널티를 전달합니다. 플랫폼은 UserControl의 모든 인스턴스에 대해 이러한 ResourceDictionary의 복사본을 만듭니다. 많이 사용되는 UserControl이 있는 경우 UserControl에서 ResourceDictionary를 이동하여 페이지 수준에 배치합니다.

Resource 및 ResourceDictionary 범위

페이지가 다른 파일에 정의된 사용자 컨트롤 또는 리소스를 참조하는 경우 프레임워크도 해당 파일을 구문 분석합니다.

여기서는 InitialPage.xamlExampleResourceDictionary.xaml의 리소스를 하나 사용하므로 시작 시 ExampleResourceDictionary.xaml 전체를 구문 분석해야 합니다.

InitialPage.xaml.

<Page x:Class="ExampleNamespace.InitialPage" ...>
    <Page.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="ExampleResourceDictionary.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Page.Resources>

    <Grid>
        <TextBox Foreground="{StaticResource TextBrush}"/>
    </Grid>
</Page>

ExampleResourceDictionary.xaml.

<ResourceDictionary>
    <SolidColorBrush x:Key="TextBrush" Color="#FF3F42CC"/>

    <!--This ResourceDictionary contains many other resources that
        are used in the app, but are not needed during startup.-->
</ResourceDictionary>

앱 전체의 여러 페이지에서 리소스를 사용하는 경우 App.xaml 에 리소스를 저장하는 것이 좋은 방법이며 중복을 방지합니다. 그러나 App.xaml은(는) 앱 시작 시 구문 분석되므로, 페이지가 초기 페이지가 아닌 경우 그 페이지에서만 사용되는 모든 리소스는 해당 페이지의 로컬 리소스에 포함시켜야 합니다. 이 예제에서는 한 페이지(초기 페이지가 아님)에서만 사용되는 리소스가 포함된 App.xaml 을 보여 줍니다. 이렇게 하면 불필요하게 앱 시작 시간이 늘어나게 됩니다.

app.xaml

<!-- NOTE: EXAMPLE OF INEFFICIENT CODE; DO NOT COPY-PASTE. -->
<Application ...>
     <Application.Resources>
        <SolidColorBrush x:Key="DefaultAppTextBrush" Color="#FF3F42CC"/>
        <SolidColorBrush x:Key="InitialPageTextBrush" Color="#FF3F42CC"/>
        <SolidColorBrush x:Key="SecondPageTextBrush" Color="#FF3F42CC"/>
        <SolidColorBrush x:Key="ThirdPageTextBrush" Color="#FF3F42CC"/>
    </Application.Resources>
</Application>

InitialPage.xaml.

<!-- NOTE: EXAMPLE OF INEFFICIENT CODE; DO NOT COPY-PASTE. -->
<Page x:Class="ExampleNamespace.InitialPage" ...>
    <StackPanel>
        <TextBox Foreground="{StaticResource InitialPageTextBrush}"/>
    </StackPanel>
</Page>

SecondPage.xaml입니다.

<!-- NOTE: EXAMPLE OF INEFFICIENT CODE; DO NOT COPY-PASTE. -->
<Page x:Class="ExampleNamespace.SecondPage" ...>
    <StackPanel>
        <Button Content="Submit" Foreground="{StaticResource SecondPageTextBrush}"/>
    </StackPanel>
</Page>

이 예제를 보다 효율적으로 만들려면, SecondPageTextBrushSecondPage.xaml로 이동하고, ThirdPageTextBrushThirdPage.xaml로 이동하십시오. InitialPageTextBrush은 앱 시작 시 애플리케이션 리소스를 구문 분석해야 하므로 App.xaml에 남아 있을 수 있습니다.

동일하게 보이는 여러 브러시를 하나의 리소스로 통합

XAML 플랫폼은 가능한 한 자주 다시 사용할 수 있도록 일반적으로 사용되는 개체를 캐시하려고 합니다. 그러나 XAML은 태그의 한 조각에 선언된 브러시가 다른 부분에 선언된 브러시와 같은지 쉽게 알 수 없습니다. 이 예제에서는 SolidColorBrush 를 사용하여 보여 주지만 GradientBrush에서 사례는 더 가능성이 높고 더 중요합니다. 미리 정의된 색을 사용하는 브러시도 확인합니다. 예를 들어 "Orange""#FFFFA500" 동일한 색입니다.

비효율적인.

<!-- NOTE: EXAMPLE OF INEFFICIENT CODE; DO NOT COPY-PASTE. -->
<Page ... >
    <StackPanel>
        <TextBlock>
            <TextBlock.Foreground>
                <SolidColorBrush Color="#FFFFA500"/>
            </TextBlock.Foreground>
        </TextBlock>
        <Button Content="Submit">
            <Button.Foreground>
                <SolidColorBrush Color="#FFFFA500"/>
            </Button.Foreground>
        </Button>
    </StackPanel>
</Page>

중복을 해결하려면 브러시를 리소스로 정의합니다. 다른 페이지의 컨트롤에서 동일한 브러시를 사용하는 경우 App.xaml로 이동합니다.

능률적인.

<Page ... >
    <Page.Resources>
        <SolidColorBrush x:Key="BrandBrush" Color="#FFFFA500"/>
    </Page.Resources>

    <StackPanel>
        <TextBlock Foreground="{StaticResource BrandBrush}" />
        <Button Content="Submit" Foreground="{StaticResource BrandBrush}" />
    </StackPanel>
</Page>

오버드로잉 최소화

덮어쓰기는 동일한 화면 픽셀에 둘 이상의 개체가 그려지는 경우에 발생합니다. 가이드라인과 요소 수를 줄이려는 욕구 사이에는 때때로 균형을 맞추어야 할 때가 있습니다.

시각적 진단 도구로 DebugSettings.IsOverdrawHeatMapEnabled을(를) 사용합니다. 장면에서 몰랐던 개체가 그려지는 것을 볼 수 있습니다.

투명 또는 숨겨진 요소

요소가 다른 요소 뒤에 투명하거나 숨겨져 있어 표시되지 않고 레이아웃에 기여하지 않는 경우 삭제합니다. 요소가 초기 시각적 상태에서는 표시되지 않지만, 다른 시각적 상태에서는 표시되는 경우, x:Load를 사용하여 상태를 제어하거나 요소 자체의 VisibilityCollapsed로 설정하고, 적절한 상태에서는 그 값을 Visible로 변경하세요. 이 추론에는 예외가 있습니다. 일반적으로 대부분의 시각적 상태에 있는 속성 값은 요소에서 로컬로 설정하는 것이 가장 좋습니다.

복합 요소

여러 요소를 계층화하여 효과를 만드는 대신 복합 요소를 사용합니다. 이 예제에서 결과는 위쪽 절반이 검정이고(Grid배경에서) 아래쪽 절반이 회색인 2톤 셰이프입니다(반투명 흰색 사각형에서 Grid검은색 배경에 알파 혼합). 여기서는 결과를 달성하는 데 필요한 픽셀의 150% 채워지고 있습니다.

비효율적인.

<!-- NOTE: EXAMPLE OF INEFFICIENT CODE; DO NOT COPY-PASTE. -->
<Grid Background="Black">
    <Grid.RowDefinitions>
        <RowDefinition Height="*"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <Rectangle Grid.Row="1" Fill="White" Opacity=".5"/>
</Grid>

능률적인.

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="*"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <Rectangle Fill="Black"/>
    <Rectangle Grid.Row="1" Fill="#FF7F7F7F"/>
</Grid>

레이아웃 패널

레이아웃 패널에는 영역에 색을 지정하고 자식 요소를 배치하는 두 가지 용도가 있을 수 있습니다. z 순서상 뒤에 있는 요소가 이미 어떤 영역에 색을 칠하고 있는 경우, 앞쪽에 있는 레이아웃 패널이 그 영역을 다시 그릴 필요가 없습니다. 대신 자식들을 배치하는 데만 집중할 수 있습니다. 다음은 예제입니다.

비효율적인.

<!-- NOTE: EXAMPLE OF INEFFICIENT CODE; DO NOT COPY-PASTE. -->
<GridView Background="Blue">
    <GridView.ItemTemplate>
        <DataTemplate>
            <Grid Background="Blue"/>
        </DataTemplate>
    </GridView.ItemTemplate>
</GridView>

능률적인.

<GridView Background="Blue">
    <GridView.ItemTemplate>
        <DataTemplate>
            <Grid/>
        </DataTemplate>
    </GridView.ItemTemplate>
</GridView>

Grid이 적중 테스트가 가능해야 한다면 투명한 배경 값을 설정하십시오.

테두리

Border 요소를 사용하여 개체 주위에 테두리를 그립니다. 이 예제에서는 Grid이 임시 테두리로서 TextBox주위에 사용됩니다. 그러나 가운데 셀의 모든 픽셀은 과도하게 그려집니다.

비효율적인.

<!-- NOTE: EXAMPLE OF INEFFICIENT CODE; DO NOT COPY-PASTE. -->
<Grid Background="Blue" Width="300" Height="45">
    <Grid.RowDefinitions>
        <RowDefinition Height="5"/>
        <RowDefinition/>
        <RowDefinition Height="5"/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="5"/>
        <ColumnDefinition/>
        <ColumnDefinition Width="5"/>
    </Grid.ColumnDefinitions>
    <TextBox Grid.Row="1" Grid.Column="1"></TextBox>
</Grid>

능률적인.

<Border BorderBrush="Blue" BorderThickness="5" Width="300" Height="45">
    <TextBox/>
</Border>

여백

여백에 유의하세요. 음의 여백이 다른 요소의 렌더링 범위까지 확장되어 오버드로잉이 발생하면, 인접한 두 요소가 겹칠 수 있습니다(의도치 않게 발생 가능).

정적 콘텐츠 캐시

오버드로잉의 또 다른 원인은 여러 겹치는 요소로 만든 셰이프입니다. 복합 셰이프를 포함하는 UIElementCacheModeBitmapCache로 설정하면, 플랫폼은 요소를 비트맵으로 한 번 렌더링한 후 각 프레임에 해당 비트맵을 사용하여 오버드로잉 대신 렌더링을 대체합니다.

비효율적인.

<Canvas Background="White">
    <Ellipse Height="40" Width="40" Fill="Blue"/>
    <Ellipse Canvas.Left="21" Height="40" Width="40" Fill="Blue"/>
    <Ellipse Canvas.Top="13" Canvas.Left="10" Height="40" Width="40" Fill="Blue"/>
</Canvas>

세 개의 단색 원이 있는 벤 다이어그램

위의 이미지는 결과입니다. 여기에 오버드로우된 지역의 지도가 있습니다. 진한 빨간색은 초과 인출의 양이 더 높다는 것을 나타냅니다.

겹치는 영역을 보여 주는 벤 다이어그램

능률적인.

<Canvas Background="White" CacheMode="BitmapCache">
    <Ellipse Height="40" Width="40" Fill="Blue"/>
    <Ellipse Canvas.Left="21" Height="40" Width="40" Fill="Blue"/>
    <Ellipse Canvas.Top="13" Canvas.Left="10" Height="40" Width="40" Fill="Blue"/>
</Canvas>

CacheMode사용에 주목하십시오. 하위 도형에 애니메이션이 있는 경우에는, 비트맵 캐시가 매 프레임 다시 생성되어 목적을 상실할 수 있으므로 이 기술을 사용하지 마세요.

XBF2 사용

XBF2는 런타임에 모든 텍스트 분석 비용을 회피하는 XAML 태그의 이진 표현입니다. 또한 부하 및 트리 생성을 위해 이진 파일을 최적화하고 XAML 형식에 대한 "빠른 경로"를 통해 VSM, ResourceDictionary, 스타일 등과 같은 힙 및 개체 생성 비용을 개선할 수 있습니다. 완전히 메모리 매핑되므로 XAML 페이지를 로드하고 읽기 위한 힙 공간이 없습니다. 또한 appx에 저장된 XAML 페이지의 디스크 공간을 줄입니다. XBF2는 더 간결한 표현이며 비교 XAML/XBF1 파일의 디스크 공간을 최대 50%줄일 수 있습니다. 예를 들어 기본 제공 사진 앱은 XBF2로 변환한 후 약 60개의% 감소가 XBF1 자산의 약 1mb에서 XBF2 자산의 ~400kb로 감소했습니다. 또한 앱은 CPU에서 15~20개% Win32 힙에서 10~15개% 혜택을 보았습니다.

프레임워크에서 제공하는 XAML 기본 제공 컨트롤 및 사전은 이미 완전히 XBF2를 사용할 수 있습니다. 사용자 고유의 앱에 대해 프로젝트 파일이 TargetPlatformVersion 8.2 이상을 선언하는지 확인합니다.

XBF2가 있는지 확인하려면 이진 편집기에서 앱을 엽니다. XBF2가 있는 경우 12번째 및 13번째 바이트는 00 02입니다.