为什么 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
环境变量
我是运行一个很简单的程序
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
环境变量