From C# to CLR Jitted Code - Call Delegate
How expensive is a delegate call vs a direct function call? The delegate call is actually pretty fast. The delegate typically stores the function and the calling target. When we invoke the delegate, the JIT will retrieve the function pointer and the target and complete the call. You can see from below example.
Here is the C# code
public class CallDelegate
{
[MethodImpl(MethodImplOptions.NoInlining)]
public static void Run()
{
Foo();
RetVoid f1 = new RetVoid(Foo);
f1(); /// Delegate invoke
}
[MethodImpl(MethodImplOptions.NoInlining)]
public static void Foo()
{
Console.WriteLine("foo");
}
}
L_0000: nop
L_0001: call void JitBlog.CallDelegate::Foo()
L_0006: nop
L_0007: ldnull
L_0008: ldftn void JitBlog.CallDelegate::Foo()
L_000e: newobj instance void JitBlog.RetVoid::.ctor(object, native int) // Build the delegate objiect
L_0013: stloc.0
L_0014: ldloc.0
L_0015: callvirt instance void JitBlog.RetVoid::Invoke() // Delegate invoke
L_001a: nop
L_001b: ret
Here is the disassmbly
call [CallDelegate.Foo()]
nop
mov ECX, 0x1f336c8 //Type of delegate
call CORINFO_HELP_NEWSFAST_CHKRESTORE
mov ESI, EAX
mov EAX, 0x1f3c090
push EAX
push 0x4220f8 // Delegate stub
mov ECX, ESI
xor EDX, EDX
call MulticastDelegate.CtorOpened(ref,int,int)
nop
mov EDI, ESI
mov ECX, EDI
mov EAX, dword ptr [ECX+12] // Retrieve the function pointer from the Delegate
mov ECX, gword ptr [ECX+4] // Retrieve the target object address from the target
call EAX // complete the call