Bestandstoegang optimaliseren

Maak WinUI-apps met Windows App SDK die efficiënt toegang hebben tot het bestandssysteem, waardoor prestatieproblemen worden voorkomen die worden veroorzaakt door schijflatentie en onnodig geheugen- of CPU-gebruik.

Als u toegang wilt krijgen tot een grote verzameling bestanden en u andere eigenschapswaarden wilt openen dan de typische eigenschappen Name, FileType en Path, opent u deze door QueryOptions te maken en SetPropertyPrefetch aan te roepen. De methode SetPropertyPrefetch kan de prestaties van apps die een verzameling items weergeven die zijn verkregen uit het bestandssysteem, zoals een verzameling afbeeldingen, aanzienlijk verbeteren. In de volgende reeks voorbeelden ziet u een aantal manieren om toegang te krijgen tot meerdere bestanden.

In het eerste voorbeeld wordt Windows.Storage.StorageFolder.GetFilesAsync gebruikt om de naamgegevens voor een set bestanden op te halen. Deze benadering biedt goede prestaties omdat het voorbeeld alleen toegang heeft tot de naameigenschap.

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;
}

In het tweede voorbeeld wordt Windows.Storage.StorageFolder.GetFilesAsync gebruikt en worden vervolgens de afbeeldingseigenschappen voor elk bestand opgehaald. Deze aanpak biedt slechte prestaties.

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;
}

In het derde voorbeeld wordt QueryOptions gebruikt om informatie over een set bestanden op te halen. Deze benadering biedt veel betere prestaties dan in het vorige voorbeeld.

// 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"];
}

Als u meerdere bewerkingen uitvoert op Windows.Storage-objecten zoals Windows.Storage.ApplicationData.Current.LocalFolder, maakt u een lokale variabele om naar die opslagbron te verwijzen, zodat u deze niet telkens opnieuw hoeft aan te maken.

Stream-prestaties in C#

Bufferen tussen Windows Runtime- en .NET-streams

Er zijn veel scenario's waarin u een Windows Runtime-stream (zoals een Windows.Storage.Streams.IInputStream of IOutputStream) wilt converteren naar een .NET-stream (System.IO.Stream). Dit is bijvoorbeeld handig wanneer u een WinUI-app schrijft en bestaande .NET-code wilt gebruiken die werkt op streams met de Windows Storage-API's. Om dit in te schakelen, biedt .NET extensiemethoden waarmee u kunt converteren tussen .NET- en Windows Runtime-stroomtypen. Zie WindowsRuntimeStreamExtensions voor meer informatie.

Wanneer u een Windows Runtime-stream converteert naar een .NET-stream, maakt u effectief een adapter voor de onderliggende Windows Runtime-stream. Onder bepaalde omstandigheden zijn er runtimekosten verbonden aan het aanroepen van methoden in Windows Runtime-streams. Dit kan van invloed zijn op de snelheid van uw app, met name in scenario's waarin u veel kleine, frequente lees- of schrijfbewerkingen uitvoert.

Om apps sneller te maken, bevatten Windows Runtime-stroomadapters een gegevensbuffer. In het volgende codevoorbeeld ziet u kleine opeenvolgende leesbewerkingen met behulp van een Windows Runtime-stroomadapter met een standaardbuffergrootte.

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);
}

Dit standaardbuffergedrag is wenselijk in de meeste scenario's waarin u een Windows Runtime-stream converteert naar een .NET-stream. In sommige scenario's kunt u echter het buffergedrag aanpassen om de prestaties te verbeteren.

Werken met grote gegevenssets

Wanneer u grotere gegevenssets leest of schrijft, kunt u mogelijk de lees- of schrijfdoorvoer verhogen door een grote buffer aan te bieden voor de asStreamForRead-, AsStreamForWrite- en AsStream-extensiemethoden . Hierdoor krijgt de streamadapter een grotere interne buffergrootte. Wanneer u bijvoorbeeld een stroom doorgeeft die afkomstig is van een groot bestand naar een XML-parser, kan de parser veel opeenvolgende kleine leesbewerkingen van de stream maken. Een grote buffer kan het aantal aanroepen naar de onderliggende Windows Runtime-stroom verminderen en de prestaties verbeteren.

Opmerking

Wees voorzichtig bij het instellen van een buffergrootte die groter is dan ongeveer 80 kB, omdat dit fragmentatie in de geheugenheap van de garbage collector kan veroorzaken. Zie de prestaties van het afvalbeheer verbeteren in WinUI-apps voor gerelateerde richtlijnen. In het volgende codevoorbeeld wordt een beheerde streamadapter gemaakt met een buffer van 81.920 byte.

// Create a stream adapter with an 80 KB buffer.
Stream managedStream = nativeStream.AsStreamForRead(bufferSize: 81920);

De methoden Stream.CopyTo en CopyToAsync wijzen ook een lokale buffer toe voor het kopiëren tussen streams. Net als bij de asStreamForRead-extensiemethode kunt u mogelijk betere prestaties krijgen voor grote streamkopieën door de standaardbuffergrootte te overschrijven. In het volgende codevoorbeeld ziet u hoe u de standaardbuffergrootte van een CopyToAsync-aanroep wijzigt.

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

In dit voorbeeld wordt een buffergrootte van 1 MB gebruikt, die groter is dan de 80 kB die eerder is aanbevolen. Door een dergelijke grote buffer te gebruiken, kan de doorvoer van de kopieerbewerking voor zeer grote gegevenssets (dat wil gezegd enkele honderden megabytes) verbeteren. Deze buffer wordt echter toegewezen aan de grote object-heap en kan de prestaties van de garbagecollection mogelijk verminderen. U moet alleen grote buffergrootten gebruiken als ze de prestaties van uw app aanzienlijk verbeteren.

Wanneer u tegelijkertijd met een groot aantal streams werkt, kunt u de geheugenoverhead van de buffer verminderen of elimineren. U kunt een kleinere buffer opgeven of de parameter bufferSize instellen op 0 om buffering volledig voor die stroomadapter uit te schakelen. U kunt nog steeds goede doorvoerprestaties bereiken zonder buffering als u grote lees- en schrijfbewerkingen naar de beheerde stream uitvoert.

Latentiegevoelige bewerkingen uitvoeren

U kunt ook buffering voorkomen als u lees- en schrijfbewerkingen met lage latentie wilt en niet wilt lezen in grote blokken uit de onderliggende Windows Runtime-stream. U wilt bijvoorbeeld lees- en schrijfbewerkingen met lage latentie als u de stream voor netwerkcommunicatie gebruikt.

In een chat-app kunt u een stream via een netwerkinterface gebruiken om berichten heen en weer te verzenden. In dit geval wilt u berichten verzenden zodra ze klaar zijn en niet wachten totdat de buffer vol is. Als u de buffergrootte instelt op 0 bij het aanroepen van de AsStreamForRead-, AsStreamForWrite- en AsStream-extensiemethoden , wijst de resulterende adapter geen buffer toe en worden alle aanroepen de onderliggende Windows Runtime-stroom rechtstreeks bewerkt.