Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
The performance implications of try are more severe than I had thought. I thought it was just code within the try block that isn’t optimized. It turns out it’s everything around the block too. Take this code:
object syncObject = new Object();
[System.Runtime.CompilerServices.MethodImpl(MethodImplOptions.NoInlining)]
public int Member ( )
{
int value = 10;
Monitor.Enter(this.syncObject);
try
{
}
finally
{
Trace.WriteLine("ding");
}
Monitor.Exit(this.syncObject);
return value;
}
Compiled with TRACE defined we get this:
!u 011d01e0
Normal JIT generated code
WindowsApplication2.Tester.Member()
Begin 011d01e0, size 65
>>> 011D01E0 55 push ebp
011D01E1 8BEC mov ebp,esp
011D01E3 57 push edi
011D01E4 56 push esi
011D01E5 53 push ebx
011D01E6 83EC1C sub esp,1Ch
011D01E9 33C0 xor eax,eax
011D01EB 8945E8 mov dword ptr [ebp-18h],eax
011D01EE 894DD8 mov dword ptr [ebp-28h],ecx
011D01F1 C745DC0A000000 mov dword ptr [ebp-24h],0Ah
011D01F8 8B45D8 mov eax,dword ptr [ebp-28h]
011D01FB 8B480C mov ecx,dword ptr [eax+0Ch]
011D01FE E8D63ACA78 call 79E73CD9 (System.Threading.Monitor.Enter(System.Object), mdToken: 060011b7)
011D0203 C745E400000000 mov dword ptr [ebp-1Ch],0
011D020A C745E8FC000000 mov dword ptr [ebp-18h],0FCh
011D0211 683C021D01 push 11D023Ch
011D0216 EB00 jmp 011D0218
011D0218 8B0DC46B3802 mov ecx,dword ptr ds:[02386BC4h]
011D021E E8FDF53279 call 7A4FF820 (System.Diagnostics.Trace.WriteLine(System.String), mdToken: 06000e9a)
011D0223 58 pop eax
011D0224 FFE0 jmp eax
011D0226 8B45D8 mov eax,dword ptr [ebp-28h]
011D0229 8B480C mov ecx,dword ptr [eax+0Ch]
011D022C E8223DCA78 call 79E73F53 (System.Threading.Monitor.Exit(System.Object), mdToken: 060011b9)
011D0231 8B45DC mov eax,dword ptr [ebp-24h]
011D0234 8D65F4 lea esp,[ebp-0Ch]
011D0237 5B pop ebx
011D0238 5E pop esi
011D0239 5F pop edi
011D023A 5D pop ebp
011D023B C3 ret
011D023C C745E800000000 mov dword ptr [ebp-18h],0
011D0243 EBE1 jmp 011D0226
Notice 0Ah assigned at 011D01F1 before the try block , it’s not optimizing the assignment of 10 to value away. Whereas if we undefined TRACE the same code produces:
!u 011d01e0
Normal JIT generated code
WindowsApplication2.Tester.Member()
Begin 011d01e0, size 1a
>>> 011D01E0 56 push esi
011D01E1 8BF1 mov esi,ecx
011D01E3 8B4E0C mov ecx,dword ptr [esi+0Ch]
011D01E6 E8EE3ACA78 call 79E73CD9 (System.Threading.Monitor.Enter(System.Object), mdToken: 060011b7)
011D01EB 8B4E0C mov ecx,dword ptr [esi+0Ch]
011D01EE E8603DCA78 call 79E73F53 (System.Threading.Monitor.Exit(System.Object), mdToken: 060011b9)
011D01F3 B80A000000 mov eax,0Ah
011D01F8 5E pop esi
011D01F9 C3 ret
That’s a HUGE difference, notice the optimization of 10 to a simple return AFTER the try block. Not to mention, if Monitor.Exit is an implicit volatile write where is my guarantee that “the write is guaranteed to happen after any memory references prior to the write instruction in the CIL instruction sequence.”
__
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.