C#64位版本代码的反汇编视图比32位调试代码长75%?

编辑

我测试了32位的版本,代码很紧凑。 因此,下面是64位问题。


我正在使用VS 2012 RC。 调试是32位,Release是64位。 下面是调试然后发布一行代码的反汇编:

crc = (crc >> 8) ^ crcTable[((val & 0x0000ff00) >> 8) ^ crc & 0xff]; 0000006f mov eax,dword ptr [ebp-40h] 00000072 shr eax,8 00000075 mov edx,dword ptr [ebp-3Ch] 00000078 mov ecx,0FF00h 0000007d and edx,ecx 0000007f shr edx,8 00000082 mov ecx,dword ptr [ebp-40h] 00000085 mov ebx,0FFh 0000008a and ecx,ebx 0000008c xor edx,ecx 0000008e mov ecx,dword ptr ds:[03387F38h] 00000094 cmp edx,dword ptr [ecx+4] 00000097 jb 0000009E 00000099 call 6F54F5EC 0000009e xor eax,dword ptr [ecx+edx*4+8] 000000a2 mov dword ptr [ebp-40h],eax ----------------------------------------------------------------------------- crc = (crc >> 8) ^ crcTable[((val & 0x0000ff00) >> 8) ^ crc & 0xff]; 000000a5 mov eax,dword ptr [rsp+20h] 000000a9 shr eax,8 000000ac mov dword ptr [rsp+38h],eax 000000b0 mov rdx,124DEE68h 000000ba mov rdx,qword ptr [rdx] 000000bd mov eax,dword ptr [rsp+00000090h] 000000c4 and eax,0FF00h 000000c9 shr eax,8 000000cc mov ecx,dword ptr [rsp+20h] 000000d0 and ecx,0FFh 000000d6 xor eax,ecx 000000d8 mov ecx,eax 000000da mov qword ptr [rsp+40h],rdx 000000df mov rax,qword ptr [rsp+40h] 000000e4 mov rax,qword ptr [rax+8] 000000e8 mov qword ptr [rsp+48h],rcx 000000ed cmp qword ptr [rsp+48h],rax 000000f2 jae 0000000000000100 000000f4 mov rax,qword ptr [rsp+48h] 000000f9 mov qword ptr [rsp+48h],rax 000000fe jmp 0000000000000105 00000100 call 000000005FA5D364 00000105 mov rax,qword ptr [rsp+40h] 0000010a mov rcx,qword ptr [rsp+48h] 0000010f mov ecx,dword ptr [rax+rcx*4+10h] 00000113 mov eax,dword ptr [rsp+38h] 00000117 xor eax,ecx 00000119 mov dword ptr [rsp+20h],eax 

64位版本中的所有额外代码是做什么的? 它正在测试什么? 我没有对此进行基准测试,但32位代码应该执行得更快。

编辑

整个function:

 public static uint CRC32(uint val) { uint crc = 0xffffffff; crc = (crc >> 8) ^ crcTable[(val & 0x000000ff) ^ crc & 0xff]; crc = (crc >> 8) ^ crcTable[((val & 0x0000ff00) >> 8) ^ crc & 0xff]; crc = (crc >> 8) ^ crcTable[((val & 0x00ff0000) >> 16) ^ crc & 0xff]; crc = (crc >> 8) ^ crcTable[(val >> 24) ^ crc & 0xff]; // flip bits return (crc ^ 0xffffffff); } 

我怀疑你在调试发布版本时使用“Go to disassembly”来获取汇编代码。

在进入工具 – >选项,调试,常规和禁用“抑制模块加载时的JIT优化”后,我得到了一个x64汇编列表而没有错误检查。

默认情况下,即使在发布模式下,如果附加调试器,代码也不会优化。 在尝试对代码进行基准测试时请记住这一点。

PS:基准测试显示x64比x86快一点,对于10亿次函数调用,4.3比4.8秒。

编辑:断点对我来说仍然有效,否则我在取消选中后无法看到反汇编。 上面的示例行看起来像这样(VS 2012 RC):

 crc = (crc >> 8) ^ crcTable[((val & 0x0000ff00) >> 8) ^ crc & 0xff]; 00000030 mov r11d,eax 00000033 shr r11d,8 00000037 mov ecx,edx 00000039 and ecx,0FF00h 0000003f shr ecx,8 00000042 movzx eax,al 00000045 xor ecx,eax 00000047 mov eax,ecx 00000049 cmp rax,r9 0000004c jae 00000000000000A4 0000004e mov eax,dword ptr [r8+rax*4+10h] 00000053 xor r11d,eax 

查看代码,这与访问crcTable的错误检查有关。 在它开始挖掘数组之前,它正在做你的界限。

在32位代码中,您会看到这一点

 0000008e mov ecx,dword ptr ds:[03387F38h] .... 0000009e xor eax,dword ptr [ecx+edx*4+8] 

在这种情况下,它从03387F38h加载数组的基址,然后使用标准指针算法访问正确的条目。

在64位代码中,这似乎更复杂。

 000000b0 mov rdx,124DEE68h 000000ba mov rdx,qword ptr [rdx] 

这会将地址加载到rdx寄存器中

 000000da mov qword ptr [rsp+40h],rdx ... 00000105 mov rax,qword ptr [rsp+40h] 0000010a mov rcx,qword ptr [rsp+48h] 0000010f mov ecx,dword ptr [rax+rcx*4+10h] 

这会将地址移动到堆栈上,然后稍后将其移动到rax寄存器中并执行相同的指针来访问数组。

几乎000000da和00000100/00000105之间的所有内容似乎都是validation码。 其余的代码在64位和32位代码之间很好地映射,在64位代码中有一些不太激进的寄存器利用率。

exp ^ crc&0xff编译为exp ^(cr&0xff):

 00000082 mov ecx,dword ptr [ebp-40h] 00000085 mov ebx,0FFh 0000008a and ecx,ebx 0000008c xor edx,ecx 

你应该把表达式写成?

 (exp ^ crc) & 0xff 

64位版本肯定不如32位版本优化。 CLR有两个单独的JIT编译器实现。

此外,如果perf是关键的,使用不安全的代码来删除边界检查。