In the following program,
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
will the JIT optimize away the expression reliesOnCondition System.DateTime.Now.Second
if condition
turns out to be false upon module loading?
No*. When F# emits the IL for the program, all accesses to condition
are done through a property accessor. Because of this extra layer of indirection the JIT engine cannot optimize away the entire body of reliesOnCondition
.
Let me show how I found this out. (Also listed on an old blog post.)
Create new F# Project 'SOQ'
Build our F# application. I just hard-coded condition to be 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
Disassemble it and reassemble it with IL opcodes in the PDB
Next use ildasm
to disassemble the IL binary using the /SOURCE
parameter. This will not only get you an IL dump of the source, but also include the original source code left as comments.
ildasm SOQ.exe /OUT=SOQ-annotated.exe.il /SOURCE
Reassemble our binary from IL
Next use ilasm
to reassemble the IL binary, but passing in the /DEBUG
flag to get a PDB. The resulting application will have two levels of code. First, the origonal F# will be left in as comments, and the actual code will be IL instructions.
ilasm SOQ-annotated.exe.il /DEBUG
Run the process and attach the Visual Studio debugger
Run the newly annotated program. This will cause the application to get JIT-ted like normal. Next, attach the Visual Studio debugger to the active process.
Step through the code
Looking at an IL dump in the VS debugger isn't sufficient. Right click the 'Stack Traces' window and check Go To Disassembly. This will give you a display of the actual x86-instructions.
Here is the x86 op code dump. Note the original F# source line up top (ildasm /SOURCE
), the IL instructions beneath that (ilasm /DEBUG
), and the x86 instructions below that (courtesy of 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 ...
As you can see, the IL instruction 34 calls Program::get_condition()
, so the JIT doesn't have enough information to properly eliminate the no-op function call. (Note that only functions can be marked inline, so you can't really go any further than this.)
*On my machine (x64 Win7). There is a difference between the x86 and x64 JIT engines as well as whether or not you used NGEN to produce your executable. Your mileage may vary.
The upvoted answer is incorrect, a property accessor indirection does not in general prevent the JIT optimizer from omitting dead code. Most simple ones get inlined, their compile time values are considered. Which means that it will not optimize away the expression since the value of condition is only known at run time. The fact that the code is jitted after the condition value is already known doesn't change this, the optimizer must be able to determine the value statically. Only a literal will suffice here, you'd get one with conditional compilation (#if in C#). Check this answer for more background info on the optimizer.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With