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.