在以下计划中,
module Program
let condition = System.DateTime.Now.Millisecond % 2 = 0
let inline reliesOnCondition (x:int) =
if condition then
printfn "%i" x
[<EntryPoint>]
let main args =
reliesOnConditional System.DateTime.Now.Second
0
如果reliesOnCondition System.DateTime.Now.Second
在模块加载时结果为假,JIT会优化表达式condition
吗?
答案 0 :(得分:4)
没有*。当F#为程序发出IL时,对condition
的所有访问都是通过属性访问器完成的。由于这个额外的间接层,JIT引擎无法优化reliesOnCondition
的整个主体。
让我说明我是如何发现这一点的。 (也列在old blog帖子上。)
创建新的F#项目'SOQ'
构建我们的F#应用程序。我只是将条件硬编码为false
。
module Program
let condition = false
let inline reliesOnCondition (x:int) =
if condition then
printfn "%i" x
[<EntryPoint>]
let main args =
printfn "(attach a debugger and press any key)"
System.Console.ReadKey(true) |> ignore
reliesOnCondition System.DateTime.Now.Second
0
将其拆解并在PDB中使用IL操作码重新组装
接下来使用ildasm
使用/SOURCE
参数反汇编IL二进制文件。这不仅可以获得源的IL转储,还可以包含留下作为注释的原始源代码。
ildasm SOQ.exe /OUT=SOQ-annotated.exe.il /SOURCE
从IL重新组合我们的二进制文件
接下来使用ilasm
重新组合IL二进制文件,但传入/DEBUG
标志以获取PDB。生成的应用程序将具有两个级别的代码。首先,原始F#将作为注释保留,实际代码将是IL指令。
ilasm SOQ-annotated.exe.il /DEBUG
运行该过程并附加Visual Studio调试器
运行新注释的程序。这将导致应用程序像正常一样进行JIT-ted。接下来,将Visual Studio调试器附加到活动进程。
逐步执行代码
在VS调试器中查看IL转储是不够的。右键单击“堆栈跟踪”窗口并选中转到反汇编。这将显示实际的x86指令。
这是x86操作码转储。请注意原始F#源代码行(ildasm /SOURCE
),其下面的IL指令(ilasm /DEBUG
)以及下面的x86指令(由Visual Studio提供)。
//000014: reliesOnCondition System.DateTime.Now.Second
IL_0026: call valuetype [mscorlib]System.DateTime [mscorlib]System.DateTime::get_Now()
000000db lea ecx,[ebp-58h]
000000de call 595E8C00
IL_002b: stloc.3
000000e3 lea edi,[ebp-30h]
000000e6 lea esi,[ebp-58h]
000000e9 movq xmm0,mmword ptr [esi]
000000ed movq mmword ptr [edi],xmm0
IL_002c: ldloca.s V_3
000000f1 lea eax,[ebp-30h]
000000f4 mov dword ptr [ebp-74h],eax
IL_002e: call instance int32 [mscorlib]System.DateTime::get_Second()
000000f7 mov ecx,dword ptr [ebp-74h]
000000fa call 5960A670
000000ff mov dword ptr [ebp-5Ch],eax
IL_0033: stloc.2
00000102 mov eax,dword ptr [ebp-5Ch]
00000105 mov dword ptr [ebp-28h],eax
IL_0034: call bool Program::get_condition()
00000108 call dword ptr ds:[004232D8h]
0000010e mov dword ptr [ebp-60h],eax
IL_0039: brfalse.s IL_003d
00000111 cmp dword ptr [ebp-60h],0
00000115 je 0000011A
... snip ...
如您所见,IL指令34调用Program::get_condition()
,因此JIT没有足够的信息来正确消除无操作函数调用。 (请注意,只有函数可以内联标记,因此您不能再进一步标记。)
* 在我的机器上(x64 Win7)。 x86和x64 JIT引擎之间存在差异,以及您是否使用NGEN生成可执行文件。您的里程可能会有所不同。
答案 1 :(得分:2)
upvoted答案是不正确的,属性访问器间接通常不会阻止JIT优化器省略死代码。大多数简单内容都会被内联,它们的编译时值被考虑在内。这意味着它不会优化表达式,因为 condition 的值仅在运行时已知。在 condition 值已知之后代码被jitted的事实不会改变这一点,优化器必须能够静态地确定该值。只有一个文字就足够了,你得到一个条件编译(C#中的#if)。有关优化程序的更多背景信息,请查看this answer。