Оптимизация доступа к файлам

Создавайте приложения универсальная платформа Windows (UWP), которые эффективно обращаются к файловой системе, избегая проблем с производительностью из-за задержки диска и циклов ЦП.

Если вы хотите получить доступ к большой коллекции файлов и получить доступ к значениям свойств, отличных от стандартных свойств Name, FileType и Path, к ним можно получить доступ, создав QueryOptions и вызвав SetPropertyPrefetch. Метод SetPropertyPrefetch может значительно повысить производительность приложений, отображающих коллекцию элементов, полученных из файловой системы, например коллекцию изображений. В следующем наборе примеров показано несколько способов доступа к нескольким файлам.

В первом примере используется Windows.служба хранилища. служба хранилища Folder.GetFilesAsync для получения сведений об имени набора файлов. Такой подход обеспечивает хорошую производительность, так как пример обращается только к свойству name.

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;
}
Dim library As StorageFolder = Windows.Storage.KnownFolders.PicturesLibrary
Dim files As IReadOnlyList(Of StorageFile) =
    Await library.GetFilesAsync(Windows.Storage.Search.CommonFileQuery.OrderByDate)

For i As Integer = 0 To files.Count - 1
    ' do something with the name of each file
    Dim fileName As String = files(i).Name
Next i

Второй пример использует Windows.служба хранилища. служба хранилища Folder.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;
}
Dim library As StorageFolder = Windows.Storage.KnownFolders.PicturesLibrary
Dim files As IReadOnlyList(Of StorageFile) = Await library.GetFilesAsync(Windows.Storage.Search.CommonFileQuery.OrderByDate)
For i As Integer = 0 To files.Count - 1
    Dim imgProps As ImageProperties =
        Await files(i).Properties.GetImagePropertiesAsync()

    ' do something with the date the image was taken
    Dim dateTaken As DateTimeOffset = imgProps.DateTaken
Next i

Третий пример использует 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/Set extra properties here
    var systemSize = propertyResults["System.Size"];
}
' Set QueryOptions to prefetch our specific properties
Dim queryOptions = New Windows.Storage.Search.QueryOptions(CommonFileQuery.OrderByDate, Nothing)
queryOptions.SetThumbnailPrefetch(ThumbnailMode.PicturesView,
            100, Windows.Storage.FileProperties.ThumbnailOptions.ReturnOnlyIfCached)
queryOptions.SetPropertyPrefetch(PropertyPrefetchOptions.ImageProperties,
                                 New String() {"System.Size"})

Dim queryResults As StorageFileQueryResult = KnownFolders.PicturesLibrary.CreateFileQueryWithOptions(queryOptions)
Dim files As IReadOnlyList(Of StorageFile) = Await queryResults.GetFilesAsync()


For Each file In files
    Dim imageProperties As ImageProperties = Await file.Properties.GetImagePropertiesAsync()

    ' Do something with the date the image was taken.
    Dim dateTaken As DateTimeOffset = imageProperties.DateTaken

    ' Performance gains increase with the number of properties that are accessed.
    Dim propertyResults As IDictionary(Of String, Object) =
        Await file.Properties.RetrievePropertiesAsync(New String() {"System.Size"})

    ' Get/Set extra properties here
    Dim systemSize = propertyResults("System.Size")

Next file

Если вы выполняете несколько операций в Windows. служба хранилища таких объектов, как Windows.Storage.ApplicationData.Current.LocalFolderсоздание локальной переменной для ссылки на источник хранилища, чтобы не создавать промежуточные объекты при каждом доступе к нему.

Потоковая производительность в C# и Visual Basic

Буферизация между потоками UWP и .NET

Существует множество сценариев, когда может потребоваться преобразовать поток UWP (например, Windows.служба хранилища. Потоки. IInputStream или IOutputStream) в поток .NET (System.IO.Stream). Например, это полезно при написании приложения UWP и использования существующего кода .NET, работающего на потоках с файловой системой UWP. Чтобы это осуществить, API .NET для приложений UWP предоставляют методы расширения, которые позволяют вам преобразовывать потоки типа .NET в потоки типа UWP и наоборот. Дополнительные сведения см. в разделе WindowsRuntimeStreamExtensions.

При преобразовании потока UWP в поток .NET вы фактически создаете адаптер для базового потока UWP. В некоторых случаях существует стоимость среды выполнения, связанная с вызовом методов в потоках UWP. Это может повлиять на скорость приложения, особенно в сценариях, где выполняется множество небольших, частых операций чтения или записи.

Чтобы ускорить приложения, адаптеры потоков UWP содержат буфер данных. В следующем примере кода показаны небольшие последовательные операции чтения с помощью адаптера потока UWP с размером буфера по умолчанию.

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);
}
Dim file As StorageFile = Await Windows.Storage.ApplicationData.Current -
.LocalFolder.GetFileAsync("example.txt")
Dim windowsRuntimeStream As Windows.Storage.Streams.IInputStream =
    Await file.OpenReadAsync()

Dim destinationArray() As Byte = New Byte(8) {}

' Create an adapter with the default buffer size.
Dim managedStream As Stream = windowsRuntimeStream.AsStreamForRead()
Using (managedStream)

    ' 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)

End Using

Это поведение буферизации по умолчанию желательно в большинстве сценариев, когда вы преобразуете поток UWP в поток .NET. Однако в некоторых сценариях может потребоваться настроить поведение буферизации, чтобы повысить производительность.

Работа с большими наборами данных

При чтении или записи больших наборов данных можно увеличить пропускную способность чтения или записи, предоставив большой размер буфера методам расширения AsStreamForRead, AsStreamForWrite и AsStream. Это дает адаптеру потока больший размер внутреннего буфера. Например, при передаче потока, который поступает из большого файла в средство синтаксического анализа XML, средство синтаксического анализа может сделать много последовательных небольших операций чтения из потока. Большой буфер может уменьшить количество вызовов базового потока UWP и повысить производительность.

Примечание. Нужно быть осторожными при установке размера буфера выше приблизительно 80 КБ, т. к. это может привести к фрагментации кучи сборщика мусора (см. раздел Улучшение производительности сборки мусора). В следующем примере кода создается управляемый адаптер потока с буфером 81 920 байтов.

// Create a stream adapter with an 80 KB buffer.
Stream managedStream = nativeStream.AsStreamForRead(bufferSize: 81920);
' Create a stream adapter with an 80 KB buffer.
Dim managedStream As Stream = 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);
Dim destination As MemoryStream = 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)

В этом примере используется размер буфера размером 1 МБ, который превышает 80 КБ ранее рекомендуемых. Использование такого большого буфера может повысить пропускную способность операции копирования для очень больших наборов данных (т. е. несколько сотен мегабайт). Однако этот буфер выделяется в куче больших объектов и может привести к снижению производительности сборки мусора. При значительном повышении производительности приложения следует использовать только большие размеры буферов.

При одновременной работе с большим количеством потоков может потребоваться уменьшить или исключить затраты на память буфера. Можно указать меньший буфер или задать параметр bufferSize равным 0, чтобы полностью отключить буферизацию для этого адаптера потока. Вы по-прежнему можете обеспечить высокую производительность пропускной способности без буферизации при выполнении больших операций чтения и записи в управляемый поток.

Выполнение операций с учетом задержки

Кроме того, можно избежать буферизации, если требуется низкая задержка чтения и записи, а также не требуется читать в больших блоках из базового потока UWP. Например, при использовании потока для сетевого обмена данными может потребоваться низкая задержка чтения и записи.

В приложении чата можно использовать поток через сетевой интерфейс для отправки сообщений обратно. В этом случае вы хотите отправить сообщения сразу после их готовности и не дождитесь заполнения буфера. Если при вызове методов расширения AsStreamForRead, AsStreamForWrite и AsStream задано значение 0, то результирующий адаптер не выделяет буфер, и все вызовы будут управлять базовым потоком UWP напрямую.