Performanță Extremă cu Span & Memory în C# în C# (.NET)

Memorie și Performanță Avansată C# 7.2 / .NET Core 2.1+ / .NET 8

Manipularea fragmentelor de șiruri de caractere și tablouri de memorie contiguă cu alocări zero pe Heap (Zero-Allocation Programming).

Sintaxă C#
Span<T>, ReadOnlySpan<char> & Memory<T>

În programarea tradițională, parsarea unui fișier mare CSV sau JSON folosind metode precum `string.Substring()` sau `string.Split()` genera zeci de mii de obiecte mici de tip string în memoria Heap, declanșând cicluri frecvente și costisitoare ale colectorului de gunoi (Garbage Collector).

Tipurile revoluționare Span<T> și ReadOnlySpan<T> reprezintă o ‘fereastră’ (view) directă peste o regiune de memorie contiguă — indiferent dacă acea memorie se află pe Stivă (Stack), pe Heap sau în memorie neadministrată nativă.

Operația de decupare (slicing) a unui `ReadOnlySpan` nu copiază datele și nu alocă niciun octet nou de memorie, ci doar reține un pointer și o lungime. Această inovație este fundamentul pe care a fost rescris întregul framework ASP.NET Core, transformându-l într-unul dintre cele mai rapide servere web din lume.

Sintaxă, opțiuni și concepte cheie

Opțiune / Concept Descriere practică
Span Tip ref struct alocat exclusiv pe stivă ce oferă acces de citire și scriere la memorie contiguă.
ReadOnlySpan Înlocuitorul modern de înaltă performanță pentru operațiile de decupare și căutare în string-uri.
Memory Alternativa sigură la Span care poate fi stocată în câmpuri de clase și utilizată în metode asincrone (`async/await`).
Zero Garbage Collection Pressure Beneficiul eliminării totale a alocărilor pe Heap pe durata parsării masive de date.

Exemple practice și cod

Exemplul 1: Parsarea unei linii de log fără nicio alocare de memorie pe Heap

Cum se extrag datele folosind ReadOnlySpan în loc de Substring.

// Linie de intrare dintr-un fișier de log:
string logLine = "2026-10-24 14:30:15 [ERROR] Conexiune baza de date refuzata";

// Transformăm stringul într-un span fără alocare:
ReadOnlySpan span = logLine.AsSpan();

// Extragem componentele exclusiv prin slicing (pointer + length, zero alocări):
ReadOnlySpan dataOra = span.Slice(0, 19);
ReadOnlySpan nivel = span.Slice(21, 5);

// Comparare directă fără alocare de string nou:
if (nivel.SequenceEqual("ERROR"))
{
    Console.WriteLine($"Eroare detectată la data: {dataOra.ToString()}");
}

Rezultat & Impact

Viteză de execuție de până la 10x mai mare cu presiune zero pe Garbage Collector.

Greșeli frecvente și bune practici

  • Încercarea de a stoca un `Span` ca un câmp într-o clasă obișnuită sau utilizarea lui într-o metodă `async`; fiind un `ref struct`, Span nu poate părăsi stiva (folosiți `Memory` pentru scenarii asincrone).
  • Conversia prematură a fiecărui fragment înapoi în `string` prin apelul `.ToString()`, ceea ce anulează complet beneficiul de alocare zero.

Ghiduri și noțiuni înrudite