代码之家  ›  专栏  ›  技术社区  ›  AakashM

有没有一种方法可以看到抖动为给定的c/cil生成的本机代码?

  •  20
  • AakashM  · 技术社区  · 15 年前

    在评论中 this answer (为了提高性能,建议在整数乘法/除法上使用位移位运算符),我询问这是否会更快。在我的脑海里有一个想法 一些 水平,有足够聪明的东西可以解决这个问题 >> 1 / 2 是相同的操作。然而,我现在想知道这是否是真的,如果是真的,在什么程度上发生。

    测试程序生成以下比较CIL(与 optimize on)对于分别划分和移动其参数的两种方法:

      IL_0000:  ldarg.0
      IL_0001:  ldc.i4.2
      IL_0002:  div
      IL_0003:  ret
    } // end of method Program::Divider
    

    对战

      IL_0000:  ldarg.0
      IL_0001:  ldc.i4.1
      IL_0002:  shr
      IL_0003:  ret
    } // end of method Program::Shifter
    

    所以C编译器正在发出 div shr 指示,而不是聪明。我现在想看看抖动产生的实际x86汇编程序,但我不知道如何做到这一点。有可能吗?

    编辑 添加

    调查结果

    感谢您的回答,已接受NoBugz提供的答案,因为它包含有关该调试器选项的关键信息。最终对我起作用的是:

    • 切换到释放配置
    • Tools | Options | Debugger ,关闭“抑制模块加载时的JIT优化”(即我们希望 允许 JIT优化)
    • 在同一个地方,关闭“仅启用我的代码”(即我们要调试 全部的 代码)
    • 放一个 Debugger.Break() 在某处发表声明
    • 生成程序集
    • 运行.exe,当它中断时,使用现有的vs实例进行调试
    • 现在,反汇编窗口显示将要执行的实际x86

    结果很有启发性,至少可以这么说——事实证明,抖动实际上可以做算术!以下是从反汇编窗口编辑的示例。各种各样 -Shifter 方法除以两次幂使用 >> 各种 -Divider 方法用整数除 /

     Console.WriteLine(string.Format("
         {0} 
         shift-divided by 2: {1} 
         divide-divided by 2: {2}", 
         60, TwoShifter(60), TwoDivider(60)));
    
    00000026  mov         dword ptr [edx+4],3Ch 
    ...
    0000003b  mov         dword ptr [edx+4],1Eh 
    ...
    00000057  mov         dword ptr [esi+4],1Eh 
    

    两种静态除2方法都是内联的,但实际计算都是由抖动完成的。

    Console.WriteLine(string.Format("
        {0} 
        divide-divided by 3: {1}", 
        60, ThreeDivider(60)));
    
    00000085  mov         dword ptr [esi+4],3Ch 
    ...
    000000a0  mov         dword ptr [esi+4],14h 
    

    与静态除以3相同。

    Console.WriteLine(string.Format("
        {0} 
        shift-divided by 4: {1} 
        divide-divided by 4 {2}", 
        60, FourShifter(60), FourDivider(60)));
    
    000000ce  mov         dword ptr [esi+4],3Ch 
    ...
    000000e3  mov         dword ptr [edx+4],0Fh 
    ...
    000000ff  mov         dword ptr [esi+4],0Fh 
    

    静态除以4。

    最好的:

    Console.WriteLine(string.Format("
        {0} 
        n-divided by 2: {1} 
        n-divided by 3: {2} 
        n-divided by 4: {3}", 
        60, Divider(60, 2), Divider(60, 3), Divider(60, 4)));
    
    0000013e  mov         dword ptr [esi+4],3Ch 
    ...
    0000015b  mov         dword ptr [esi+4],1Eh 
    ...
    0000017b  mov         dword ptr [esi+4],14h 
    ...
    0000019b  mov         dword ptr [edi+4],0Fh 
    

    它是内联的,然后计算所有这些静态分区!

    但是如果结果不是静态的呢?我添加到代码以从控制台读取整数。这就是它为各部门所产生的:

    Console.WriteLine(string.Format("
        {0} 
        shift-divided by 2:  {1} 
        divide-divided by 2: {2}", 
        i, TwoShifter(i), TwoDivider(i)));
    
    00000211  sar         eax,1 
    ...
    00000230  sar         eax,1 
    

    所以尽管CIL是不同的,抖动知道除以2就是右移乘以1。

    Console.WriteLine(string.Format("
        {0} 
        divide-divided by 3: {1}", i, ThreeDivider(i)));
    

    00000283 IDIV EAX和ECX

    它知道你必须除以3。

    Console.WriteLine(string.Format("
        {0} 
        shift-divided by 4: {1} 
        divide-divided by 4 {2}", 
        i, FourShifter(i), FourDivider(i)));
    
    000002c5  sar         eax,2 
    ...
    000002ec  sar         eax,2 
    

    它知道除以4就是右移乘以2。

    最后(又是最好的!)

    Console.WriteLine(string.Format("
        {0} 
        n-divided by 2: {1} 
        n-divided by 3: {2} 
        n-divided by 4: {3}", 
        i, Divider(i, 2), Divider(i, 3), Divider(i, 4)));
    
    00000345  sar         eax,1 
    ...
    00000370  idiv        eax,ecx 
    ...
    00000395  sar         esi,2 
    

    它在静态可用参数的基础上嵌入了该方法,并找到了最佳的处理方法。很好。


    是的,在C和x86之间的堆栈中,有一些 聪明到可以解决这个问题 >>一 2 都一样。所有这一切在我的头脑中给予了更大的分量,我认为把C编译器、抖动和clr加在一起会使 更聪明 比我们作为普通应用程序程序员所能尝试的任何小窍门都重要:)

    3 回复  |  直到 15 年前
        1
  •  8
  •   Hans Passant    15 年前

    在配置调试器之前,不会得到有意义的结果。工具+选项,调试,常规,关闭“模块加载时抑制JIT优化”。切换到释放模式配置。示例代码段:

    static void Main(string[] args) {
      int value = 4;
      int result = divideby2(value);
    }
    

    如果拆卸过程如下所示,则说明操作正确:

    00000000  ret  
    

    您必须愚弄JIT优化器才能强制计算表达式。使用console.writeline(变量)可以有所帮助。那么你应该看到这样的东西:

    0000000a  mov         edx,2 
    0000000f  mov         eax,dword ptr [ecx] 
    00000011  call        dword ptr [eax+000000BCh] 
    

    是的,它在编译时对结果进行了评估。效果很好,不是吗?

        2
  •  3
  •   Maximilian Mayerl    15 年前

    对。Visual Studio有一个内置的反汇编程序来实现这一点。不过,您必须将该命令添加到菜单栏中。转到extras/customize/commands(我不知道在英语版本中是否真的是这样调用的),然后在菜单栏的某个地方添加命令disassembly,这是unter调试。

    然后,在程序中设置一个断点,当它中断时,单击这个反汇编命令。vs将显示分解后的机器代码。

    除法器方法的示例输出:

    public static int Divider(int intArg)
        {
    00000000  push        ebp  
    00000001  mov         ebp,esp 
    00000003  push        edi  
    00000004  push        esi  
    00000005  push        ebx  
    00000006  sub         esp,34h 
    00000009  mov         esi,ecx 
    0000000b  lea         edi,[ebp-38h] 
    0000000e  mov         ecx,0Bh 
    00000013  xor         eax,eax 
    00000015  rep stos    dword ptr es:[edi] 
    00000017  mov         ecx,esi 
    00000019  xor         eax,eax 
    0000001b  mov         dword ptr [ebp-1Ch],eax 
    0000001e  mov         dword ptr [ebp-3Ch],ecx 
    00000021  cmp         dword ptr ds:[00469240h],0 
    00000028  je          0000002F 
    0000002a  call        6BA09D91 
    0000002f  xor         edx,edx 
    00000031  mov         dword ptr [ebp-40h],edx 
    00000034  nop              
        return intArg / 2;
    00000035  mov         eax,dword ptr [ebp-3Ch] 
    00000038  sar         eax,1 
    0000003a  jns         0000003F 
    0000003c  adc         eax,0 
    0000003f  mov         dword ptr [ebp-40h],eax 
    00000042  nop              
    00000043  jmp         00000045 
        }
    
        3
  •  2
  •   Oliver    15 年前

    在调试期间(并且只有在调试期间),只需单击debug-windows-disassembly或按相应的快捷方式ctrl+alt+d即可。