Windows 런타임 구성 요소 및 interop 최적화하기
Interop 성능 문제를 방지하면서 네이티브 형식과 관리되는 형식 간의 Interop 및 Windows 런타임 구성 요소를 사용하는 Windows 앱을 만듭니다.
Windows 런타임 구성 요소와의 상호 운용성을 위한 모범 사례
주의하지 않으면 Windows 런타임 구성 요소 사용이 앱 성능에 큰 영향을 미칠 수 있습니다. 이 섹션에서는 앱에서 Windows 런타임 구성 요소를 사용할 때 우수한 성능을 얻는 방법을 설명합니다.
소개
상호 운용성이 성능에 큰 영향을 미칠 수 있으며, 자신도 모르는 새 상호 운용성을 이용하고 있는 경우도 있습니다. Windows 런타임은 사용자가 생산성을 높이고 다른 언어로 개발된 코드를 다시 사용할 수 있도록 여러 상호 운용성 문제를 다룹니다. Windows 런타임의 기능을 효과적으로 활용하는 것이 좋지만, 성능에 미칠 영향도 알아둘 필요가 있습니다. 이 섹션에서는 상호 운용성이 앱 성능에 미치는 영향을 줄이기 위해 수행할 수 있는 작업에 대해 설명합니다.
Windows 런타임에는 유니버설 Windows 런타임 플랫폼 앱을 만들 수 있는 모든 언어에서 액세스 가능한 형식의 라이브러리가 있습니다. Windows 런타임 형식을 C# 또는 Microsoft Visual Basic에서 .NET 개체를 사용하는 것과 동일한 방법으로 사용합니다. Windows 런타임 구성 요소에 액세스하기 위해 플랫폼에서 메서드를 호출하게 할 필요 없습니다. 이렇게 하면 앱 작성이 훨씬 덜 복잡해지지만 예상보다 더 많은 상호 운용성이 발생할 수 있다는 점을 인식하는 것이 중요합니다. Windows 런타임 구성 요소가 C# 또는 Visual Basic이 아닌 언어로 개발된 경우 그 구성 요소를 사용할 때 상호 운용성의 경계를 넘게 됩니다. 상호 운용성 경계를 넘으면 앱의 성능에 영향을 줄 수 있습니다.
C# 또는 Visual Basic으로 Universal Windows Platform 앱을 개발할 때 자주 사용하는 두 가지 API 집합은 Windows Runtime API와 UWP 앱용 .NET API입니다. 일반적으로 "Windows" 및 .NET 형식으로 시작하는 네임스페이스의 Windows 런타임을 기반으로 하는 Windows에서 제공된 형식은 "System"으로 시작하는 네임스페이스에 있습니다. 그러나 예외가 있습니다. UWP 앱용 .NET의 형식을 사용할 때는 상호 운용성이 필요하지 않습니다. Windows 런타임을 사용하는 영역에서 성능이 저조할 경우 UWP 앱용 .NET을 사용하여 성능을 높일 수도 있습니다.
참고 Windows 10과 함께 제공되는 Windows 런타임 구성 요소의 대부분은 C++로 구현됩니다. 따라서 C# 또는 Visual Basic에서 이를 사용할 때 상호 운용성 경계를 넘습니다. 언제나 그렇듯 코드 변경에 투자하기 전에 Windows 런타임 구성 요소의 사용이 앱의 성능에 영향을 주는지 확인하기 위해 앱을 측정해 봐야 합니다.
이 항목에서 "Windows 런타임 구성 요소"는 C#이나 Visual Basic이 아닌 언어로 작성된 Windows 런타임 구성 요소를 의미합니다.
Windows 런타임 구성 요소의 속성에 액세스하거나 메서드를 호출할 때마다 상호 운용성의 부담이 발생합니다. 실제로 Windows 런타임 구성 요소를 생성할 때 .NET 개체를 생성하는 것보다 더 부담이 큽니다. Windows 런타임에서는 앱의 언어에서 구성 요소의 언어로 변환하는 코드를 실행해야 하기 때문입니다. 또한 구성 요소에 데이터를 전달하는 경우 관리되는 형식과 관리되지 않는 형식 간에 데이터를 변환해야 합니다.
Windows 런타임 구성 요소를 효율적으로 사용하기
성능을 개선할 필요가 있는 경우 코드에서 Windows 런타임 구성 요소를 최대한 효율적으로 사용하는지 확인할 수 있습니다. 이 섹션에서는 Windows 런타임 구성 요소를 사용할 때 성능을 향상시키기 위한 몇 가지 팁을 설명합니다.
성능에 미치는 영향을 확인할 수 있으려면 짧은 시간에 무수히 많은 호출을 수행해야 합니다. 비즈니스 논리 및 다른 관리 코드에서 Windows 런타임 구성 요소에 대한 호출을 캡슐화하는 잘 설계된 애플리케이션이라면 상호 운용성으로 인한 부담이 크지 않을 것입니다. 그러나 Windows 런타임 구성 요소 사용이 앱의 성능에 영향을 주고 있음이 테스트를 통해 확인된 경우 이 섹션의 팁을 참조하여 성능을 개선할 수 있습니다.
UWP 앱에 .NET에서 제공하는 형식을 사용하는 것이 좋습니다.
Windows 런타임 형식 또는 UWP 앱용 .NET에서 제공하는 형식을 사용하여 작업을 수행할 수 있는 경우가 있습니다. .NET 형식과 Windows 런타임 형식을 혼용하지 않는 것이 좋습니다. 둘 중 하나만 선택하여 계속 사용하세요. 예를 들어, xml 스트림을 구문 분석하기 위해 Windows.Data.Xml.Dom.XmlDocument 형식(Windows 런타임 형식) 또는 System.Xml.XmlReader 형식(.NET 형식) 중 하나를 사용할 수 있습니다. 스트림과 동일한 기술의 API를 사용합니다. 예를 들어, MemoryStream의 xml을 읽는 경우 System.Xml.XmlReader 형식을 사용합니다. 둘 다 .NET 형식이기 때문입니다. 파일에서 읽는 경우 파일 API와 XmlDocument는 모두 기본 Windows 런타임 구성 요소에서 구현되기 때문에 Windows.Data.Xml.Dom.XmlDocument 형식을 사용합니다.
Windows 런타임 개체를 .NET 형식에 복사하기
Windows 런타임 구성 요소가 Windows 런타임 개체를 반환할 때 그 개체를 .NET 개체로 복사하는 것이 유용할 수 있습니다. 이는 컬렉션과 스트림을 다룰 때 특히 중요합니다.
컬렉션을 반환하는 Windows 런타임 API를 호출하고 그 컬렉션을 여러 차례 저장하고 액세스하는 경우, 컬렉션을 .NET 컬렉션으로 복사한 다음, .NET 버전을 계속 사용하는 것이 효과적일 수 있습니다.
Windows 런타임 구성 요소에 대한 호출 결과를 캐시에 저장하여 나중에 사용하기
Windows 런타임 형식에 여러 차례 액세스하기보다는 로컬 변수에 값을 저장하는 방법으로 성능을 개선할 수도 있습니다. 이는 루프의 내부에서 값을 사용할 경우 특히 유용할 수 있습니다. 앱을 측정하여 로컬 변수 사용으로 앱의 성능이 개선되는지 확인합니다. 캐시 값을 사용하면 상호 운용성에 소요되는 시간이 단축되므로 앱의 속도가 향상될 수 있습니다.
Windows 런타임 구성 요소에 대한 호출 결합하기
가급적 UWP 개체에 대한 호출 횟수를 최소화하면서 작업을 완료합니다. 예를 들어 하나의 스트림에서 많은 양의 데이터를 한꺼번에 읽는 것이 한 번에 소량씩 읽는 것보다 낫습니다.
작업을 적게 수행하고 더 많은 호출이 필요한 API 대신 작업을 가능한 한 적은 호출로 번들로 묶는 API를 사용합니다. 예를 들어 기본 생성자를 호출하고 한 번에 하나씩 속성을 할당하기보다는 여러 속성을 한 번 초기화하는 생성자를 호출하여 개체를 만드는 것이 좋습니다.
Windows 런타임 구성 요소 빌드하기
C++ 또는 JavaScript로 개발되는 앱에서 사용 가능한 Windows 런타임 구성 요소를 만드는 경우, 우수한 성능을 염두에 두고 구성 요소를 설계해야 합니다. 앱에서 좋은 성능을 얻기 위한 모든 제안은 구성 요소에서 좋은 성능을 얻는 데 적용됩니다. 구성 요소를 측정하여 트래픽 패턴이 높은 API를 찾고 해당 영역에 대해 사용자가 몇 번의 호출로 작업을 수행할 수 있는 API를 제공하는 것이 좋습니다.
관리 코드에서 interop 사용 시 앱을 빠르게 유지하기
Windows 런타임은 네이티브 코드와 관리 코드 간의 상호 운용을 간편하게 하지만 주의하지 않을 경우 성능 비용이 발생할 수 있습니다. 여기서는 관리되는 UWP 앱에서 interop를 사용할 경우 우수한 성능을 얻는 방법을 보여 줍니다.
Windows 런타임을 통해 개발자는 각 언어로 사용 가능한 Windows 런타임 API의 프로젝션 덕분에 선택한 언어로 XAML을 사용하는 앱을 작성할 수 있습니다. C# 또는 Visual Basic으로 앱을 작성할 때 Windows 런타임 API는 일반적으로 네이티브 코드로 구현되기 때문에 이러한 편의성은 interop 비용이 발생하고, C# 또는 Visual Basic에서 Windows 런타임을 호출하면 관리형 스택 프레임에서 네이티브 스택 프레임으로 그리고 마샬 함수 매개 변수에서 네이티브 코드로 액세스할 수 있는 표현으로 CLR이 전환됩니다. 대부분 앱의 경우 이 오버헤드는 무시할 수 있습니다. 그러나 앱의 중요 경로에서 Windows 런타임 API에 대해 수십만 개에서 수백만 개까지 많은 호출을 생성할 경우 그 비용이 눈에 띄게 상승할 수 있습니다. 일반적으로 언어 간 전환에 소요되는 시간은 나머지 코드 실행에 비해 작아야 합니다. 이는 다음의 다이어그램에서 설명됩니다.
Windows 앱용 .NET에 나열된 유형은 C# 또는 Visual Basic에서 사용될 경우 이 interop 비용을 유발하지 않습니다. 일반적으로 “Windows.”로 시작되는 네임스페이스의 형식은 Windows에서 제공하는 Windows 런타임 API 집합의 일부이며 "System."으로 시작하는 네임스페이스의 형식은 .NET 유형이라고 가정할 수 있습니다. Windows 런타임 형식을 단순하게 사용하더라도 할당 또는 속성 액세스에 대한 interop 비용이 발생합니다.
앱을 측정하고 interop가 앱 실행 시간의 많은 부분을 차지하고 있는지 확인한 다음 interop 비용을 최적화해야 합니다. Visual Studio에서 앱의 성능을 분석할 때 Functions 보기를 사용하여 Windows 런타임을 호출하는 메서드에 소요된 포괄 시간을 확인하여 interop 비용의 상한값을 쉽게 알 수 있습니다.
interop 오버헤드로 인해 앱이 느려지면 활발한 코드 경로에서 Windows 런타임 API에 대한 호출을 줄여 성능을 개선할 수 있습니다. 예를 들어 UIElements의 위치 및 크기를 지속적으로 쿼리하여 많은 물리학 계산을 수행하고 있는 게임 엔진이 필요한 정보를 UIElements에서 로컬 변수로 저장하고 이 캐시된 값에서 계산을 수행하고 계산이 완료된 후 최종 결과를 다시 UIElements에 할당하여 많은 시간을 절약할 수 있습니다. 다른 예시: C# 또는 Visual Basic 코드에서 컬렉션에 많이 액세스하는 경우에는 Windows.Foundation.Collections 네임스페이스의 컬렉션이 아니라 System.Collections 네임스페이스의 컬렉션을 사용하는 것이 보다 효율적입니다. Windows 런타임 구성 요소에 대한 호출을 통합할 수도 있습니다. 이것이 가능한 한 가지 예시는 Windows.Storage.BulkAccess API를 사용하는 것입니다.
UWP 구성 요소 빌드하기
C++ 또는 JavaScript로 개발되는 앱에서 사용하기 위한 Windows 런타임 구성 요소를 작성하는 경우 우수한 성능을 염두에 두고 구성 요소를 설계해야 합니다. API 표면에서는 interop 경계를 정의하고 사용자가 이 항목의 지침에 대해 고려해야 하는 정도를 정의합니다. 이는 구성 요소를 다른 당사자에게 배포하려는 경우 특히 중요합니다.
앱의 성능을 높이기 위한 모든 제안은 구성 요소의 성능을 높이는 데에도 적용됩니다. 구성 요소를 측정하여 트래픽 패턴이 높은 API를 찾고 해당 영역에 대해 사용자가 몇 번의 호출로 작업을 수행할 수 있는 API를 제공하는 것이 좋습니다. Windows 런타임을 설계하는 데 상당한 노력을 기울여 interop 경계를 자주 넘지 않고도 앱에서 사용할 수 있도록 했습니다.