Dot-Net

將 MethodImplOptions.AggressiveInlining 應用於 F# 函式

  • October 18, 2016

該屬性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
}

沒有“積極的內聯”。我還嘗試將該屬性應用於適當類的靜態和非靜態方法 ( 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”標誌。

這種行為是期望/預期的嗎?它是 F# 編譯器中的錯誤嗎?

(注意:我知道 F#inline關鍵字,但這僅適用於我的庫的 F# 客戶端,而不適用於 C# 消費者。)

@kvb 是正確的,F#編譯器似乎去掉了MethodImpl.

ComputeMethodImplAttribs呼叫IlxGen.fs中的方法來計算方法屬性。

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初始送出。我確實認為剝離我認為會影響 JIT 的MethodImpl特別是錯誤AggressiveInlining的:因此它需要在程序集中。

我建議註冊一個問題。也許你至少可以得到一個解釋。

引用自:https://stackoverflow.com/questions/40104687