Partilhar via


Otimize o acesso a arquivos

Crie aplicativos da Plataforma Universal do Windows (UWP) que acessam o sistema de arquivos de forma eficiente, evitando problemas de desempenho devido à latência do disco e aos ciclos de memória/CPU.

Quando você deseja acessar uma grande coleção de arquivos e deseja acessar valores de propriedade diferentes das propriedades típicas Name, FileType e Path, acesse-os criando QueryOptions e chamando SetPropertyPrefetch. O método SetPropertyPrefetch pode melhorar drasticamente o desempenho de aplicativos que exibem uma coleção de itens obtidos do sistema de arquivos, como uma coleção de imagens. O próximo conjunto de exemplos mostra algumas maneiras de acessar vários arquivos.

O primeiro exemplo usa Windows.Storage.StorageFolder.GetFilesAsync para recuperar as informações de nome de um conjunto de arquivos. Essa abordagem fornece um desempenho bom, porque o exemplo acessa apenas a propriedade '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

O segundo exemplo usa Windows.Storage.StorageFolder.GetFilesAsync e, em seguida, recupera as propriedades da imagem para cada arquivo. Esta abordagem proporciona um desempenho fraco.

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

O terceiro exemplo usa QueryOptions para obter informações sobre um conjunto de arquivos. Essa abordagem fornece um desempenho muito melhor do que o exemplo anterior.

// 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

Se você estiver executando várias operações em objetos Windows.Storage, como Windows.Storage.ApplicationData.Current.LocalFolder, crie uma variável local para fazer referência a essa fonte de armazenamento para que você não recrie objetos intermediários cada vez que acessá-la.

Desempenho de fluxo em C# e Visual Basic

Buffering entre fluxos UWP e .NET

Há muitos cenários em que você pode querer converter um fluxo UWP (como um Windows.Storage.Streams.IInputStream ou IOutputStream) em um fluxo .NET (System.IO.Stream). Por exemplo, isso é útil quando você está escrevendo um aplicativo UWP e deseja usar o código .NET existente que funciona em fluxos com o sistema de arquivos UWP. Para habilitar isso, as APIs do .NET para aplicativos UWP fornecem métodos de extensão que permitem converter entre os tipos de fluxo .NET e UWP. Para obter mais informações, consulte WindowsRuntimeStreamExtensions.

Ao converter um fluxo UWP em um fluxo .NET, você efetivamente cria um adaptador para o fluxo UWP subjacente. Em algumas circunstâncias, há um custo de tempo de execução associado à invocação de métodos em fluxos UWP. Isso pode afetar a velocidade do seu aplicativo, especialmente em cenários em que você executa muitas operações pequenas e frequentes de leitura ou gravação.

Para acelerar os aplicativos, os adaptadores de fluxo UWP contêm um buffer de dados. O exemplo de código a seguir demonstra pequenas leituras consecutivas usando um adaptador de fluxo UWP com um tamanho de buffer padrão.

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

Esse comportamento de buffer padrão é desejável na maioria dos cenários em que você converte um fluxo UWP em um fluxo .NET. No entanto, em alguns cenários, você pode querer ajustar o comportamento de buffer para aumentar o desempenho.

Trabalhar com grandes conjuntos de dados

Ao ler ou gravar conjuntos maiores de dados, pode aumentar a sua taxa de transferência de leitura ou gravação fornecendo um tamanho de buffer grande para os métodos de extensão AsStreamForRead, AsStreamForWritee AsStream. Isso dá ao adaptador de fluxo um tamanho de buffer interno maior. Por exemplo, ao passar um fluxo que vem de um arquivo grande para um analisador XML, o analisador pode fazer muitas pequenas leituras sequenciais do fluxo. Um buffer grande pode reduzir o número de chamadas para o fluxo UWP subjacente e aumentar o desempenho.

Observação Você deve ter cuidado ao definir um tamanho de buffer maior que aproximadamente 80 KB, pois isso pode causar fragmentação na pilha do coletor de lixo (consulte Melhorar o desempenho da coleta de lixo). O exemplo de código a seguir cria um adaptador de fluxo gerenciado com um buffer de 81.920 bytes.

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

Os métodos Stream.CopyTo e CopyToAsync também alocam um buffer local para cópia entre fluxos. Tal como acontece com o método de extensão AsStreamForRead, poderá conseguir um melhor desempenho para cópias de fluxo grandes ao alterar o tamanho predefinido da memória intermédia. O exemplo de código a seguir demonstra a alteração do tamanho do buffer padrão de uma chamada de 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)

Este exemplo usa um tamanho de buffer de 1 MB, que é maior do que os 80 KB recomendados anteriormente. Usar um buffer tão grande pode melhorar a taxa de transferência da operação de cópia para conjuntos de dados muito grandes (ou seja, várias centenas de megabytes). No entanto, este buffer é alocado na pilha de objetos grandes e pode potencialmente degradar o desempenho da recolha de lixo. Você só deve usar grandes tamanhos de buffer se isso melhorar visivelmente o desempenho do seu aplicativo.

Quando você estiver trabalhando com um grande número de fluxos simultaneamente, convém reduzir ou eliminar a sobrecarga de memória do buffer. Você pode especificar um buffer menor ou definir o parâmetro bufferSize como 0 para desativar totalmente o buffer para esse adaptador de fluxo. Você ainda pode obter um bom desempenho de taxa de transferência sem buffering se executar grandes leituras e gravações no fluxo gerenciado.

Executando operações sensíveis à latência

Você também pode querer evitar o buffer se quiser leituras e gravações de baixa latência e não quiser ler em grandes blocos fora do fluxo UWP subjacente. Por exemplo, você pode querer leituras e gravações de baixa latência se estiver usando o fluxo para comunicações de rede.

Em um aplicativo de bate-papo, você pode usar um fluxo em uma interface de rede para enviar mensagens de um lado para o outro. Neste caso, você deseja enviar mensagens assim que estiverem prontas e não esperar que o buffer se enche. Se você definir o tamanho do buffer como 0 ao chamar o AsStreamForRead, AsStreamForWritee AsStream métodos de extensão, o adaptador resultante não alocará um buffer e todas as chamadas manipularão o fluxo UWP subjacente diretamente.