Performance, Span, Memory, GC & Caching Masterclass
Welcome to Phase 16 (Chapter 43): C# Performance Optimization โ Span<T>, Memory<T>, GC, Caching & Profiling Masterclass! Performance in C# comes from understanding memory allocation, avoiding unnecessary GC pressure, using stack-based structures for hot paths, caching repeated computations, optimizing async I/O, and profiling to find real bottlenecks before optimizing. In this chapter, we master all key .NET performance tools and patterns.
Key Rule: Reduce heap allocations in hot paths to avoid Gen 0 GC pauses. Use Span<T>, stackalloc, ArrayPool<T>, and struct types to keep data on the stack instead of triggering heap allocation.
// SLOW: String.Substring allocates a NEW string object on the heap every call
string original = "Hello, World!";
string sub = original.Substring(7); // Heap allocation!
// FAST: Span<T> is a view into existing memory โ ZERO heap allocation
ReadOnlySpan<char> span = original.AsSpan(7); // No allocation!
Console.WriteLine(span.ToString()); // "World!"
// Span over array slice โ no copy
int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
Span<int> middle = numbers.AsSpan(3, 4); // Elements 4,5,6,7 โ no copy
foreach (int n in middle) Console.Write(n + " ");
// Program.cs โ Register IMemoryCache
builder.Services.AddMemoryCache();
// Service โ Cache expensive database results
public class ProductService
{
private readonly IMemoryCache _cache;
private readonly AppDbContext _context;
public ProductService(IMemoryCache cache, AppDbContext context)
{
_cache = cache; _context = context;
}
public async Task<List<Product>> GetAllProductsAsync()
{
const string cacheKey = "products:all";
if (!_cache.TryGetValue(cacheKey, out List<Product>? products))
{
// Cache MISS โ hit the database
products = await _context.Products.ToListAsync();
// Store in cache for 5 minutes
_cache.Set(cacheKey, products, TimeSpan.FromMinutes(5));
}
return products!; // Cache HIT โ no DB query!
}
}
Q1: When should I use Span<T> vs Memory<T>?
Span<T> is a ref struct that can only live on the stack โ ideal for synchronous hot-path processing (parsing, slicing). Memory<T> can live on the heap and works across async method boundaries where Span<T> cannot be stored.
Q2: What is the best profiling tool for .NET performance?
Use dotnet-trace and dotnet-counters for CLI profiling, BenchmarkDotNet for micro-benchmarks, Visual Studio Profiler for CPU/memory analysis, and Application Insights / Datadog for production performance monitoring.