System DesignHard
When should you use Span<T> for performance in C#?
Span<T> is a stack-allocated, no-copy view into contiguous memory. Use it when allocation pressure or copies actually matter.
Use cases that earn it:
1. Parsing without allocating substrings:
public static (string user, string host) SplitEmail(ReadOnlySpan<char> email) {
int at = email.IndexOf('@');
return (email[..at].ToString(), email[(at+1)..].ToString());
}
SplitEmail("a@b.com".AsSpan()); // no intermediate substring allocations
2. Buffer slicing in I/O / serialization:
public void Write(Span<byte> destination, int value) {
BinaryPrimitives.WriteInt32LittleEndian(destination[..4], value);
}
3. String building in tight loops:
Span<char> buf = stackalloc char[32];
buf[0] = '#'; int.TryFormat(buf[1..], out int written);
return new string(buf[..(written+1)]);
When NOT to use it:
- Random code paths that aren't in a profiler
- Methods you might call from async contexts (Span can't cross
await) - Class fields (Span is a
ref struct— stack-only) - Code that's already plenty fast
Signs you should profile first:
- Garbage collection pressure shows up as P95 latency spikes
- A hot method allocates >100KB/sec at scale
- BenchmarkDotNet shows allocations as the bottleneck
Reality: for 95% of business code, string and List<T> are perfectly fine. Span<T> shines in parsing, serialization, and the inner loops of frameworks. Profile first; optimise where it matters.