Dot.Blog

Consulting DotNet C#, XAML, WinUI, WPF, MAUI, IA

Memory<T> et MemoryPool<T> : manipulation efficace de buffers en .NET

Les objets Memory<T> et MemoryPool<T> permettent de manipuler les buffers de données de façon très efficace. Encore trop peu connus et utilisés, faisons le point !

🎯 Objectif

Comprendre en quoi Memory<T> et MemoryPool<T> complètent Span<T>, et comment ils permettent de gérer efficacement la mémoire en .NET dans les cas suivants :

  • buffers réutilisables ou longs à vivre
  • manipulation asynchrone de données (I/O, réseau…)
  • allocations contrôlées ou à faible empreinte mémoire

📍 Rappel : pourquoi Span<T> ne suffit pas toujours ?

Span<T> permet de manipuler une portion contiguë de mémoire de manière sûre et sans allocation. Il est ultra performant, mais :

  • il ne peut vivre que sur la stack
  • il ne peut pas être capturé dans une closure ou stocké dans une variable asynchrone

Dès qu’on sort du scope immédiat, on a besoin d’autre chose : Memory<T>.

🔍 Memory<T> : la version heap-safe de Span<T>

Définition

Memory<T> représente un bloc de mémoire comme Span<T>, mais stockable, transmissible et compatible async.

Exemple simple

Memory<byte> buffer = new byte[1024];
DoSomethingAsync(buffer);

async Task DoSomethingAsync(Memory<byte> data)
{
    await Task.Delay(10); // Autorisé : Memory<T> peut être capturé ici
    var span = data.Span;
    span[0] = 42;
}

🔧 Memory<T>.Span permet d’obtenir un Span<T> temporaire pour la manipulation directe.

🏊‍♂️ MemoryPool<T> : réutiliser les buffers sans allouer à chaque fois

Chaque fois que vous écrivez :

var buffer = new byte[4096];

… vous allouez sur le tas. Si vous le faites souvent (par ex. traitement réseau ou fichiers), cela génère beaucoup de garbage.

MemoryPool<T> fournit une stratégie de réutilisation de blocs de mémoire.

Exemple d’utilisation de MemoryPool<T>

var pool = MemoryPool<byte>.Shared;

using var owner = pool.Rent(4096);

Memory<byte> buffer = owner.Memory;

await DoSomethingAsync(buffer); // Transmet un buffer loué
// Pas besoin de libérer : l'objet est retourné au pool à la fin du `using`

Exemple concret : lecture d’un flux réseau ou fichier

async Task ReadStreamAsync(Stream source)
{
    var pool = MemoryPool<byte>.Shared;

    using var bufferOwner = pool.Rent(4096);
    var buffer = bufferOwner.Memory;

    int bytesRead = await source.ReadAsync(buffer);
    Console.WriteLine($"Lu {bytesRead} octets");
}

Aucun garbage produit, buffer réutilisable, et compatible async.

⚖️ Comparatif rapide

Type

Vivant sur

Capturable async ?

Allocation

Idéal pour

Span<T>

Stack

❌ Non

Non

Traitement immédiat

Memory<T>

Heap

✅ Oui

Oui

Données longues à vivre

MemoryPool<T>

Heap (réutilisé)

✅ Oui

Faible

I/O performants, buffers récurrents

🛠️ Astuces et bonnes pratiques

  • Utilisez Span<T> dans les fonctions internes non asynchrones.
  • Passez à Memory<T> si vos données doivent traverser un await, une closure, ou être conservées.
  • Pour des buffers récurrents, adoptez systématiquement MemoryPool<T>.

🔁 Pour aller plus loin

  • IMemoryOwner<T> vous permet de rendre vos API plus flexibles (possibilité de libérer le buffer ou non)
  • ReadOnlyMemory<T> est la version immutable de Memory<T> (comme ReadOnlySpan<T>)
  • Les nouvelles API .NET (par ex. Stream.ReadAsync) acceptent Memory<T> en standard depuis .NET Core 2.1+

✅ Conclusion

Memory<T> et MemoryPool<T> sont les piliers du traitement efficace de données en .NET moderne. Ils permettent d’écrire du code rapide, sécurisé, compatible async, sans sacrifier la performance mémoire.

Comme Span<T> ces outils d'accès à la mémoire nous éloignent un peu du côté rassurant d'un langage "managé" avec garbage collector pour nous entraîner sur le territoire marécageux de C et C++, des pointeurs, des buffers et autres techniques qu'on croyait d'un autre temps... Mais en réalité ces structures de données sont toujours vivantes et utiles ! C# réussit (grâce aux évolutions de ..NET Core) à rendre leur utilisation naturelle en les intégrant à sa logique propre. Je ne vous dis pas qu'il faut truffer vos Apps désormais de ces techniques d'accès à la mémoire, mais je vous dis, oui elles sont utiles et vivantes et aujourd'hui elles deviennent utilisables dans le contexte d'un langage managé, c'est une arme à garder sous le coude pour certains cas où cela s'avère un avantage indiscutable.

Stay Tuned !

Faites des heureux, PARTAGEZ l'article !