我偶然发现了这一点,我有点困惑.
我有一个开箱即用的VS 2010 F#项目,所有默认设置都针对.NET 4.0.
F#代码是这样的:
let test(a:int, b:int, c:int) = min a (min b c)
Run Code Online (Sandbox Code Playgroud)
当我编译它以供发布时,生成的IL包含一些NOP
分散的奇怪指令.像这样:
为此生成的IL(具有所有默认设置):
.method public static int32 test(int32 a,
int32 b,
int32 c) cil managed
{
// Code size 20 (0x14)
.maxstack 4
.locals init ([0] int32 V_0)
// HERE
IL_0000: nop
IL_0001: ldarg.1
IL_0002: ldarg.2
IL_0003: bge.s IL_0009
IL_0005: ldarg.1
// HERE
IL_0006: nop
IL_0007: br.s IL_000b
IL_0009: ldarg.2
// HERE
IL_000a: nop
IL_000b: stloc.0
IL_000c: ldarg.0
IL_000d: ldloc.0
IL_000e: bge.s IL_0012
IL_0010: ldarg.0
IL_0011: ret
IL_0012: ldloc.0
IL_0013: ret
} // end of method Module1::test
Run Code Online (Sandbox Code Playgroud)
我的.fsproj项目配置是:
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<Tailcalls>true</Tailcalls>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<WarningLevel>3</WarningLevel>
<DocumentationFile>bin\Release\TestFsIL.XML</DocumentationFile>
</PropertyGroup>
Run Code Online (Sandbox Code Playgroud)
现在,如果我注释掉线<DebugType>pdbonly</DebugType>,NOP说明就会消失.但当然PDB文件也是如此!
.method public static int32 test(int32 a,
int32 b,
int32 c) cil managed
{
// Code size 17 (0x11)
.maxstack 4
.locals init (int32 V_0)
IL_0000: ldarg.1
IL_0001: ldarg.2
IL_0002: bge.s IL_0007
IL_0004: ldarg.1
IL_0005: br.s IL_0008
IL_0007: ldarg.2
IL_0008: stloc.0
IL_0009: ldarg.0
IL_000a: ldloc.0
IL_000b: bge.s IL_000f
IL_000d: ldarg.0
IL_000e: ret
IL_000f: ldloc.0
IL_0010: ret
} // end of method Module1::test
Run Code Online (Sandbox Code Playgroud)
该.locals行还有一个微妙的不同:
.locals init ([0] int32 V_0)
Run Code Online (Sandbox Code Playgroud)
VS
.locals init (int32 V_0)
Run Code Online (Sandbox Code Playgroud)
当我尝试使用C#编译器时,它NOP仅在调试版本中生成指令,但这些指令似乎在发布版本中消失,即使使用包含PDB文件也是如此<DebugType>pdbonly</DebugType>.
问题:
当包含PDB文件时,为什么在发布版本中在F#中生成NOP指令,而C#似乎能够避免这种情况.
有没有办法摆脱那些NOP,但仍然有PDB文件?
PS.这里 和这里有关于SO的相关问题,但是那里有所有的答案
你正在调试模式下编译,如果你在发布模式下编译,它
NOP就会消失
这与我对F#编译器的经验相矛盾.
我不完全知道这在F#编译器内部是如何工作的(来自F#团队的人可能有更好的答案),但我的猜测是生成nop指令只是生成可以从中引用的位置的一种相当简单的方法.pdb文件.
该pdb文件需要在可以放置断点的代码中为表达式指定一些IL范围 - 这在Debug和Release模式下都是这种情况.这意味着如果在某处放置断点,则需要在IL中有相应的位置.但是,如果没有与源代码位置对应的实际指令,则编译器需要插入一些内容 - 因此它会添加nop.
在发布模式下,F#编译器会进行更多优化,但如果您需要pdb文件,则仍需要为所有源代码位置提供位置.在C#中可能不需要这样做,因为C#更紧密地映射到源IL,但在F#中可能更难避免这种情况.