Reputation: 21727
I would like to see if the following 2 simple recursive function would perform as well as C# versions, so I decompiled them into C# using ILSPY.
let rec findPivot i =
if i = 0 then -1
else
if myArray.[i] > myArray.[i-1] then i - 1
else findPivot (i - 1)
let rec findTarget value i =
if (myArray.[i] > value) then i
else findTarget value (i - 1)
Getting:
internal static int findPivot@11(int[] myArray, int i)
{
while (i != 0)
{
if (myArray[i] > myArray[i - 1])
{
return i - 1;
}
int[] arg_22_0 = myArray; // useless
i--;
myArray = arg_22_0; // useless
}
return -1;
}
internal static int findTarget@17(int[] myArray, int value, int i)
{
while (myArray[i] <= value)
{
int[] arg_16_0 = myArray; // useless
int arg_14_0 = value; // useless
i--;
value = arg_14_0; // useless
myArray = arg_16_0; // useless
}
return i;
}
I am surprised that F# compiled would generate such messy code. Although it might not affect performance (JIT may optimize further). I am still a bit concern about the performance when the code is more complicated and critical in the system.
Any comments on why the compiler emits such code?
Upvotes: 0
Views: 380
Reputation: 78272
I've looked into it and it seems that the IL is being misinterpreted as a declaration of a new local. Keep in mind that the F# compiler generates IL that won't always match the C# compiler. It seems that F# compiler will reassign the arguments regardless if they are mutated within the loop. Take notice that we have no locals declared so everything is being pushed onto the stack.
.method public static
int32 findTarget (
int32 'value',
int32 i
) cil managed
{
.custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationArgumentCountsAttribute::.ctor(int32[]) = (
01 00 02 00 00 00 01 00 00 00 01 00 00 00 00 00
)
// Method begins at RVA 0x2084
// Code size 27 (0x1b)
.maxstack 8
// loop start
IL_0000: nop
IL_0001: call int32[] Program::get_myArray()
IL_0006: ldarg.1
IL_0007: ldelem.any [mscorlib]System.Int32
IL_000c: ldarg.0
IL_000d: ble.s IL_0011
IL_000f: ldarg.1
IL_0010: ret
IL_0011: ldarg.0
IL_0012: ldarg.1
IL_0013: ldc.i4.1
IL_0014: sub
IL_0015: starg.s i
IL_0017: starg.s 'value'
IL_0019: br.s IL_0000
// end loop
} // end of method Program::findTarget
Upvotes: 2