I have two dumps from production and both of them JIT the method System.ComponentModel.Composition.Hosting.CompositionLock.LockComposition from the System.ComponentModel.Composition.dll assembly like this:
0:146> !U /d 00007ffc93d72c10
Normal JIT generated code
System.ComponentModel.Composition.Hosting.CompositionLock.LockComposition()
Begin 00007ffc93d72c10, size 53
>>> 00007ffc`93d72c10 57 push rdi
00007ffc`93d72c11 56 push rsi
00007ffc`93d72c12 4883ec28 sub rsp,28h
00007ffc`93d72c16 488bf1 mov rsi,rcx
00007ffc`93d72c19 807e1400 cmp byte ptr [rsi+14h],0
00007ffc`93d72c1d 7427 je 00007ffc`93d72c46
00007ffc`93d72c1f 48b9487a2f9cfc7f0000 mov rcx,7FFC9C2F7A48h (MT: System.ComponentModel.Composition.Hosting.CompositionLock+CompositionLockHolder)
00007ffc`93d72c29 e8e2f8ef54 call clr!JIT_TrialAllocSFastMP_InlineGetThread (00007ffc`e8c72510)
00007ffc`93d72c2e 488bf8 mov rdi,rax
00007ffc`93d72c31 488bcf mov rcx,rdi
00007ffc`93d72c34 488bd6 mov rdx,rsi
00007ffc`93d72c37 e844e5ffff call 00007ffc`93d71180 (System.ComponentModel.Composition.Hosting.CompositionLock+CompositionLockHolder..ctor(System.ComponentModel.Composition.Hosting.CompositionLock), mdToken: 000000000600061e)
00007ffc`93d72c3c 488bc7 mov rax,rdi
00007ffc`93d72c3f 4883c428 add rsp,28h
00007ffc`93d72c43 5e pop rsi
00007ffc`93d72c44 5f pop rdi
00007ffc`93d72c45 c3 ret
00007ffc`93d72c46 b977050000 mov ecx,577h
00007ffc`93d72c4b ba61000000 mov edx,61h
00007ffc`93d72c50 e80b00f054 call clr!JIT_GetSharedGCStaticBase_InlineGetAppDomain (00007ffc`e8c72c60)
00007ffc`93d72c55 488b8038010000 mov rax,qword ptr [rax+138h]
00007ffc`93d72c5c 4883c428 add rsp,28h
00007ffc`93d72c60 5e pop rsi
00007ffc`93d72c61 5f pop rdi
00007ffc`93d72c62 c3 ret
The absolute memory addresses are different, of course, but other than that both dumps show exactly the same code.
When I inspect the same method in a locally running sample application, I get a very different code:
0:063> !U /d 00007ff93aa22e70
Normal JIT generated code
System.ComponentModel.Composition.Hosting.CompositionLock.LockComposition()
Begin 00007ff93aa22e70, size 7e
>>> 00007ff9`3aa22e70 57 push rdi
00007ff9`3aa22e71 56 push rsi
00007ff9`3aa22e72 53 push rbx
00007ff9`3aa22e73 4883ec30 sub rsp,30h
00007ff9`3aa22e77 488bf1 mov rsi,rcx
00007ff9`3aa22e7a 400fb67e14 movzx edi,byte ptr [rsi+14h]
00007ff9`3aa22e7f 85ff test edi,edi
00007ff9`3aa22e81 744d je 00007ff9`3aa22ed0
00007ff9`3aa22e83 48b9f053b13af97f0000 mov rcx,7FF93AB153F0h (MT: System.ComponentModel.Composition.Hosting.CompositionLock+CompositionLockHolder)
00007ff9`3aa22e8d e81ed1a45e call clr!JIT_TrialAllocSFastMP_InlineGetThread (00007ff9`9946ffb0)
00007ff9`3aa22e92 488bd8 mov rbx,rax
00007ff9`3aa22e95 488d4b08 lea rcx,[rbx+8]
00007ff9`3aa22e99 488bd6 mov rdx,rsi
00007ff9`3aa22e9c e8bfada45e call clr!JIT_WriteBarrier (00007ff9`9946dc60)
00007ff9`3aa22ea1 33c9 xor ecx,ecx
00007ff9`3aa22ea3 894b10 mov dword ptr [rbx+10h],ecx
00007ff9`3aa22ea6 85ff test edi,edi
00007ff9`3aa22ea8 741b je 00007ff9`3aa22ec5
00007ff9`3aa22eaa b96b000000 mov ecx,6Bh
00007ff9`3aa22eaf ba61000000 mov edx,61h
00007ff9`3aa22eb4 e847d8a45e call clr!JIT_GetSharedGCStaticBase_InlineGetAppDomain (00007ff9`99470700)
00007ff9`3aa22eb9 488b8830010000 mov rcx,qword ptr [rax+130h]
00007ff9`3aa22ec0 e88bd2a45e call clr!JIT_MonEnter (00007ff9`99470150)
00007ff9`3aa22ec5 488bc3 mov rax,rbx
00007ff9`3aa22ec8 4883c430 add rsp,30h
00007ff9`3aa22ecc 5b pop rbx
00007ff9`3aa22ecd 5e pop rsi
00007ff9`3aa22ece 5f pop rdi
00007ff9`3aa22ecf c3 ret
00007ff9`3aa22ed0 b96b000000 mov ecx,6Bh
00007ff9`3aa22ed5 ba61000000 mov edx,61h
00007ff9`3aa22eda e821d8a45e call clr!JIT_GetSharedGCStaticBase_InlineGetAppDomain (00007ff9`99470700)
00007ff9`3aa22edf 488b8038010000 mov rax,qword ptr [rax+138h]
00007ff9`3aa22ee6 4883c430 add rsp,30h
00007ff9`3aa22eea 5b pop rbx
00007ff9`3aa22eeb 5e pop rsi
00007ff9`3aa22eec 5f pop rdi
00007ff9`3aa22eed c3 ret
The file (not assembly) version of System.ComponentModel.Composition.dll on the production servers is 4.8.3761.0 and that of the local one is 4.8.9032.0 - slightly different, but according to IL Spy the code in question has not changed.
The C# code for the function is:
public IDisposable LockComposition()
{
if (_isThreadSafe)
{
return new CompositionLockHolder(this);
}
return _EmptyLockHolder;
}
And it looks like the server did not inline the creation of CompositionLockHolder instance.
But the local code seems to have inlined it.
Here is more code from the same area:
public void SatisfyImportsOnce(ComposablePart part)
{
...
using (_lock.LockComposition())
{
...
}
}
...
public CompositionLockHolder(CompositionLock @lock)
{
_lock = @lock;
_isDisposed = 0;
_lock.EnterCompositionLock();
}
...
private void EnterCompositionLock()
{
if (_isThreadSafe)
{
Monitor.Enter(_compositionLock);
}
}
Can we instruct the local runtime to JIT the same way as the server?