Span<T> and performance
Copying data costs time, and every new heap object is work for the garbage collector. A Span<T> is a view of memory that already exists: a reference to the first element plus a length. Follow a nine-line program that adds up the numbers in "12,34,5" with a ReadOnlySpan<char>: slicing moves the view, never the chars, so the parsing makes no new strings and the program prints 51, where csv.Split(',') would make 4 new heap objects.
5 quick questions
Pick an answer to see why. Your score saves with your progress.
01What does ReadOnlySpan<char> s = csv; store?
A span is a view: a reference to where the data starts plus how many elements it covers. The chars stay where they are; nothing is copied.
02s holds "12,34,5". What happens in memory when you write s = s[3..]?
Slicing a span makes a new view over the same memory. The string is untouched; only the two numbers that describe the window change.
03How many new heap objects does csv.Split(',') make for "12,34,5"?
Split returns a new array, and each part is a new string with its chars copied in. Each one is work to create and later work for the garbage collector. The span parsing in this lesson makes no new heap objects.
04Why can't you store a Span<char> in a field of a class?
A ref struct cannot be boxed, be a field of a class, or be held across an await. To keep a view on the heap or across an await, use Memory<T> or ReadOnlyMemory<T>.
05s views "12,34,5". Which line finds the comma and reads the first number without making a new string?
IndexOf searches inside the span and returns 2. s[..i] is a view of the first two chars, and int.Parse has an overload that reads a ReadOnlySpan<char> directly. Substring, Split and ToString all copy chars into new strings.
Glossary
- Allocation
- Making a new object on the managed heap. The garbage collector must later clean up every allocation.
- Span<T>
- A ref struct that views a contiguous region of memory (an array, stackalloc or native memory; ReadOnlySpan<char> for a string) as a reference to its first element plus a length.
- ReadOnlySpan<T>
- A span you can read but not write through. A string converts to ReadOnlySpan<char> without copying.
- Slice
- A smaller view of the same memory, written with a range: s[..i] is the first i elements, s[3..] everything from index 3. Nothing is copied.
- ref struct
- A struct the compiler keeps on the stack: it cannot be boxed, be a field of a class, or be held across an await.
- Boxing
- Copying a value (such as a struct) into a new object on the heap so it can be treated as an object. A ref struct is never allowed to be boxed.
- await
- The C# keyword that pauses an async method until a task finishes. The method's state may move to the heap while it waits, so a span cannot be held across it.
- Memory<T>
- A span's heap-friendly cousin: it can be stored in fields and kept across await, and gives a Span<T> when you need one.
Notes and sources
- The program is real C# (top-level statements, .NET 8 or later). Put the nine lines in Program.cs and dotnet run prints 51.
- Inside .NET a span holds a managed reference to its first element and an int length. The figure draws the reference as the index of the char it points at.
- String.Split returns a new array of new strings. The figure counts only those result objects.
- int.Parse(ReadOnlySpan<char>) uses the current culture, like int.Parse(string). The digits here parse the same in every culture.
- Spans can also view arrays, stackalloc memory and native memory. To keep a view on the heap or across an await, use Memory<T> or ReadOnlyMemory<T>.
- csv is a string literal. The runtime interns literals: it makes each one once, when the code first needs it, and reuses it. On .NET 8 and later, literals are usually stored on a frozen heap that the garbage collector never cleans up.
- The zero counts only the parsing work (lines 2 to 8). Console.WriteLine(sum) on line 9 still turns 51 into text to print it, and that can allocate.
- Since C# 13 a ref struct local may appear in an async method or iterator, but still never across an await or yield return.
- Microsoft Learn: Span<T> struct
- Microsoft Learn: ReadOnlySpan<T> struct
- Microsoft Learn: Memory<T> and Span<T> usage guidelines
- Microsoft Learn: Memory- and span-related types
- Microsoft Learn: ref struct types
- Microsoft Learn: Int32.Parse method
- Microsoft Learn: MemoryExtensions.IndexOf method
- Microsoft Learn: String.Split method
- Microsoft Learn: Indices and ranges
- Microsoft Learn: String.Intern method (the intern pool of literals)
- Microsoft Learn: Boxing and unboxing
- Microsoft Learn: Fundamentals of garbage collection
Quiz Unlocks after the last step.