我想看看下面两个简单的递归函数是否与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可能会进一步优化)。当代码在系统中更复杂和更关键时,我仍然对性能有点关注。
有关编译器发出此类代码的原因的任何评论?
答案 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