Phase 1 of 30 · Topic 1.5

Architecture Traps: JIT De-optimizations, Boxing & Devirtualization

1Concept

Calling interface methods or non-sealed virtual methods requires a vtable indirect jump (`callvirt`). Marking classes and methods as `sealed` allows RyuJIT to direct-call and inline aggressively, eliminating CPU branch mispredictions.

2Architecture Diagram

Virtual Call (Indirect):
Method Invocation ──> Dereference TypeHandle ──> Look up VTable Slot ──> Jump to Address

Sealed Call (Direct / Inlined):
Method Invocation ──> Direct Native CALL / Inlined Machine Instructions (Zero Overhead)

3Code Example

C# 13 & .NET 9
using System;
using System.Runtime.CompilerServices;

public interface ICalculator { int Calculate(int v); }

public sealed class OptimizedCalculator : ICalculator
{
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public int Calculate(int v) => v * 42;
}

public class DevirtualizationDemo
{
    public static void Main()
    {
        var calc = new OptimizedCalculator();
        int sum = 0;
        for (int i = 0; i < 1_000; i++)
        {
            sum += calc.Calculate(i); // RyuJIT inlines as: sum += (i * 42)
        }
        Console.WriteLine($"Optimized Sealed Inlined Sum: {sum}");
    }
}

4Expected Output

Optimized Sealed Inlined Sum: 20979000

5Key Takeaways

  • Always `seal` classes unless explicitly designed for inheritance to enable RyuJIT devirtualization.
  • Avoid interface dispatch in high-frequency inner loops.
  • Use `[MethodImpl(MethodImplOptions.AggressiveInlining)]` cautiously on small hot-path methods.