Partager via


Optimiser l’accès aux fichiers

Créez des applications WinUI avec le Kit de développement logiciel (SDK) d’application Windows qui accèdent efficacement au système de fichiers, ce qui évite les problèmes de performances causés par la latence du disque et l’utilisation inutile de la mémoire ou du processeur.

Lorsque vous souhaitez accéder à une grande collection de fichiers et que vous souhaitez accéder aux valeurs de propriété autres que les propriétés Name, FileType et Path classiques, accédez-y en créant QueryOptions et en appelant SetPropertyPrefetch. La méthode SetPropertyPrefetch peut améliorer considérablement les performances des applications qui affichent une collection d’éléments obtenus à partir du système de fichiers, comme une collection d’images. L’ensemble d’exemples suivant montre quelques façons d’accéder à plusieurs fichiers.

Le premier exemple utilise Windows.Storage.StorageFolder.GetFilesAsync pour récupérer les informations de nom d’un ensemble de fichiers. Cette approche offre de bonnes performances, car l’exemple accède uniquement à la propriété "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;
}

Le deuxième exemple utilise Windows.Storage.StorageFolder.GetFilesAsync , puis récupère les propriétés d’image pour chaque fichier. Cette approche offre des performances médiocres.

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

Le troisième exemple utilise QueryOptions pour obtenir des informations sur un ensemble de fichiers. Cette approche offre de meilleures performances que l’exemple précédent.

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

Si vous effectuez plusieurs opérations sur des objets Windows.Storage tels que Windows.Storage.ApplicationData.Current.LocalFolder, créez une variable locale pour référencer cette source de stockage afin de ne pas recréer d’objets intermédiaires chaque fois que vous y accédez.

Performances des flux en C#

Mise en mémoire tampon entre les flux Windows Runtime et .NET

Il existe de nombreux scénarios lorsque vous souhaiterez peut-être convertir un flux Windows Runtime (par exemple , Windows.Storage.Streams.IInputStream ou IOutputStream) en un flux .NET (System.IO.Stream). Par exemple, cela est utile lorsque vous écrivez une application WinUI et que vous souhaitez utiliser du code .NET existant qui fonctionne sur des flux avec les API de stockage Windows. Pour l’activer, .NET fournit des méthodes d’extension qui vous permettent de convertir entre les types de flux .NET et Windows Runtime. Pour plus d’informations, consultez WindowsRuntimeStreamExtensions.

Lorsque vous convertissez un flux Windows Runtime en flux .NET, vous créez efficacement un adaptateur pour le flux Windows Runtime sous-jacent. Dans certaines circonstances, il existe un coût d’exécution associé à l’appel de méthodes sur les flux Windows Runtime. Cela peut affecter la vitesse de votre application, en particulier dans les scénarios où vous effectuez de nombreuses petites opérations de lecture ou d'écriture fréquentes.

Pour accélérer les applications, les adaptateurs de flux Windows Runtime contiennent une mémoire tampon de données. L’exemple de code suivant illustre de petites lectures consécutives à l’aide d’un adaptateur de flux Windows Runtime avec une taille de mémoire tampon par défaut.

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

Ce comportement de mise en mémoire tampon par défaut est souhaitable dans la plupart des scénarios où vous convertissez un flux Windows Runtime en flux .NET. Toutefois, dans certains scénarios, vous pouvez modifier le comportement de mise en mémoire tampon afin d’augmenter les performances.

Utilisation de jeux de données volumineux

Lors de la lecture ou de l’écriture de jeux de données plus volumineux, vous pouvez augmenter votre débit de lecture ou d’écriture en fournissant une grande taille de mémoire tampon aux méthodes d’extension AsStreamForRead, AsStreamForWrite et AsStream . Cela donne à l’adaptateur de flux une plus grande taille de mémoire tampon interne. Par exemple, lors du passage d’un flux provenant d’un fichier volumineux à un analyseur XML, l’analyseur peut effectuer de nombreuses petites lectures séquentielles à partir du flux. Une mémoire tampon volumineuse peut réduire le nombre d’appels au flux Windows Runtime sous-jacent et améliorer les performances.

Note

Veillez à définir une taille de mémoire tampon plus grande que environ 80 Ko, car cela risque d'entraîner une fragmentation sur le tas du ramasse-miettes. Pour obtenir des conseils connexes, consultez Améliorer les performances du garbage collection dans les applications WinUI. L’exemple de code suivant crée un adaptateur de flux managé avec une mémoire tampon de 81 920 octets.

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

Les méthodes Stream.CopyTo et CopyToAsync allouent également une mémoire tampon locale pour la copie entre les flux. Comme avec la méthode d’extension AsStreamForRead , vous pouvez obtenir de meilleures performances pour les copies de flux volumineuses en remplaçant la taille de mémoire tampon par défaut. L’exemple de code suivant illustre la modification de la taille de mémoire tampon par défaut d’un appel 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);

Cet exemple utilise une taille de mémoire tampon de 1 Mo, supérieure à la taille de 80 Ko précédemment recommandée. L’utilisation d’une telle mémoire tampon volumineuse peut améliorer le débit de l’opération de copie pour des jeux de données très volumineux (c’est-à-dire plusieurs centaines de mégaoctets). Toutefois, cette mémoire tampon est allouée sur le tas d’objets volumineux et peut dégrader potentiellement les performances du ramasse-miettes. Vous devez utiliser des tailles de mémoire tampon volumineuses uniquement si elles améliorent sensiblement les performances de votre application.

Lorsque vous travaillez simultanément avec un grand nombre de flux, vous pouvez réduire ou éliminer la surcharge mémoire de la mémoire tampon. Vous pouvez spécifier une mémoire tampon plus petite ou définir le paramètre bufferSize sur 0 pour désactiver entièrement la mise en mémoire tampon pour cet adaptateur de flux. Vous pouvez toujours obtenir de bonnes performances de débit sans mettre en mémoire tampon si vous effectuez des lectures et des écritures volumineuses dans le flux managé.

Exécution d’opérations sensibles à la latence

Vous pouvez également éviter la mise en mémoire tampon si vous souhaitez des lectures et des écritures à faible latence et ne souhaitez pas lire dans de grands blocs hors du flux Windows Runtime sous-jacent. Par exemple, vous pouvez souhaiter des lectures et des écritures à faible latence si vous utilisez le flux pour les communications réseau.

Dans une application de conversation, vous pouvez utiliser un flux sur une interface réseau pour envoyer des messages. Dans ce cas, vous souhaitez envoyer des messages dès qu’ils sont prêts et n’attendez pas que la mémoire tampon se remplisse. Si vous définissez la taille de la mémoire tampon sur 0 lors de l’appel des méthodes d’extension AsStreamForRead, AsStreamForWrite et AsStream , l’adaptateur résultant n’alloue pas de mémoire tampon, et tous les appels manipulent directement le flux Windows Runtime sous-jacent.