反编译一个简单的递归函数,并获得一个包含不必要代码的循环

时间:2013-05-06 23:17:56

标签: f#

我想看看下面两个简单的递归函数是否与C#版本一样好,所以我使用ILSPY将它们反编译成C#。

    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)

获得:

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;
}

我很惊讶F#编译会生成如此混乱的代码。虽然它可能不会影响性能(JIT可能会进一步优化)。当代码在系统中更复杂和更关键时,我仍然对性能有点关注。

有关编译器发出此类代码的原因的任何评论?

1 个答案:

答案 0 :(得分:2)

我已经研究过了,似乎IL被误解为新本地的声明。请记住,F#编译器生成的IL不会始终与C#编译器匹配。似乎F#编译器将重新分配参数,无论它们是否在循环内变异。请注意,我们没有声明本地,因此所有内容都被压入堆栈。

.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