Lesson 29 of 40
Performance
Expert
55 min
Memory Management & Spans
Write zero-allocation, high-performance C# using Span
Part 1: Span Fundamentals
// Span — stack-only, zero allocation slice
Span<byte> buffer = stackalloc byte[256];
Span<byte> slice = buffer[10..20]; // no allocation
// Parse numbers from string without substring
ReadOnlySpan<char> text = "Price: 42.50";
decimal.TryParse(text[7..], out var price);
Span<byte> buffer = stackalloc byte[256];
Span<byte> slice = buffer[10..20]; // no allocation
// Parse numbers from string without substring
ReadOnlySpan<char> text = "Price: 42.50";
decimal.TryParse(text[7..], out var price);
Part 2: ArrayPool to Avoid Allocations
var pool = ArrayPool<byte>.Shared;
var buffer = pool.Rent(1024);
try
{
// Use buffer as Span
Span<byte> span = buffer.AsSpan(0, 1024);
// ... process
}
finally { pool.Return(buffer); }
var buffer = pool.Rent(1024);
try
{
// Use buffer as Span
Span<byte> span = buffer.AsSpan(0, 1024);
// ... process
}
finally { pool.Return(buffer); }
Part 3: System.IO.Pipelines
Pipelines provide a high-performance I/O API that avoids double-buffering:
var pipe = new Pipe();
// Producer fills pipe
await socket.ReceiveAsync(pipe.Writer.GetMemory(4096));
pipe.Writer.Advance(bytesRead);
// Consumer reads from pipe
ReadResult result = await pipe.Reader.ReadAsync();
// Producer fills pipe
await socket.ReceiveAsync(pipe.Writer.GetMemory(4096));
pipe.Writer.Advance(bytesRead);
// Consumer reads from pipe
ReadResult result = await pipe.Reader.ReadAsync();
Part 4: Measuring Allocation Impact
Use dotMemory or PerfView to measure allocations. In VS 2026 profiler, the Allocation Tracking view shows:
- Bytes allocated per call path
- GC generations promoted to
- LOH (Large Object Heap) allocations