将 MethodImplOptions.AggressiveInlining 应用于 F# 函数
Applying MethodImplOptions.AggressiveInlining to F# functions
属性 System.Runtime.CompilerServices.MethodImplAttribute
可用于向 JIT 编译器提供有关如何处理修饰方法的提示。特别是,选项 MethodImplOptions.AggressiveInlining
指示编译器尽可能内联受影响的方法。不幸的是,F# 编译器在生成 IL 时似乎忽略了这个属性。
示例:以下 C# 代码
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Inc(int x) => x + 1;
翻译成
.method public hidebysig static int32 Inc(int32 x) cil managed aggressiveinlining
{
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldc.i4.1
IL_0002: add
IL_0003: ret
}
注意 "aggressiveinlining" 标志。
但是这个 F# 代码
[<MethodImpl(MethodImplOptions.AggressiveInlining)>]
let inc x = x + 1
变成
.method public static int32 inc(int32 x) cil managed
{
.maxstack 8
IL_0000: nop
IL_0001: ldarg.0
IL_0002: ldc.i4.1
IL_0003: add
IL_0004: ret
}
没有"aggressiveinlining"。我也尝试将属性应用于适当的类(type ...
)的静态和非静态方法,但结果是一样的。
如果我将它应用于自定义索引器,就像这样
type Dummy =
member self.Item
with [<MethodImpl(MethodImplOptions.AggressiveInlining)>] get x = x + 1
生成的 IL 是
.method public hidebysig specialname instance int32 get_Item(int32 x) cil managed
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.MethodImplAttribute::.ctor(valuetype [mscorlib]System.Runtime.CompilerServices.MethodImplOptions) = ( 01 00 00 01 00 00 00 00 )
.maxstack 8
IL_0000: nop
IL_0001: ldarg.1
IL_0002: ldc.i4.1
IL_0003: add
IL_0004: ret
}
...虽然我不确定这是否等同于 C# 编译器生成的 "aggressiveinling" 标志。
这种行为是 desired/expected 吗?它是 F# 编译器中的错误吗?
(注意:我知道 F# inline
关键字,但它仅适用于我库的 F# 客户端,不适用于 C# 使用者。)
@kvb 是正确的,F#
编译器似乎去掉了 MethodImpl
.
调用 IlxGen.fs 中的 ComputeMethodImplAttribs
来计算方法属性。
and ComputeMethodImplAttribs cenv (_v:Val) attrs =
let implflags =
match TryFindFSharpAttribute cenv.g cenv.g.attrib_MethodImplAttribute attrs with
| Some (Attrib(_,_,[ AttribInt32Arg flags ],_,_,_,_)) -> flags
| _ -> 0x0
let hasPreserveSigAttr =
match TryFindFSharpAttributeOpt cenv.g cenv.g.attrib_PreserveSigAttribute attrs with
| Some _ -> true
| _ -> false
// strip the MethodImpl pseudo-custom attribute
// The following method implementation flags are used here
// 0x80 - hasPreserveSigImplFlag
// 0x20 - synchronize
// (See ECMA 335, Partition II, section 23.1.11 - Flags for methods [MethodImplAttributes])
let attrs = attrs
|> List.filter (IsMatchingFSharpAttribute cenv.g cenv.g.attrib_MethodImplAttribute >> not)
|> List.filter (IsMatchingFSharpAttributeOpt cenv.g cenv.g.attrib_PreserveSigAttribute >> not)
let hasPreserveSigImplFlag = ((implflags &&& 0x80) <> 0x0) || hasPreserveSigAttr
let hasSynchronizedImplFlag = (implflags &&& 0x20) <> 0x0
let hasNoInliningImplFlag = (implflags &&& 0x08) <> 0x0
hasPreserveSigImplFlag, hasSynchronizedImplFlag, hasNoInliningImplFlag, attrs
仔细查看行:4990:
let attrs = attrs
|> List.filter (IsMatchingFSharpAttribute cenv.g cenv.g.attrib_MethodImplAttribute >> not)
|> List.filter (IsMatchingFSharpAttributeOpt cenv.g cenv.g.attrib_PreserveSigAttribute >> not)
第一个 filter
过滤掉 MethodImplAttribute
。
现在,我看起来有点试图找到基本原理,但这段代码可以追溯到 latkin
初始提交。我确实认为剥离 MethodImpl
可能是错误的,特别是 AggressiveInlining
我认为它会影响 JIT:ing 因此它需要在程序集中。
我建议注册 issue。也许你至少可以得到一个解释。
属性 System.Runtime.CompilerServices.MethodImplAttribute
可用于向 JIT 编译器提供有关如何处理修饰方法的提示。特别是,选项 MethodImplOptions.AggressiveInlining
指示编译器尽可能内联受影响的方法。不幸的是,F# 编译器在生成 IL 时似乎忽略了这个属性。
示例:以下 C# 代码
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Inc(int x) => x + 1;
翻译成
.method public hidebysig static int32 Inc(int32 x) cil managed aggressiveinlining
{
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldc.i4.1
IL_0002: add
IL_0003: ret
}
注意 "aggressiveinlining" 标志。
但是这个 F# 代码
[<MethodImpl(MethodImplOptions.AggressiveInlining)>]
let inc x = x + 1
变成
.method public static int32 inc(int32 x) cil managed
{
.maxstack 8
IL_0000: nop
IL_0001: ldarg.0
IL_0002: ldc.i4.1
IL_0003: add
IL_0004: ret
}
没有"aggressiveinlining"。我也尝试将属性应用于适当的类(type ...
)的静态和非静态方法,但结果是一样的。
如果我将它应用于自定义索引器,就像这样
type Dummy =
member self.Item
with [<MethodImpl(MethodImplOptions.AggressiveInlining)>] get x = x + 1
生成的 IL 是
.method public hidebysig specialname instance int32 get_Item(int32 x) cil managed
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.MethodImplAttribute::.ctor(valuetype [mscorlib]System.Runtime.CompilerServices.MethodImplOptions) = ( 01 00 00 01 00 00 00 00 )
.maxstack 8
IL_0000: nop
IL_0001: ldarg.1
IL_0002: ldc.i4.1
IL_0003: add
IL_0004: ret
}
...虽然我不确定这是否等同于 C# 编译器生成的 "aggressiveinling" 标志。
这种行为是 desired/expected 吗?它是 F# 编译器中的错误吗?
(注意:我知道 F# inline
关键字,但它仅适用于我库的 F# 客户端,不适用于 C# 使用者。)
@kvb 是正确的,F#
编译器似乎去掉了 MethodImpl
.
ComputeMethodImplAttribs
来计算方法属性。
and ComputeMethodImplAttribs cenv (_v:Val) attrs =
let implflags =
match TryFindFSharpAttribute cenv.g cenv.g.attrib_MethodImplAttribute attrs with
| Some (Attrib(_,_,[ AttribInt32Arg flags ],_,_,_,_)) -> flags
| _ -> 0x0
let hasPreserveSigAttr =
match TryFindFSharpAttributeOpt cenv.g cenv.g.attrib_PreserveSigAttribute attrs with
| Some _ -> true
| _ -> false
// strip the MethodImpl pseudo-custom attribute
// The following method implementation flags are used here
// 0x80 - hasPreserveSigImplFlag
// 0x20 - synchronize
// (See ECMA 335, Partition II, section 23.1.11 - Flags for methods [MethodImplAttributes])
let attrs = attrs
|> List.filter (IsMatchingFSharpAttribute cenv.g cenv.g.attrib_MethodImplAttribute >> not)
|> List.filter (IsMatchingFSharpAttributeOpt cenv.g cenv.g.attrib_PreserveSigAttribute >> not)
let hasPreserveSigImplFlag = ((implflags &&& 0x80) <> 0x0) || hasPreserveSigAttr
let hasSynchronizedImplFlag = (implflags &&& 0x20) <> 0x0
let hasNoInliningImplFlag = (implflags &&& 0x08) <> 0x0
hasPreserveSigImplFlag, hasSynchronizedImplFlag, hasNoInliningImplFlag, attrs
仔细查看行:4990:
let attrs = attrs
|> List.filter (IsMatchingFSharpAttribute cenv.g cenv.g.attrib_MethodImplAttribute >> not)
|> List.filter (IsMatchingFSharpAttributeOpt cenv.g cenv.g.attrib_PreserveSigAttribute >> not)
第一个 filter
过滤掉 MethodImplAttribute
。
现在,我看起来有点试图找到基本原理,但这段代码可以追溯到 latkin
初始提交。我确实认为剥离 MethodImpl
可能是错误的,特别是 AggressiveInlining
我认为它会影响 JIT:ing 因此它需要在程序集中。
我建议注册 issue。也许你至少可以得到一个解释。