디스크 대기 시간 및 불필요한 메모리 또는 CPU 사용으로 인한 성능 문제를 방지하여 파일 시스템에 효율적으로 액세스하는 Windows 앱 SDK를 사용하여 WinUI 앱을 만듭니다.
대용량 파일 컬렉션에 액세스하고 일반적인 Name, FileType 및 Path 속성 이외의 속성 값에 액세스하려는 경우 QueryOptions를 만들고 SetPropertyPrefetch를 호출하여 액세스합니다. SetPropertyPrefetch 메서드는 이미지 컬렉션과 같이 파일 시스템에서 가져온 항목 컬렉션을 표시하는 앱의 성능을 크게 향상시킬 수 있습니다. 다음 예제 집합에서는 여러 파일에 액세스하는 몇 가지 방법을 보여 줍니다.
첫 번째 예제에서는 Windows.Storage.StorageFolder.GetFilesAsync 를 사용하여 파일 집합의 이름 정보를 검색합니다. 이 방법은 예제가 이름 속성에만 액세스하기 때문에 좋은 성능을 제공합니다.
StorageFolder library = Windows.Storage.KnownFolders.PicturesLibrary;
IReadOnlyList<StorageFile> files = await library.GetFilesAsync(Windows.Storage.Search.CommonFileQuery.OrderByDate);
for (int i = 0; i < files.Count; i++)
{
// Do something with the name of each file.
string fileName = files[i].Name;
}
두 번째 예제에서는 Windows.Storage.StorageFolder.GetFilesAsync 를 사용한 다음 각 파일에 대한 이미지 속성을 검색합니다. 이 방법은 성능 저하를 제공합니다.
StorageFolder library = Windows.Storage.KnownFolders.PicturesLibrary;
IReadOnlyList<StorageFile> files = await library.GetFilesAsync(Windows.Storage.Search.CommonFileQuery.OrderByDate);
for (int i = 0; i < files.Count; i++)
{
ImageProperties imgProps = await files[i].Properties.GetImagePropertiesAsync();
// Do something with the date the image was taken.
DateTimeOffset date = imgProps.DateTaken;
}
세 번째 예제에서는 QueryOptions를 사용하여 파일 집합에 대한 정보를 가져옵니다. 이 방법은 이전 예제보다 훨씬 더 나은 성능을 제공합니다.
// Set QueryOptions to prefetch our specific properties.
var queryOptions = new Windows.Storage.Search.QueryOptions(CommonFileQuery.OrderByDate, null);
queryOptions.SetThumbnailPrefetch(
ThumbnailMode.PicturesView,
100,
ThumbnailOptions.ReturnOnlyIfCached);
queryOptions.SetPropertyPrefetch(
PropertyPrefetchOptions.ImageProperties,
new string[] { "System.Size" });
StorageFileQueryResult queryResults = KnownFolders.PicturesLibrary.CreateFileQueryWithOptions(queryOptions);
IReadOnlyList<StorageFile> files = await queryResults.GetFilesAsync();
foreach (var file in files)
{
ImageProperties imageProperties = await file.Properties.GetImagePropertiesAsync();
// Do something with the date the image was taken.
DateTimeOffset dateTaken = imageProperties.DateTaken;
// Performance gains increase with the number of properties that are accessed.
IDictionary<string, object> propertyResults =
await file.Properties.RetrievePropertiesAsync(new string[] { "System.Size" });
// Get or set extra properties here.
var systemSize = propertyResults["System.Size"];
}
Windows.Storage Windows.Storage.ApplicationData.Current.LocalFolder개체에서 여러 작업을 수행하는 경우 액세스할 때마다 중간 개체를 다시 만들지 않도록 해당 스토리지 원본을 참조하는 로컬 변수를 만듭니다.
C의 스트림 성능#
Windows 런타임과 .NET 스트림 간의 버퍼링
Windows 런타임 스트림(예: Windows.Storage.Streams.IInputStream 또는 IOutputStream)을 .NET 스트림(System.IO.Stream)으로 변환하려는 많은 시나리오가 있습니다. 예를 들어 WinUI 앱을 작성하고 Windows Storage API를 사용하여 스트림에서 작동하는 기존 .NET 코드를 사용하려는 경우에 유용합니다. 이를 사용하도록 설정하기 위해 .NET은 .NET과 Windows 런타임 스트림 형식 간에 변환할 수 있는 확장 메서드를 제공합니다. 자세한 내용은 WindowsRuntimeStreamExtensions를 참조하세요.
Windows 런타임 스트림을 .NET 스트림으로 변환하면 기본 Windows 런타임 스트림에 대한 어댑터를 효과적으로 만듭니다. 경우에 따라 Windows 런타임 스트림에서 메서드를 호출하는 것과 관련된 런타임 비용이 발생합니다. 이는 특히 작고 빈번한 읽기 또는 쓰기 작업을 많이 수행하는 시나리오에서 앱의 속도에 영향을 줄 수 있습니다.
앱 속도를 높이기 위해 Windows 런타임 스트림 어댑터에는 데이터 버퍼가 포함되어 있습니다. 다음 코드 샘플에서는 기본 버퍼 크기의 Windows 런타임 스트림 어댑터를 사용하여 작은 연속 읽기를 보여 줍니다.
StorageFile file = await Windows.Storage.ApplicationData.Current
.LocalFolder.GetFileAsync("example.txt");
Windows.Storage.Streams.IInputStream windowsRuntimeStream =
await file.OpenReadAsync();
byte[] destinationArray = new byte[8];
// Create an adapter with the default buffer size.
using (var managedStream = windowsRuntimeStream.AsStreamForRead())
{
// Read 8 bytes into destinationArray.
// A larger block is actually read from the underlying
// windowsRuntimeStream and buffered within the adapter.
await managedStream.ReadAsync(destinationArray, 0, 8);
// Read 8 more bytes into destinationArray.
// This call may complete much faster than the first call
// because the data is buffered and no call to the
// underlying windowsRuntimeStream needs to be made.
await managedStream.ReadAsync(destinationArray, 0, 8);
}
이 기본 버퍼링 동작은 Windows 런타임 스트림을 .NET 스트림으로 변환하는 대부분의 시나리오에서 바람직합니다. 그러나 일부 시나리오에서는 성능을 높이기 위해 버퍼링 동작을 조정하는 것이 좋습니다.
큰 데이터 집합 작업
더 큰 데이터 집합을 읽거나 쓸 때 AsStreamForRead, AsStreamForWrite 및 AsStream 확장 메서드에 큰 버퍼 크기를 제공하여 읽기 또는 쓰기 처리량을 늘릴 수 있습니다. 이렇게 하면 스트림 어댑터의 내부 버퍼 크기가 커지게 됩니다. 예를 들어 큰 파일에서 XML 파서로 오는 스트림을 전달할 때 파서는 스트림에서 많은 순차적 작은 읽기를 만들 수 있습니다. 큰 버퍼는 기본 Windows 런타임 스트림에 대한 호출 수를 줄이고 성능을 향상시킬 수 있습니다.
메모
약 80KB보다 큰 버퍼 크기를 설정할 때는 가비지 수집기 힙에서 조각화가 발생할 수 있으므로 주의해야 합니다. 관련 지침은 WinUI 앱에서 가비지 수집 성능 향상을 참조하세요. 다음 코드 예제에서는 81,920 바이트 버퍼를 사용하여 관리되는 스트림 어댑터를 만듭니다.
// Create a stream adapter with an 80 KB buffer.
Stream managedStream = nativeStream.AsStreamForRead(bufferSize: 81920);
Stream.CopyTo 및 CopyToAsync 메서드는 스트림 간에 복사하기 위한 로컬 버퍼도 할당합니다. AsStreamForRead 확장 메서드와 마찬가지로 기본 버퍼 크기를 재정의하여 큰 스트림 복사본의 성능을 높일 수 있습니다. 다음 코드 예제에서는 CopyToAsync 호출의 기본 버퍼 크기를 변경하는 방법을 보여 줍니다.
MemoryStream destination = new MemoryStream();
// Copies the buffer into memory using the default copy buffer.
await managedStream.CopyToAsync(destination);
// Copy the buffer into memory using a 1 MB copy buffer.
await managedStream.CopyToAsync(destination, bufferSize: 1024 * 1024);
이 예제에서는 이전에 권장한 80KB보다 큰 1MB의 버퍼 크기를 사용합니다. 이러한 큰 버퍼를 사용하면 매우 큰 데이터 집합(즉, 수백 메가바이트)에 대한 복사 작업의 처리량을 향상시킬 수 있습니다. 그러나 이 버퍼는 대형 개체 힙에 할당되므로 가비지 수집 성능이 저하될 수 있습니다. 큰 버퍼 크기는 앱의 성능을 눈에 띄게 향상시키는 경우에만 사용해야 합니다.
많은 수의 스트림을 동시에 사용하는 경우 버퍼의 메모리 오버헤드를 줄이거나 제거할 수 있습니다. 더 작은 버퍼를 지정하거나 bufferSize 매개 변수를 0으로 설정하여 해당 스트림 어댑터에 대한 버퍼링을 완전히 해제할 수 있습니다. 관리되는 스트림에 대한 큰 읽기 및 쓰기를 수행하는 경우 버퍼링하지 않고도 처리량 성능을 향상할 수 있습니다.
지연에 민감한 작업 수행
대기 시간이 짧은 읽기 및 쓰기를 원하고 기본 Windows 런타임 스트림의 큰 블록에서 읽지 않으려는 경우에도 버퍼링을 방지할 수 있습니다. 예를 들어 네트워크 통신에 스트림을 사용하는 경우 짧은 대기 시간 읽기 및 쓰기를 원할 수 있습니다.
채팅 앱에서는 네트워크 인터페이스를 통해 스트림을 사용하여 메시지를 앞뒤로 보낼 수 있습니다. 이 경우 메시지가 준비되는 즉시 메시지를 보내고 버퍼가 채워지도록 기다리지 않으려는 경우 AsStreamForRead, AsStreamForWrite 및 AsStream 확장 메서드를 호출할 때 버퍼 크기를 0으로 설정하면 결과 어댑터가 버퍼를 할당하지 않으며 모든 호출이 기본 Windows 런타임 스트림을 직접 조작합니다.
Windows developer