ManualMethods.Average(System.Collections.Generic.Dictionary`2<System.String,Int32>)
L0000: push ebp
L0001: mov ebp, esp
L0003: push edi
L0004: push esi
L0005: push ebx
L0006: sub esp, 0x54
L0009: vzeroupper
L000c: vxorps xmm4, xmm4, xmm4
L0010: vmovdqu [ebp-0x50], xmm4
L0015: vmovdqu [ebp-0x40], xmm4
L001a: vmovdqu [ebp-0x30], xmm4
L001f: vmovdqu [ebp-0x20], xmm4
L0024: mov ebx, ecx
L0026: xor eax, eax
L0028: xor edx, edx
L002a: mov [ebp-0x54], eax
L002d: mov [ebp-0x58], edx
L0030: mov [ebp-0x50], ebx
L0033: mov ecx, [ebx+0x24]
L0036: mov [ebp-0x4c], ecx
L0039: mov [ebp-0x48], edx
L003c: mov dword ptr [ebp-0x44], 2
L0043: mov [ebp-0x40], edx
L0046: mov [ebp-0x3c], edx
L0049: lea edi, [ebp-0x38]
L004c: lea esi, [ebp-0x50]
L004f: mov ecx, 6
L0054: rep movsd [edi], [esi]
L0056: lea ecx, [ebp-0x38]
L0059: mov edx, 0x277b31bc
L005e: call System.Collections.Generic.Dictionary`2+Enumerator[[System.__Canon, System.Private.CoreLib],[System.Int32, System.Private.CoreLib]].MoveNext()
L0063: test eax, eax
L0065: je short L00c8
L0067: lea ecx, [ebp-0x28]
L006a: mov ecx, [ecx+4]
L006d: mov edx, ecx
L006f: sar edx, 0x1f
L0072: mov esi, ecx
L0074: add esi, [ebp-0x54]
L0077: mov edi, edx
L0079: adc edi, [ebp-0x58]
L007c: jo short L00d0
L007e: lea ecx, [ebp-0x38]
L0081: mov edx, 0x277b31bc
L0086: call System.Collections.Generic.Dictionary`2+Enumerator[[System.__Canon, System.Private.CoreLib],[System.Int32, System.Private.CoreLib]].MoveNext()
L008b: test eax, eax
L008d: jne short L00c0
L008f: push edi
L0090: push esi
L0091: call 0x71d052a0
L0096: fstp qword ptr [ebp-0x60], st
L0099: mov eax, [ebx+0x18]
L009c: sub eax, [ebx+0x20]
L009f: vxorps xmm1, xmm1, xmm1
L00a3: vcvtsi2sd xmm1, xmm1, eax
L00a7: vmovsd xmm0, [ebp-0x60]
L00ac: vdivsd xmm0, xmm0, xmm1
L00b0: vmovsd [ebp-0x60], xmm0
L00b5: fld st, qword ptr [ebp-0x60]
L00b8: lea esp, [ebp-0xc]
L00bb: pop ebx
L00bc: pop esi
L00bd: pop edi
L00be: pop ebp
L00bf: ret
L00c0: mov [ebp-0x54], esi
L00c3: mov [ebp-0x58], edi
L00c6: jmp short L0067
L00c8: mov esi, [ebp-0x54]
L00cb: mov edi, [ebp-0x58]
L00ce: jmp short L008f
L00d0: call 0x71e30f00
L00d5: int3
Так что в принципе "лишнего" кода, который ломает оптимизацию тут достаточно.
Больше всего грешу на делегат, тк вызов делегата сильно дороже, чем просто число взять.
А ещё при вызове через foreach используется GetEnumerator от словаря, что также позволяет сэкономить на виртуальных вызовах.