为什么 x86 JIT 比 x64 更聪明?

Why x86 JIT is smarter than x64?

我是运行一个很简单的程序

    static void Main(string[] args)
    {
        Console.WriteLine(Get4S());
        Console.WriteLine(Get4());
    }

    private static int Get4S()
    {
        return 4;
    }

    private static int Get4()
    {
        int res = 0;
        for (int i = 0; i < 4; i++)
        {
            res++;
        }
        return res;
    }

当它在 x86 下工作时,它内联 Get4S 方法和 Get4 asm 代码是:

00000000  push        ebp 
00000001  mov         ebp,esp 
00000003  xor         eax,eax 
00000005  inc         eax 
00000006  inc         eax 
00000007  inc         eax 
00000008  inc         eax 
00000009  pop         ebp 
0000000a  ret 

但是当 运行 在 x64 下我们得到与 Get4S 方法相同的 asm,但是 Get4 asm 根本没有优化:

00000000  xor         eax,eax 
00000002  xor         edx,edx 
00000004  inc         eax 
00000006  inc         edx 
00000008  cmp         edx,4 
0000000b  jl          0000000000000004 
0000000d  ret 

我假设x64 JIT展开循环,然后看到结果可以在编译时计算,并且将内联具有编译时结果的函数。但它什么也没发生。

为什么x64在这种情况下如此愚蠢?..

我明白了。这是因为在选择 x64 build 时使用 RyuJIT,即使选择了 .Net 4.5.2 target platform。所以我通过在 App.Config 文件中添加此部分来修复它:

<configuration>
  <runtime>
    <useLegacyJit enabled="1" />
  </runtime>
</configuration>

此标记启用 "legacy" x64 JIT(在引号中,因为我认为他比 "shiny" RyuJIT 好得多),并且 main 方法中的结果 ASM 是:

00000000  sub         rsp,28h 
00000004  mov         ecx,4 
00000009  call        000000005EE75870 
0000000e  mov         ecx,4 
00000013  call        000000005EE75870 
00000018  nop 
00000019  add         rsp,28h 
0000001d  ret 

这两种方法都是在编译时计算的,并由它们的值内联。

结论:安装.Net 4.6后,对于CLR4.0下的所有解决方案,旧的x64抖动被RyuJIT取代。所以关闭它的唯一方法是 useLegacyJit 开关或 COMPLUS_AltJit 环境变量