在对this answer的评论中(这建议使用移位运算符,而不是整数乘法/除法来提高性能),我询问这实际上是否会更快。我的想法是,在某种程度上,某种东西会足够聪明,以至于>> 1/ 2是同一操作。但是,我现在想知道这是否真的是真的,如果是的话,它发生在什么级别。

一个测试程序为两种分别划分和移动其参数的方法生成以下比较CIL(打开optimize):

  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#编译器会发出divshr指令,而不很聪明。现在,我想看看JITter生成的实际x86汇编程序,但是我不知道该怎么做。可能吗

编辑添加

发现

感谢您的回答,已接受nobugz的回答,因为它包含有关该调试器选项的关键信息。最终对我有用的是:
  • 切换到发布配置
  • Tools | Options | Debugger中,关闭“在模块加载时禁止JIT优化”(即我们要允许JIT优化)
  • 在同一位置,关闭“启用我的代码”(即我们要调试所有代码)
  • Debugger.Break()语句放在
  • 某处
  • 生成装配体
  • 运行.exe,当它损坏时,使用现有的VS实例
  • 进行调试
  • 现在,“反汇编”窗口显示将要执行的实际x86

  • 至少可以说,结果令人鼓舞-事实证明JITter实际上可以算术!这是在“反汇编”窗口中编辑的样本。各种-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
    

    两种静态除数方法均已内联,实际的计算已由JITter完成
    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
    

    与static-by-by-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有所不同,但JITter知道除以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之间的堆栈中的某个地方,足够聪明的东西可以算出>> 1/ 2是相同的。在我看来,所有这些使我更加重视,我认为将C#编译器,JITter和CLR结合起来,比我们作为谦虚的应用程序程序员可以尝试的任何小技巧都聪明得多:)

    最佳答案

    在配置调试器之前,您不会得到有意义的结果。在“工具+选项”,“调试”,“常规”中,关闭“在模块加载时禁止JIT优化”。切换到 Release模式配置。样本片段:

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

    如果反汇编如下所示,则说明您做对了:
    00000000  ret
    

    您必须愚弄JIT优化器以强制对表达式进行求值。使用Console.WriteLine(variable)会有所帮助。然后,您应该看到这样的内容:
    0000000a  mov         edx,2
    0000000f  mov         eax,dword ptr [ecx]
    00000011  call        dword ptr [eax+000000BCh]
    

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

    10-08 02:16