colinfang
colinfang

Reputation: 21727

Decompile a simple recursive function and get a loop with unnecessary code

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

Answers (1)

ChaosPandion
ChaosPandion

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

Related Questions