Performance, Span, Memory, GC & Caching Masterclass

โšก C# 12 & .NET 8 ๐ŸŸข Chapter 43 of 35 ๐Ÿ“‚ Phase 16: Advanced .NET ๐Ÿ“… 2026 Edition
๐Ÿ“Œ Covered in this chapter: GC Generations ยท Memory Allocation ยท Span ยท Memory ยท Zero-Copy Processing ยท IMemoryCache ยท Cache-Aside Pattern ยท BenchmarkDotNet ยท Profiling

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.

1Garbage Collection & Memory Allocation
.NET Garbage Collector (GC) Generations: Gen 0 โ† New objects (short-lived). Collected frequently (milliseconds). โ””โ”€โ”€ Survivors promoted to... Gen 1 โ† Medium-lived objects. Collected less often. โ””โ”€โ”€ Survivors promoted to... Gen 2 โ† Long-lived objects (singletons, caches). Collected rarely. LOH โ†’ Large Object Heap (objects > 85KB). Rarely collected!

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.

2Span<T> & Memory<T> โ€” Zero-Copy Processing
C# โ€” Span<T> Performance โ–ถ Run in Compiler
// 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 + " ");
3In-Memory Caching & Distributed Cache
C# โ€” IMemoryCache โ–ถ Run in Compiler
// 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!
    }
}
4Technical FAQs

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.